R 使用 lapply() 填充和命名数据框列表中的一列

R use of lapply() to populate and name one column in list of dataframes

找了一段时间,没找到流畅的R类解法。

我有一个向量列表,我想将其转换为数据帧并添加一个包含向量名称的列。我无法将 cbind() 和 melt() 用于单个数据帧 b/c 存在具有不同行数的向量。

基本示例为:

list<-list(a=c(1,2,3),b=c(4,5,6,7))
var<-"group"

我想出并起作用的是:

list<-lapply(list, function(x) data.frame(num=x,grp=""))

for (j in 1:length(list)){
  list[[j]][,2]<-names(list[j])
  names(list[[j]])[2]<-var
}

但我正在尝试更好地使用 lapply() 并拥有更清晰的编码实践。现在我非常依赖 for 和 if 语句,很多基本函数已经这样做了,而且比我此时编写代码的效率要高得多。

我想要的伪代码是这样的:

list<-lapply(list, function(x) data.frame(num=x,get(var)=names(x))

有没有一种干净的方法来完成这项工作?

第二个密切相关的问题,如果我已经有了数据框列表,为什么使用 lapply() 重新分配列值和名称如此困难?

所以使用类似的东西:

list<-list(a=data.frame(num=c(1,2,3),grp=""),b=data.frame(num=c(4,5,6,7),grp=""))
var<-"group"

#pseudo code
list<-lapply(list, function(x) x[,2]<-names(x)) #populate second col with name of df[x]
list<-lapply(list, function(x) names[[x]][2]<-var) #set 2nd col name to 'var'

第一行伪代码抛出关于匹配行长度的错误。为什么 lapply() 不像单个数据帧上的相同函数在 for 循环中那样循环并重复 names(x)?

对于第二行,据我所知,我可以使用 setNames() 来重新分配所有列名,但是我如何只对其中一个列名进行这项工作?

非常感谢任何想法或指向涵盖此内容的其他线程并帮助我理解 lapply() 在这种情况下的行为。

关于您的 first/main 问题,您可以使用包 tibble 中的函数 enframe 来达到此目的

library(tibble)
library(tidyr)
library(dplyr)

l<-list(a=c(1,2,3),b=c(4,5,6,7))

l %>% 
  enframe(name = "group", value="value") %>% 
  unnest(value) %>% 
  group_split(group)

试试这个:

library(dplyr)
mylist <- list(a = c(1,2,3), b = c(4,5,6,7))
bind_rows(lapply(names(mylist), function(x) tibble(grp = x, num = mylist[[x]])))
# A tibble: 7 x 2
  grp     num
  <chr> <dbl>
1 a         1
2 a         2
3 a         3
4 b         4
5 b         5
6 b         6
7 b         7

这本质上是一个基于 lapply 的解决方案,您可以在其中迭代列表的名称,而不是单个列表元素本身。如果你更喜欢在 base R 中做所有事情,请注意以上等同于

do.call(rbind, lapply(names(mylist), function(x) data.frame(grp = x, num = mylist[[x]], stringsAsFactors = F)))

话虽如此,tibbles 作为 data.frames 的现代实现是首选,bind_rows 优于 do.call(rbind... 结构。

关于第二个问题,注意以下几点:

lapply(mylist, function(x) str(x))
 num [1:3] 1 2 3
 num [1:4] 4 5 6 7
....
lapply(mylist, function(x) names(x))
$a
NULL
$b
NULL

这里看到的是lapply里面的函数获取了mylist的元素。在这种情况下,它开始使用数字向量。就 lapply 内部调用的函数而言,它没有任何名称。为了强调这一点,请考虑以下内容:

names(c(1,2,3))
NULL

相同的是:向量c(1,2,3)没有名称属性。

不使用循环的完整 R 基础方法

> l<-list(a=c(1,2,3),b=c(4,5,6,7))
> data.frame(grp=rep(names(l), lengths(l)), num=unlist(l), row.names = NULL)
  grp num
1   a   1
2   a   2
3   a   3
4   b   4
5   b   5
6   b   6