我有一个嵌套的数据列表。它的长度是 132,每个项目是一个长度为 20 的列表。有没有一种快速的方法可以将此结构转换为具有 132 行和 20 列数据的数据框?
以下是一些可以使用的示例数据:
l <- replicate(
132,
as.list(sample(letters, 20)),
simplify = FALSE
)
与rbind
do.call(rbind.data.frame, your_list)
编辑:以前的版本返回 list
的 data.frame
而不是向量(正如@IanSudbery 在评论中指出的那样)。
2020 年 7 月更新:
参数 stringsAsFactors
的默认值现在是 default.stringsAsFactors()
,这反过来产生 FALSE
作为其默认值。
假设您的列表列表称为 l
:
df <- data.frame(matrix(unlist(l), nrow=length(l), byrow=TRUE))
以上将所有字符列转换为因子,为避免这种情况,您可以在 data.frame() 调用中添加一个参数:
df <- data.frame(matrix(unlist(l), nrow=132, byrow=TRUE),stringsAsFactors=FALSE)
names(df) <- names(unlist(l[1]))
。
您可以使用 plyr
包。例如表单的嵌套列表
l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
, b = list(var.1 = 4, var.2 = 5, var.3 = 6)
, c = list(var.1 = 7, var.2 = 8, var.3 = 9)
, d = list(var.1 = 10, var.2 = 11, var.3 = 12)
)
现在长度为 4,l
中的每个列表都包含另一个长度为 3 的列表。现在您可以运行
library (plyr)
df <- ldply (l, data.frame)
并且应该得到与@Marek 和@nico 的答案相同的结果。
matrix
方法的字符。
修复样本数据,使其与原始描述“每个项目都是长度为 20 的列表”相匹配
mylistlist <- replicate(
132,
as.list(sample(letters, 20)),
simplify = FALSE
)
我们可以将其转换为这样的数据框:
data.frame(t(sapply(mylistlist,c)))
sapply
将其转换为矩阵。 data.frame
将矩阵转换为数据框。
导致:
https://i.stack.imgur.com/Lv0kn.png
c
在这里扮演什么角色,列表数据的一个实例?哦,等等,c 代表连接函数,对吧?对@mnel 对 c 的使用感到困惑。我也同意@dhandler,在我的用例中,正确的列名是一个有价值的需求。出色的解决方案。
?c
:Combine Values into a Vector or List
假设您的列表名为 L
,
data.frame(Reduce(rbind, L))
data.frame(Reduce(rbind, list(c('col1','col2'))))
生成一个具有 2 行 1 列 的数据框(我期望 1 行 2 列)
reduce(L, rbind)
。这会输出一个数据框,并假设列表 (L) 中的每个数据框都以相同的方式组织(即包含相同数量的相同顺序的列。
包 data.table
具有函数 rbindlist
,它是 do.call(rbind, list(...))
的超快实现。
它可以将 lists
、data.frames
或 data.tables
的列表作为输入。
library(data.table)
ll <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
, b = list(var.1 = 4, var.2 = 5, var.3 = 6)
, c = list(var.1 = 7, var.2 = 8, var.3 = 9)
, d = list(var.1 = 10, var.2 = 11, var.3 = 12)
)
DT <- rbindlist(ll)
这将返回从 data.frame
继承的 data.table
。
如果您真的想要转换回 data.frame,请使用 as.data.frame(DT)
setDF
现在允许通过引用返回 data.frame。
tibble
包有一个函数 enframe()
,它通过将嵌套的 list
对象强制为嵌套的 tibble
(“整洁”数据框)对象来解决这个问题。以下是来自 R for Data Science 的简短示例:
x <- list(
a = 1:5,
b = 3:4,
c = 5:6
)
df <- enframe(x)
df
#> # A tibble: 3 × 2
#> name value
#> <chr> <list>
#> 1 a <int [5]>
#> 2 b <int [2]>
#> 3 c <int [2]>
由于您的列表中有多个嵌套 l
,您可以使用 unlist(recursive = FALSE)
删除不必要的嵌套,只获得一个分层列表,然后传递给 enframe()
。我使用 tidyr::unnest()
将输出取消嵌套到单个级别的“整洁”数据框中,该数据框有两列(一列用于组 name
,一列用于组 value
的观察)。如果您想要使列变宽,您可以使用 add_column()
添加一个列,该列仅将值的顺序重复 132 次。然后只需 spread()
值。
library(tidyverse)
l <- replicate(
132,
list(sample(letters, 20)),
simplify = FALSE
)
l_tib <- l %>%
unlist(recursive = FALSE) %>%
enframe() %>%
unnest()
l_tib
#> # A tibble: 2,640 x 2
#> name value
#> <int> <chr>
#> 1 1 d
#> 2 1 z
#> 3 1 l
#> 4 1 b
#> 5 1 i
#> 6 1 j
#> 7 1 g
#> 8 1 w
#> 9 1 r
#> 10 1 p
#> # ... with 2,630 more rows
l_tib_spread <- l_tib %>%
add_column(index = rep(1:20, 132)) %>%
spread(key = index, value = value)
l_tib_spread
#> # A tibble: 132 x 21
#> name `1` `2` `3` `4` `5` `6` `7` `8` `9` `10` `11`
#> * <int> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 1 d z l b i j g w r p y
#> 2 2 w s h r i k d u a f j
#> 3 3 r v q s m u j p f a i
#> 4 4 o y x n p i f m h l t
#> 5 5 p w v d k a l r j q n
#> 6 6 i k w o c n m b v e q
#> 7 7 c d m i u o e z v g p
#> 8 8 f s e o p n k x c z h
#> 9 9 d g o h x i c y t f j
#> 10 10 y r f k d o b u i x s
#> # ... with 122 more rows, and 9 more variables: `12` <chr>, `13` <chr>,
#> # `14` <chr>, `15` <chr>, `16` <chr>, `17` <chr>, `18` <chr>,
#> # `19` <chr>, `20` <chr>
根据列表的结构,有一些 tidyverse
选项可以很好地处理不等长度的列表:
l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
, b = list(var.1 = 4, var.2 = 5)
, c = list(var.1 = 7, var.3 = 9)
, d = list(var.1 = 10, var.2 = 11, var.3 = NA))
df <- dplyr::bind_rows(l)
df <- purrr::map_df(l, dplyr::bind_rows)
df <- purrr::map_df(l, ~.x)
# all create the same data frame:
# A tibble: 4 x 3
var.1 var.2 var.3
<dbl> <dbl> <dbl>
1 1 2 3
2 4 5 NA
3 7 NA 9
4 10 11 NA
您还可以混合使用向量和数据框:
library(dplyr)
bind_rows(
list(a = 1, b = 2),
data_frame(a = 3:4, b = 5:6),
c(a = 7)
)
# A tibble: 4 x 2
a b
<dbl> <dbl>
1 1 2
2 3 5
3 4 6
4 7 NA
X2
can't be convert from integer to character
此方法使用 tidyverse
包 (purrr)。
名单:
x <- as.list(mtcars)
将其转换为数据框(更具体地说是 tibble
):
library(purrr)
map_df(x, ~.x)
编辑:2021 年 5 月 30 日
这实际上可以通过 dplyr
中的 bind_rows()
函数来实现。
x <- as.list(mtcars)
dplyr::bind_rows(x)
A tibble: 32 x 11
mpg cyl disp hp drat wt qsec vs am gear carb
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 21 6 160 110 3.9 2.62 16.5 0 1 4 4
2 21 6 160 110 3.9 2.88 17.0 0 1 4 4
3 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
4 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
5 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
6 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
7 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
8 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
9 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2
10 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4
# ... with 22 more rows
Reshape2 产生与上述 plyr 示例相同的输出:
library(reshape2)
l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3)
, b = list(var.1 = 4, var.2 = 5, var.3 = 6)
, c = list(var.1 = 7, var.2 = 8, var.3 = 9)
, d = list(var.1 = 10, var.2 = 11, var.3 = 12)
)
l <- melt(l)
dcast(l, L1 ~ L2)
产量:
L1 var.1 var.2 var.3
1 a 1 2 3
2 b 4 5 6
3 c 7 8 9
4 d 10 11 12
如果您几乎没有像素,您可以使用 recast() 在 1 行中完成所有操作。
扩展@Marek 的回答:如果您想避免字符串变成因素并且效率不是问题,请尝试
do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))
对于具有 3 个或更多级别的深度嵌套列表的一般情况,例如从嵌套 JSON 获得的列表:
{
"2015": {
"spain": {"population": 43, "GNP": 9},
"sweden": {"population": 7, "GNP": 6}},
"2016": {
"spain": {"population": 45, "GNP": 10},
"sweden": {"population": 9, "GNP": 8}}
}
首先考虑 melt()
将嵌套列表转换为 tall 格式的方法:
myjson <- jsonlite:fromJSON(file("test.json"))
tall <- reshape2::melt(myjson)[, c("L1", "L2", "L3", "value")]
L1 L2 L3 value
1 2015 spain population 43
2 2015 spain GNP 9
3 2015 sweden population 7
4 2015 sweden GNP 6
5 2016 spain population 45
6 2016 spain GNP 10
7 2016 sweden population 9
8 2016 sweden GNP 8
然后是 dcast()
,然后再次扩大到一个整洁的数据集中,其中每个变量形成一列,每个观察形成一行:
wide <- reshape2::dcast(tall, L1+L2~L3)
# left side of the formula defines the rows/observations and the
# right side defines the variables/measurements
L1 L2 GNP population
1 2015 spain 9 43
2 2015 sweden 6 7
3 2016 spain 10 45
4 2016 sweden 8 9
更多答案以及此问题的答案时间:What is the most efficient way to cast a list as a data frame?
最快的方法似乎是(来自 Martin Morgan 的回答):
l <- list(list(col1="a",col2=1),list(col1="b",col2=2))
f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE)
as.data.frame(Map(f(l), names(l[[1]])))
有时您的数据可能是相同长度的向量列表的列表。
lolov = list(list(c(1,2,3),c(4,5,6)), list(c(7,8,9),c(10,11,12),c(13,14,15)) )
(内部向量也可以是列表,但我正在简化以使其更易于阅读)。
然后您可以进行以下修改。请记住,您可以一次取消列出一个级别:
lov = unlist(lolov, recursive = FALSE )
> lov
[[1]]
[1] 1 2 3
[[2]]
[1] 4 5 6
[[3]]
[1] 7 8 9
[[4]]
[1] 10 11 12
[[5]]
[1] 13 14 15
现在使用其他答案中提到的您最喜欢的方法:
library(plyr)
>ldply(lov)
V1 V2 V3
1 1 2 3
2 4 5 6
3 7 8 9
4 10 11 12
5 13 14 15
以下简单命令对我有用:
myDf <- as.data.frame(myList)
参考 (Quora answer)
> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6))
> myList
$a
[1] 1 2 3
$b
[1] 4 5 6
> myDf <- as.data.frame(myList)
a b
1 1 4
2 2 5
3 3 6
> class(myDf)
[1] "data.frame"
但是,如果不明显如何将列表转换为数据框,这将失败:
> myList <- list(a = c(1, 2, 3), b = c(4, 5, 6, 7))
> myDf <- as.data.frame(myList)
错误(函数(...,row.names = NULL,check.rows = FALSE,check.names = TRUE,:参数暗示不同的行数:3、4
注意:答案朝向问题的标题,可能会跳过问题的一些细节
如果您的列表包含具有相同尺寸的元素,您可以使用 tidyverse 中的 bind_rows
函数。
# Load the tidyverse
Library(tidyverse)
# make a list with elements having same dimensions
My_list <- list(a = c(1, 4, 5), b = c(9, 3, 8))
## Bind the rows
My_list %>% bind_rows()
结果是一个有两行的数据框。
这最终对我有用:
do.call("rbind", lapply(S1, as.data.frame))
对于使用 purrr
系列解决方案的并行(多核、多会话等)解决方案,请使用:
library (furrr)
plan(multisession) # see below to see which other plan() is the more efficient
myTibble <- future_map_dfc(l, ~.x)
其中 l
是列表。
要对最有效的 plan()
进行基准测试,您可以使用:
library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()
一个简短的(但可能不是最快的)方法是使用基数 r,因为数据框只是一个 list of equal length vectors。因此,您的输入列表和 30 x 132 data.frame 之间的转换将是:
df <- data.frame(l)
从那里我们可以将其转置为 132 x 30 矩阵,并将其转换回数据帧:
new_df <- data.frame(t(df))
作为一个单行:
new_df <- data.frame(t(data.frame(l)))
行名看起来很烦人,但你总是可以用
rownames(new_df) <- 1:nrow(new_df)
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)
我发现的每个解决方案似乎只适用于 list
中的每个对象都具有相同的 length
。当 list
中的 length
对象的 length
不相等时,我需要将 list
转换为 data.frame
。下面是我想出的基本 R
解决方案。毫无疑问,这是非常低效的,但它似乎确实有效。
x1 <- c(2, 13)
x2 <- c(2, 4, 6, 9, 11, 13)
x3 <- c(1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 13)
my.results <- list(x1, x2, x3)
# identify length of each list
my.lengths <- unlist(lapply(my.results, function (x) { length(unlist(x))}))
my.lengths
#[1] 2 6 20
# create a vector of values in all lists
my.values <- as.numeric(unlist(c(do.call(rbind, lapply(my.results, as.data.frame)))))
my.values
#[1] 2 13 2 4 6 9 11 13 1 1 2 3 3 4 5 5 6 7 7 8 9 9 10 11 11 12 13 13
my.matrix <- matrix(NA, nrow = max(my.lengths), ncol = length(my.lengths))
my.cumsum <- cumsum(my.lengths)
mm <- 1
for(i in 1:length(my.lengths)) {
my.matrix[1:my.lengths[i],i] <- my.values[mm:my.cumsum[i]]
mm <- my.cumsum[i]+1
}
my.df <- as.data.frame(my.matrix)
my.df
# V1 V2 V3
#1 2 2 1
#2 13 4 1
#3 NA 6 2
#4 NA 9 3
#5 NA 11 3
#6 NA 13 4
#7 NA NA 5
#8 NA NA 5
#9 NA NA 6
#10 NA NA 7
#11 NA NA 7
#12 NA NA 8
#13 NA NA 9
#14 NA NA 9
#15 NA NA 10
#16 NA NA 11
#17 NA NA 11
#18 NA NA 12
#19 NA NA 13
#20 NA NA 13
将 map_
函数与 for
循环一起使用怎么样?这是我的解决方案:
list_to_df <- function(list_to_convert) {
tmp_data_frame <- data.frame()
for (i in 1:length(list_to_convert)) {
tmp <- map_dfr(list_to_convert[[i]], data.frame)
tmp_data_frame <- rbind(tmp_data_frame, tmp)
}
return(tmp_data_frame)
}
其中 map_dfr
将每个列表元素转换为 data.frame,然后 rbind
将它们合并在一起。
在你的情况下,我想它会是:
converted_list <- list_to_df(l)
map(list_to_convert, ~map_dfr(., data.frame))
,但仍然是错误的。
试试 collapse::unlist2d
('unlist to data.frame' 的简写):
l <- replicate(
132,
list(sample(letters, 20)),
simplify = FALSE
)
library(collapse)
head(unlist2d(l))
.id.1 .id.2 V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20
1 1 1 e x b d s p a c k z q m u l h n r t o y
2 2 1 r t i k m b h n s e p f o c x l g v a j
3 3 1 t r v z a u c o w f m b d g p q y e n k
4 4 1 x i e p f d q k h b j s z a t v y l m n
5 5 1 d z k y a p b h c v f m u l n q e i w j
6 6 1 l f s u o v p z q e r c h n a t m k y x
head(unlist2d(l, idcols = FALSE))
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20
1 e x b d s p a c k z q m u l h n r t o y
2 r t i k m b h n s e p f o c x l g v a j
3 t r v z a u c o w f m b d g p q y e n k
4 x i e p f d q k h b j s z a t v y l m n
5 d z k y a p b h c v f m u l n q e i w j
6 l f s u o v p z q e r c h n a t m k y x
或者您可以使用 tibble 包(来自 tidyverse):
#create examplelist
l <- replicate(
132,
as.list(sample(letters, 20)),
simplify = FALSE
)
#package tidyverse
library(tidyverse)
#make a dataframe (or use as_tibble)
df <- as_data_frame(l,.name_repair = "unique")
我也想提出这个解决方案。尽管它看起来与其他解决方案相似,但它使用了 plyr 包中的 rbind.fill。这在列表缺少列或 NA 值的情况下是有利的。
l <- replicate(10,as.list(sample(letters,10)),simplify = FALSE)
res<-data.frame()
for (i in 1:length(l))
res<-plyr::rbind.fill(res,data.frame(t(unlist(l[i]))))
res
从不同的角度;
install.packages("smotefamily")
library(smotefamily)
library(dplyr)
data_example = sample_generator(5000,ratio = 0.80)
genData = BLSMOTE(data_example[,-3],data_example[,3])
#There are many lists in genData. If we want to convert one of them to dataframe.
sentetic=as.data.frame.array(genData$syn_data)
# as.data.frame.array seems to be working.
不定期副业成功案例分享
rbind(your_list)
返回一个 1x32 列表矩阵?do.call
将your_list
的元素作为参数传递给rbind
。它相当于rbind(your_list[[1]], your_list[[2]], your_list[[3]], ....., your_list[[length of your_list]])
。your_list
包含大小相等的向量。NULL
的长度为 0,因此它应该失败。