我有一个未命名的列表列表,我需要将其转换为可用的data.frame。在大多数情况下,列表中的每个列表都具有相同的元素名称,但有些列表中有一些元素不会。因此,每个列表应该是我的data.frame中的一行,每个变量名称应该是一列,如果列表没有特定的变量,data.frame应该包含一个NA元素。
在我的例子中,this_list是我正在使用的,而this_df就是我想拥有的。我已经尝试了各种方法来取消列表并转换为data.frame,但我的列名只是重复,我只得到1次观察。谢谢。
b20.Text = "Text1";
b21.Text = "Text2";
答案 0 :(得分:4)
您可以使用rbindlist
中的data.table
:
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
答案 1 :(得分:4)
这是人们经常联系dplyr::bind_rows
或data.table::rbindlist
的任务。但是,在基数R中,如果列表元素一致,则快速基础R解决方案为do.call(rbind, ...)
:
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 ++编写的包实现慢得多。
答案 2 :(得分:3)
仅使用基础R的解决方案。按顺序对每个列表元素执行完全连接。 (根据@RichScriven的评论编辑)
this_df <- Reduce(function(x, y) merge(x, y, all = TRUE), this_list)
答案 3 :(得分:3)
使用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