更改 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】:
这是使用setNames
和gsub
的一种可能性:
# 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 <- lapply(lst, function(x) names(x)[names(x) == 'Foo'] <- '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 <- purrr::map(lst, ~setNames(.x, c('new', names(.x)[-1])))
【讨论】:
以上是关于更改 R 中数据框列表中的列名子集的主要内容,如果未能解决你的问题,请参考以下文章