更改 R 中数据框列表中的列名子集

Posted

技术标签:

【中文标题】更改 R 中数据框列表中的列名子集【英文标题】:Changing a subset of column names in a list of data frames in R 【发布时间】:2018-11-18 18:34:41 【问题描述】:

这个问题是Changing Column Names in a List of Data Frames in R的扩展。

该帖子解决了 data.frame 的 所有 列的更改名称。

但是如何只更改选定数量的列的名称?

例子:

我只想更改列表中每个 data.frame 中第一列的名称:

dat <- data.frame(Foo = 1:5,Bar = 1:5)
lst <- list(dat,dat)

print(lst)

[[1]]
  Foo Bar
1   1   1
2   2   2
3   3   3
4   4   4
5   5   5

[[2]]
  Foo Bar
1   1   1
2   2   2
3   3   3
4   4   4
5   5   5

(失败)尝试:

lapply(1:2, function(x) names(lst[[x]])[names(lst[[x]]) == 'Foo'] <- 'New')
lapply(1:2, function(x) names(lst[[x]])[names(lst[[x]]) == 'Foo'])  <- rep('New',2)
lapply(1:2, function(x) setNames(lst[[x]][names(lst[[x]]) == 'Foo'],'New'))

【问题讨论】:

注意:需要更改的列号可能因data.frames而异。在上面的例子中,Foo 可能在一个 data.frame 的第 1 列,但在第二个 data.frame 的第 2 列。等等 【参考方案1】:

这是使用setNamesgsub 的一种可能性:

# Sample data
dat <- data.frame(Foo = 1:5,Bar = 1:5)
lst <- list(dat,dat[, 2:1])

# Replace Foo with FooFoo
lst <- lapply(lst, function(x) setNames(x, gsub("^Foo$", "FooFoo", names(x))) )
#[[1]]
#  FooFoo Bar
#1      1   1
#2      2   2
#3      3   3
#4      4   4
#5      5   5
#
#[[2]]
#  Bar FooFoo
#1   1      1
#2   2      2
#3   3      3
#4   4      4
#5   5      5

【讨论】:

优秀的答案,与@Gregor 的solution 一样有用。谢谢+1 为了迂腐和保守,您可能希望将正则表达式模式更改为"^Foo$" 或使用replace 而不是gsub。当我只打算匹配整个字符串时,我肯定曾经被字符串的模式匹配部分所困扰。 @Gregor,你这么说很有趣,b/c 我刚刚意识到这是我的实际 data.frames 列表中的一个问题,激发了我发布这个问题! :p 谢谢! (感谢更新,毛里茨)【参考方案2】:

你的尝试有两个问题:

    使用lapply(1:2, ...) 代替lapply(lst, ...) 很奇怪。这会让你的匿名函数更尴尬。

    您的匿名函数没有return 数据框。返回函数的最后一行(在没有return() 语句的情况下)。在您的第一次尝试中,最后一行的值只是分配的值,"new" - 我们需要返回整个数据框并修改名称。

解决方案:

lapply(lst, function(x) names(x)[names(x) == 'Foo'] <- 'New'; x)
# [[1]]
#   New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5
# 
# [[2]]
#   New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5

【讨论】:

这很好用(感谢您的建议)。但是,如果我不想打印列表怎么办?我只想修改已保存列表的名称。使用您的方法,我是否只需通过lst &lt;- lapply(lst, function(x) names(x)[names(x) == 'Foo'] &lt;- 'New'; x) 将我以前的列表对象替换为这个对象? 没错。就像任何其他 R 函数一样,您可以选择覆盖原始对象、将其另存为新对象,或者直接打印结果而不进行任何更改。 data.table包例外——如果你使用data.table你可以使用data.table::setnames通过引用修改原始数据表的名称。【参考方案3】:

这是一种按列索引更改列名的方法。

lapply(lst, function(x, pos = 1, newname = "New")
  # x: data frame, pos: column index, newname: new name of the column
  column <- names(x)
  column[pos] <- newname
  names(x) <- column
  return(x)
)
# [[1]]
#   New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5
# 
# [[2]]
#   New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5

我在看到来自 OP 的更新评论之前发布了这个答案,说每个数据帧中目标列的索引可能不同。这在原帖中没有提到。请查看其他人的帖子,因为我的答案仅在列索引一致的情况下才有效。

【讨论】:

对不起,迟到的评论!但是谢谢你的回答! +1【参考方案4】:

我的解决方案比其他解决方案更复杂,但它就是这样。

主要区别在于它使用grep(带有参数ignore.case = TRUE)而不是==

lapply(lst, function(DF) 
  inx <- grep("^foo$", names(DF), ignore.case = TRUE)
  names(DF)[inx] <- "New"
  DF
)
#[[1]]
#  New Bar
#1   1   1
#2   2   2
#3   3   3
#4   4   4
#5   5   5
#
#[[2]]
#  New Bar
#1   1   1
#2   2   2
#3   3   3
#4   4   4
#5   5   5

【讨论】:

查看我对 Maurits 答案的评论 - 我建议您对正则表达式模式更加小心,或者只使用 ==,或者明确表示这将重命名任何列 包含字符串“foo”。【参考方案5】:

使用tidyverse

library(tidyverse)
map(lst,rename_at,"Foo",~"New")
# [[1]]
# New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5
# 
# [[2]]
# New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5

使用data.table

library(data.table)
lst2 <- copy(lst)
lapply(lst2,setnames,"Foo","New")

# [[1]]
# New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5
# 
# [[2]]
# New Bar
# 1   1   1
# 2   2   2
# 3   3   3
# 4   4   4
# 5   5   5

这里的更改是通过引用进行的,所以我们先复制一份。

【讨论】:

【参考方案6】:

注意没有赋值,它不会改变原始对象。

lst &lt;- purrr::map(lst, ~setNames(.x, c('new', names(.x)[-1])))

【讨论】:

以上是关于更改 R 中数据框列表中的列名子集的主要内容,如果未能解决你的问题,请参考以下文章

R语言学习:提取R对象的子集

更改 R 中的某些列名 [重复]

根据 R 中的特定列名从字符向量中删除逗号

循环遍历 R 中列名的特定子集

R语言数据结构-数据框&矩阵&列表

从R中的数据框中子集列[重复]