给定一个列表变量,我想拥有每个元素位置的数据框。对于一个简单的非嵌套列表,它看起来非常简单。
例如,这是一个字符向量列表。
l <- replicate(
10,
sample(letters, rpois(1, 2), replace = TRUE),
simplify = FALSE
)
l
看起来像这样:
[[1]]
[1] "m"
[[2]]
[1] "o" "r"
[[3]]
[1] "g" "m"
# etc.
要获取职位数据框,我可以使用:
d <- data.frame(
value = unlist(l),
i = rep(seq_len(length(l)), lengths(l)),
j = rapply(l, seq_along, how = "unlist"),
stringsAsFactors = FALSE
)
head(d)
## value i j
## 1 m 1 1
## 2 o 2 1
## 3 r 2 2
## 4 g 3 1
## 5 m 3 2
## 6 w 4 1
给出一个比较棘手的嵌套列表,例如:
l2 <- list(
"a",
list("b", list("c", c("d", "a", "e"))),
character(),
c("e", "b"),
list("e"),
list(list(list("f")))
)
这不容易概括。
我希望这个例子的输出是:
data.frame(
value = c("a", "b", "c", "d", "a", "e", "e", "b", "e", "f"),
i1 = c(1, 2, 2, 2, 2, 2, 4, 4, 5, 6),
i2 = c(1, 1, 2, 2, 2, 2, 1, 2, 1, 1),
i3 = c(NA, 1, 1, 2, 2, 2, NA, NA, 1, 1),
i4 = c(NA, NA, 1, 1, 2, 3, NA, NA, NA, 1),
i5 = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, 1)
)
如何获取嵌套列表的位置数据框?
答案 0 :(得分:14)
这是一种产生与您所展示的输出略有不同的输出方法,但它在未来的发展中会更有用。
gplots
函数f <- function(l) {
names(l) <- seq_along(l)
lapply(l, function(x) {
x <- setNames(x, seq_along(x))
if(is.list(x)) f(x) else x
})
}
简单地迭代(递归地)遍历给定列表的所有级别并命名它的元素f
,其中1,2,...,n
是(子)列表的长度。然后,我们可以利用这样一个事实:n
默认情况下unlist
参数为use.names
,并且在命名列表中使用时有效(这就是为什么我们有使用TRUE
首先命名列表。
对于嵌套列表f
,它返回:
l2
现在,为了按照问题的要求返回unlist(f(l2))
# 1.1 2.1.1 2.2.1.1 2.2.2.1 2.2.2.2 2.2.2.3 4.1 4.2 5.1.1 6.1.1.1.1
# "a" "b" "c" "d" "a" "e" "e" "b" "e" "f"
,我执行此操作:
data.frame
并像这样应用:
g <- function(l) {
vec <- unlist(f(l))
n <- max(lengths(strsplit(names(vec), ".", fixed=TRUE)))
require(tidyr)
data.frame(
value = unname(vec),
i = names(vec)
) %>%
separate(i, paste0("i", 1:n), sep = "\\.", fill = "right", convert = TRUE)
}
由@AnandaMahto(谢谢!)提供的g(l2)
# value i1 i2 i3 i4 i5
#1 a 1 1 NA NA NA
#2 b 2 1 1 NA NA
#3 c 2 2 1 1 NA
#4 d 2 2 2 1 NA
#5 a 2 2 2 2 NA
#6 e 2 2 2 3 NA
#7 e 4 1 NA NA NA
#8 b 4 2 NA NA NA
#9 e 5 1 1 NA NA
#10 f 6 1 1 1 1
的改进版本将使用g
:
data.table
修改(积分转到@TylerRinkler - 谢谢!)
这有利于轻松转换为 data.tree 对象,然后可以将其转换为许多其他数据类型。使用g <- function(inlist) {
require(data.table)
temp <- unlist(f(inlist))
setDT(tstrsplit(names(temp), ".", fixed = TRUE))[, value := unname(temp)][]
}
的轻微模式:
g
并制作一个不错的情节:
g <- function(l) {
vec <- unlist(f(l))
n <- max(lengths(strsplit(names(vec), ".", fixed=TRUE)))
require(tidyr)
data.frame(
i = names(vec),
value = unname(vec)
) %>%
separate(i, paste0("i", 1:n), sep = "\\.", fill = "right", convert = TRUE)
}
library(data.tree)
x <- data.frame(top=".", g(l2))
x$pathString <- apply(x, 1, function(x) paste(trimws(na.omit(x)), collapse="/"))
mytree <- data.tree::as.Node(x)
mytree
# levelName
#1 .
#2 ¦--1
#3 ¦ °--1
#4 ¦ °--a
#5 ¦--2
#6 ¦ ¦--1
#7 ¦ ¦ °--1
#8 ¦ ¦ °--b
#9 ¦ °--2
#10 ¦ ¦--1
#11 ¦ ¦ °--1
#12 ¦ ¦ °--c
#13 ¦ °--2
#14 ¦ ¦--1
#15 ¦ ¦ °--d
#16 ¦ ¦--2
#17 ¦ ¦ °--a
#18 ¦ °--3
#19 ¦ °--e
#20 ¦--4
#21 ¦ ¦--1
#22 ¦ ¦ °--e
#23 ¦ °--2
#24 ¦ °--b
#25 ¦--5
#26 ¦ °--1
#27 ¦ °--1
#28 ¦ °--e
#29 °--6
#30 °--1
#31 °--1
#32 °--1
#33 °--1
#34 °--f
其他形式的数据呈现:
plot(mytree)
有关转换 data.tree 类型的详情:
https://cran.r-project.org/web/packages/data.tree/vignettes/data.tree.html#tree-conversion http://www.r-bloggers.com/how-to-convert-an-r-data-tree-to-json/
答案 1 :(得分:6)
这是另一种选择。它不会像@docendodiscimus那样快,但它仍然非常简单。
基本思路是使用“reshape2”/“data.table”中的melt
。 melt
method
list
melt(l2)
# value L3 L2 L4 L1
# 1 a NA NA NA 1
# 2 b NA 1 NA 2
# 3 c 1 2 NA 2
# 4 d 2 2 NA 2
# 5 a 2 2 NA 2
# 6 e 2 2 NA 2
# 7 e NA NA NA 4
# 8 b NA NA NA 4
# 9 e NA 1 NA 5
# 10 f 1 1 1 6
可创建如下输出:
rapply(l2, seq_along)
除了您感兴趣的列排序和最后一个值之外,它似乎包含您所需的所有信息。要获得您感兴趣的最后一个值,可以使用myFun <- function(inlist) {
require(reshape2) ## Load required package
x1 <- melt(inlist) ## Melt the data
x1[[paste0("L", ncol(x1))]] <- NA_integer_ ## Add a column to hold the position info
x1 <- x1[c(1, order(names(x1)[-1]) + 1)] ## Reorder the columns
vals <- rapply(inlist, seq_along) ## These are the positional values
positions <- max.col(is.na(x1), "first") ## This is where the positions should go
x1[cbind(1:nrow(x1), positions)] <- vals ## Matrix indexing for replacement
x1 ## Return the output
}
myFun(l2)
# value L1 L2 L3 L4 L5
# 1 a 1 1 NA NA NA
# 2 b 2 1 1 NA NA
# 3 c 2 2 1 1 NA
# 4 d 2 2 2 1 NA
# 5 a 2 2 2 2 NA
# 6 e 2 2 2 3 NA
# 7 e 4 1 NA NA NA
# 8 b 4 2 NA NA NA
# 9 e 5 1 1 NA NA
# 10 f 6 1 1 1 1
。
将这两个要求放在一起,你就会有这样的事情:
g
来自@docendodiscimus的答案中的g <- function(inlist) {
require(data.table)
temp <- unlist(f(inlist))
setDT(tstrsplit(names(temp), ".", fixed = TRUE))[, value := unname(temp)][]
}
的“data.table”版本更为直接:
ia32-libs
答案 2 :(得分:2)
与docendo类似,但尝试在递归内尽可能多地操作,而不是之后修复结果:
ff = function(x)
{
if(!is.list(x)) if(length(x)) return(seq_along(x)) else return(NA)
lapply(seq_along(x),
function(i) cbind(i, do.call(rBind, as.list(ff(x[[i]])))))
}
ans = do.call(rBind, ff(l2))
data.frame(value = unlist(l2),
ans[rowSums(is.na(ans[, -1L])) != (ncol(ans) - 1L), ])
# value X1 X2 X3 X4 X5
#1 a 1 1 NA NA NA
#2 b 2 1 1 NA NA
#3 c 2 2 1 1 NA
#4 d 2 2 2 1 NA
#5 a 2 2 2 2 NA
#6 e 2 2 2 3 NA
#7 e 4 1 NA NA NA
#8 b 4 2 NA NA NA
#9 e 5 1 1 NA NA
#10 f 6 1 1 1 1
rBind
是rbind
的包装,以避免出现“不匹配列”错误:
rBind = function(...)
{
args = lapply(list(...), function(x) if(is.matrix(x)) x else matrix(x))
nc = max(sapply(args, ncol))
do.call(rbind,
lapply(args, function(x)
do.call(cbind, c(list(x), rep_len(list(NA), nc - ncol(x))))))
}
答案 3 :(得分:0)
这也可以通过使用rrapply
返回rrapply
包中的rapply
包(基础how = "melt"
的扩展版本)来返回融化的数据。 {1}}:
reshape2::melt
注意:以后可以将级别列转换为数字列。
library(rrapply)
## use rapply or rrapply to convert terminal nodes to lists
l2_list <- rapply(l2, f = as.list, how = "replace")
## use rrapply with how = "melt" to return melted data.frame
l2_melt <- rrapply(l2_list, how = "melt")
#> L1 L2 L3 L4 L5 value
#> 1 ..1 ..1 <NA> <NA> <NA> a
#> 2 ..2 ..1 ..1 <NA> <NA> b
#> 3 ..2 ..2 ..1 ..1 <NA> c
#> 4 ..2 ..2 ..2 ..1 <NA> d
#> 5 ..2 ..2 ..2 ..2 <NA> a
#> 6 ..2 ..2 ..2 ..3 <NA> e
#> 7 ..4 ..1 <NA> <NA> <NA> e
#> 8 ..4 ..2 <NA> <NA> <NA> b
#> 9 ..5 ..1 ..1 <NA> <NA> e
#> 10 ..6 ..1 ..1 ..1 ..1 f
计算时间
使用rrapply(l2_melt, condition = function(x, .xname) grepl("^L", .xname), f = function(x) as.numeric(sub("\\.+", "", x)))
#> L1 L2 L3 L4 L5 value
#> 1 1 1 NA NA NA a
#> 2 2 1 1 NA NA b
#> 3 2 2 1 1 NA c
#> 4 2 2 2 1 NA d
#> 5 2 2 2 2 NA a
#> 6 2 2 2 3 NA e
#> 7 4 1 NA NA NA e
#> 8 4 2 NA NA NA b
#> 9 5 1 1 NA NA e
#> 10 6 1 1 1 1 f
而不是rrapply
可以显着提高(非常)大型嵌套列表的速度,如下面的基准时间所示:
reshape2::melt