在R中更改数据框列表中一部分列名

4
这个问题是对在R中更改数据框列表中所有列名的问题的扩展。
该帖子解决了更改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'))

注意:需要更改的列号可能因数据框而异。在上面的示例中,Foo 可能在一个数据框的第一列,但在第二个数据框的第二列等等。 - theforestecologist
6个回答

7
这里有一种可能性,使用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

1
非常好的答案,同样有用的是@Gregor的解决方案。谢谢+1 - theforestecologist
3
为了更加严谨保守,你可能希望将正则表达式模式更改为"^Foo$"或使用replace而非gsub。我以前曾因为正则表达式匹配部分字符串而不是整个字符串而遭受过损失。 - Gregor Thomas
1
@Gregor,你这么说真是有趣,因为我刚刚意识到这是我实际的数据框列表中的一个问题,激励我发布了这个问题!:p 谢谢!(还要感谢Maurits的更新) - theforestecologist

5
你的尝试存在两个问题:
  1. 使用 lapply(1:2, ...) 而不是 lapply(lst, ...) 是奇怪的。这使得匿名函数更加笨拙。

  2. 你的匿名函数没有 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 <- lapply(lst, function(x) {names(x)[names(x) == 'Foo'] <- 'New'; x}) 用这个新对象替换我的先前列表对象? - theforestecologist
1
没错。就像其他任何R函数一样,您可以选择覆盖原始对象、将其保存为新对象或仅打印结果而不更改任何内容。 - Gregor Thomas
1
唯一的例外是 data.table 包 - 如果你正在使用 data.table,你可以使用 data.table::setnames 来通过引用修改原始数据表的名称。 - Gregor Thomas

1
这是一种通过列索引更改列名称的方法。
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

在看到问题提出者更新评论说每个数据框中目标列的索引可能不同之前,我已经发布了这个答案。原帖中没有提到这一点。请查看其他人的帖子,因为我的答案仅在列索引一致时有效。

1
抱歉晚了才留言!但是感谢你的回答!+1 - theforestecologist

1
我的解决方案比其他人的更复杂,但是这里是我的方法。
主要区别在于,它使用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" 的列。 - Gregor Thomas

1
使用 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

这里通过引用进行更改,因此我们首先进行复制。


0

注意,如果没有分配,它不会更改原始对象。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接