将一个列表的嵌套列表展开,而不使用recursive=FALSE。

6

我有一个包含其他列表的列表,同时也包含一些不是列表的额外对象,例如数据框。 我想将它压扁成单个列表,但通常的建议(参见如何展平列表将列表转换为单个列表)的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

这个方法是有效的,但有没有更加直接的想法?(我非常希望使用基础R解决方案,而不是tidyverse。)
2个回答

4

诚然,这可能不够“优雅”,但仍然:

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")

因此,您基本上定义了哪些类不应进一步压缩。我认为这个解决方案确实可以称之为“优雅”。


2
对于这种情况,即使使用rrapply(d, classes = c("numeric", "data.frame"), how = "flatten")也可以。 - tmfmnk
真的。我甚至没试着去定义几个类。谢谢你的提示。 - deschen
谢谢 - 我之前没有接触过rrapply。这看起来很有趣,但我更喜欢排除非基本包,而且这两个方法都比我的解决方案慢得多。 - Josh

3

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

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

1
应该使用FALSE而不是F,以确保安全。 - user2554330
这基本上与我的尝试做的完全相同;添加列表,然后删除它们,对吧?不幸的是,它也比较慢,经过微基准测试证实。 - Josh
1
基本上,它是相同的原理,但泛化到任意深度的列表。 - Maël
啊,好观点。这对我的特定情况没有帮助(嵌套只会进行一层),但其他人可能会发现这非常有用! - Josh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接