r语言 - 将列表的列表平坦化,而不递归=FALSE



我有一个列表,其中包含一些其他列表,但也包含其他不是列表的对象,如数据帧。我想将其平摊为单个列表,但通常的建议(参见如何平摊列表的列表?或者将一个列表的列表转换为一个列表)的unlist(..., recursive = FALSE)将不起作用,因为它也在data.frame上运行。

> d <- list(list(a = 1, b = 2), c = data.frame(1:4))
> d
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 2

$c
X1.4
1    1
2    2
3    3
4    4
> unlist(d, recursive = FALSE)
$a
[1] 1
$b
[1] 2
$c.X1.4
[1] 1 2 3 4

预期的结果是$c将保留相同的data.frame结构。

到目前为止,我唯一的解决方案是在unlist之前为所有非列表对象添加一个额外的列表层,但这是一个非常不优雅的解决方案。

> unlist(lapply(d, function(x) if(!inherits(x, "list")) list(x) else x), recursive = FALSE)
$a
[1] 1
$b
[1] 2
$c
X1.4
1    1
2    2
3    3
4    4

这是有效的,但是有人有更直接的想法吗?(我更喜欢base R解,而不是tidyverse。)

诚然,不一定"优雅",但仍然:

library(rrapply)
c(rrapply(rrapply(d, classes = "list", how = "flatten"), how = "flatten"),
rrapply(d, f = as.data.frame, classes = "data.frame", how = "flatten"))
$a
[1] 1
$b
[1] 2
$c
X1.4
1    1
2    2
3    3
4    4

正如@tmfmnk在评论中提到的,在您的特殊情况下,即使这样也可以工作:

rrapply(d, classes = c("numeric", "data.frame"), how = "flatten")

所以你基本上定义了哪些类不应该进一步扁平化。这个特殊的解决方案我称之为"优雅的"。

您可以使用递归函数(受此答案的启发)。

flattenMixed <- function(x) {
if (is.data.frame(x)) return(list(x))
if (!is.list(x)) return(x)
unlist(lapply(x, flattenMixed), FALSE)
}
flattenMixed(d)
$a
[1] 1
$b
[1] 2
$c
X1.4
1    1
2    2
3    3
4    4

最新更新