我有一个嵌套的数据列表。它的长度是132,每一项是一个长度为20的列表。是否有一种快速的方法可以将这种结构转换为具有132行和20列数据的数据帧?
下面是一些示例数据:
l <- replicate(
132,
as.list(sample(letters, 20)),
simplify = FALSE
)
我有一个嵌套的数据列表。它的长度是132,每一项是一个长度为20的列表。是否有一种快速的方法可以将这种结构转换为具有132行和20列数据的数据帧?
下面是一些示例数据:
l <- replicate(
132,
as.list(sample(letters, 20)),
simplify = FALSE
)
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)
用rbind
do.call(rbind.data.frame, your_list)
编辑:以前的版本返回list的data.frame而不是向量(正如@IanSudbery在评论中指出的那样)。
修正样本数据,使其符合原始描述“每个项目是一个长度为20的列表”
mylistlist <- replicate(
132,
as.list(sample(letters, 20)),
simplify = FALSE
)
我们可以像这样把它转换成一个数据帧:
data.frame(t(sapply(mylistlist,c)))
Sapply将其转换为矩阵。 data.frame将矩阵转换为数据帧。
导致:
你可以使用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相同的结果。
更多的答案,以及这个问题的答案中的时间: 将列表转换为数据帧的最有效方法是什么?
最快的方法,不产生一个数据框架与列表,而不是向量的列似乎是(从马丁摩根的回答):
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]])))
包数据。Table中有rbindlist函数,它是do的一个超快速实现。调用(rbind列表(…))。
它可以接受一个列表的列表,data。frame或data。表作为输入。
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。frame使用as。data。frame(DT)
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
如果你几乎没有像素,你可以在一行w/ recast()中完成这一切。
扩展@Marek的回答:如果你想避免字符串变成因素和效率不是一个问题,尝试一下
do.call(rbind, lapply(your_list, data.frame, stringsAsFactors=FALSE))
l <- replicate(10,list(sample(letters, 20)))
a <-lapply(l[1:10],data.frame)
do.call("cbind", a)
有时你的数据可能是相同长度的向量的列表。
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
对于像从嵌套JSON中获得的3级或更多级别的深度嵌套列表的一般情况:
{
"2015": {
"spain": {"population": 43, "GNP": 9},
"sweden": {"population": 7, "GNP": 6}},
"2016": {
"spain": {"population": 45, "GNP": 10},
"sweden": {"population": 9, "GNP": 8}}
}
考虑一下melt()将嵌套列表转换为高格式的方法:
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(),然后再次扩大到一个整洁的数据集,其中每个变量组成一个a列,每个观察值组成一行:
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
tibble包有一个函数enframe(),它通过将嵌套的列表对象强制转换为嵌套的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]>
Since you have several nests in your list, l, you can use the unlist(recursive = FALSE) to remove unnecessary nesting to get just a single hierarchical list and then pass to enframe(). I use tidyr::unnest() to unnest the output into a single level "tidy" data frame, which has your two columns (one for the group name and one for the observations with the groups value). If you want columns that make wide, you can add a column using add_column() that just repeats the order of the values 132 times. Then just spread() the values.
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包(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
根据列表的结构,有一些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
一个简短的(但可能不是最快的)方法是使用基底r,因为数据帧只是一个长度相等的向量的列表。因此,你的输入列表和一个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)))
行名看起来很讨厌,但是您总是可以用
行名称(new_df) <- 1:nrow(new_df)
下面这个简单的命令对我有用:
myDf <- as.data.frame(myList)
参考(Quora的答案)
> 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,检查。rows = FALSE, check.names = TRUE,: 参数暗示不同的行数:3,4
注意:答案是朝着问题的标题,可能会跳过问题的一些细节
对于使用purrr系列解决方案的并行(多核,多会话等)解决方案,使用:
library (furrr)
plan(multisession) # see below to see which other plan() is the more efficient
myTibble <- future_map_dfc(l, ~.x)
其中l是列表。
要对最有效的计划()进行基准测试,您可以使用:
library(tictoc)
plan(sequential) # reference time
# plan(multisession) # benchamark plan() goes here. See ?plan().
tic()
myTibble <- future_map_dfc(l, ~.x)
toc()
如何使用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)
我发现的每个解决方案似乎只适用于列表中的每个对象都具有相同的长度。当列表中对象的长度不相等时,我需要将列表转换为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
尝试折叠::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")
我也想提出这个解决方案。尽管它看起来与其他解决方案相似,但它使用了rbind。从胶合板包装填充。这在列表缺少列或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
如果您的列表具有相同尺寸的元素,则可以使用来自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()
结果是一个有两行的数据帧。
从不同的角度;
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.