我有一个未命名列表的列表,我需要将其转换为可用的data.frame。在大多数情况下,列表中的每个列表都具有相同的元素名称,但有些列表将具有其他列表不会的一些元素。所以每个列表都应该是我的data.frame中的一个行,每个变量名称都应该是一列,如果列表没有特定的变量,data.frame应该包含一个NA元素。
在我的例子中,this_list是我正在使用的,this_df是我想要的。我已经尝试了各种方法来取消列出并转换为 data.frame,但我的列名称只是重复,我只得到 1 个观察结果。 谢谢。
this_list <- list(list(
Name = "One",
A = 2,
B = 3,
C = 4,
D = 5
),
list(
Name = "Two",
A = 5,
B = 2,
C = 1
))
this_df <- data.frame(Name=c("One","Two"),
A=c(2,5),
B=c(3,2),
C=c(4,1),
D=c(5,NA))
您可以使用data.table
中的rbindlist
:
library(data.table)
that_df <- as.data.frame(rbindlist(this_list, fill = TRUE))
# the result
Name A B C D
1: One 2 3 4 5
2: Two 5 2 1 NA
人们经常要dplyr::bind_rows
或data.table::rbindlist
的任务。但是,在基本 R 中,如果列表元素一致,则do.call(rbind, ...)
快速基本 R 解决方案:
do.call(rbind, list(this_list[[1]][1:4], this_list[[2]]))
#> Name A B C
#> [1,] "One" 2 3 4
#> [2,] "Two" 5 2 1
它返回一个矩阵,但可以相当容易地清理。
但是,如果列表元素不一致,它会以烦人的方式回收(谢天谢地,带有警告(:
do.call(rbind, this_list)
#> Warning in (function (..., deparse.level = 1) : number of columns of result
#> is not a multiple of vector length (arg 2)
#> Name A B C D
#> [1,] "One" 2 3 4 5
#> [2,] "Two" 5 2 1 "Two"
因此需要更强大的解决方案,例如
rbind_list <- function(list, ...){
# generate a vector of all variable names
vars <- Reduce(function(x, y){union(x, names(y))}, list, init = c());
filled_list <- lapply(list, function(x){
x <- x[vars] # add missing elements, reordering if necessary
names(x) <- vars # fix missing names
x <- lapply(x, function(y){
if (is.null(y)) { # replace NULL with NA
NA
} else if (is.list(y)) {
if (length(y) != 1) y <- list(y) # handle non-length-1 list columns
I(y) # add as-is class to list columns so they don't fail
} else {
y
}
})
as.data.frame(x, ...) # coerce to data frame
})
do.call(rbind, filled_list) # rbind resulting list of data frames
}
它确实比do.call(rbind, ...)
更好:
rbind_list(this_list, stringsAsFactors = FALSE)
#> Name A B C D
#> 1 One 2 3 4 5
#> 2 Two 5 2 1 NA
rbind_list(c(this_list, this_list))
#> Name A B C D
#> 1 One 2 3 4 5
#> 2 Two 5 2 1 NA
#> 3 One 2 3 4 5
#> 4 Two 5 2 1 NA
rbind_list(list(list(a = 1), list(b = 2)))
#> a b
#> 1 1 NA
#> 2 NA 2
rbind_list(list(list(a = 1), list(a = 1, b = 2)))
#> a b
#> 1 1 NA
#> 2 1 2
rbind_list(list(list(a = 1, b = 2), list(b = 2, a = 1)))
#> a b
#> 1 1 2
#> 2 1 2
。尽管列表列处理仍然不一致:
# correct; is a list column
rbind_list(list(list(a = 1, c = list('foo')), list(a = 1, c = list('baz'))))
#> a c
#> 1 1 foo
#> 2 1 baz
# also correct
rbind_list(list(list(a = 1, c = list(c('foo', 'bar'))), list(a = 1, c = list('baz'))))
#> a c
#> 1 1 foo, bar
#> 2 1 baz
# can handle non-encapsulated nested lists
rbind_list(list(list(a = 1, c = list('foo', 'bar')), list(a = 1, c = list('baz'))))
#> a c
#> 1 1 foo, bar
#> 2 1 baz
# ...which confuses dplyr
dplyr::bind_rows(list(list(a = 1, c = list('foo', 'bar')), list(a = 1, c = list('baz'))))
#> Error in bind_rows_(x, .id): Argument 2 must be length 1, not 2
# ...but fills missing list elements with NA because it doesn't track classes across observations
rbind_list(list(list(a = 1), list(c = list('baz'))))
#> a c
#> 1 1 NA
#> 2 NA baz
# ...which dplyr handles better
dplyr::bind_rows(list(list(a = 1), list(c = list('baz'))))
#> # A tibble: 2 x 2
#> a c
#> <dbl> <list>
#> 1 1.00 <NULL>
#> 2 NA <chr [1]>
虽然肯定比do.call(rbind, ...)
更健壮,但在规模上,这种方法可能比用C或C++编写的包实现慢得多。
仅使用基本 R 的解决方案。按顺序对每个列表元素执行完全联接。(根据@RichScriven的评论编辑(
this_df <- Reduce(function(x, y) merge(x, y, all = TRUE), this_list)
只是使用 dplyr
包的另一种选择:
bind_rows(this_list)
# A tibble: 2 x 5
Name A B C D
<chr> <dbl> <dbl> <dbl> <dbl>
1 One 2 3 4 5
2 Two 5 2 1 NA
编辑:
当我们在它的时候。这是rlist
的另一个快速替代方案:
list.stack(this_list, fill = TRUE)
Name A B C D
1 One 2 3 4 5
2 Two 5 2 1 NA