【问题标题】:Recursively apply function to list elements递归地将函数应用于列表元素
【发布时间】:2015-05-27 15:35:27
【问题描述】:

使用rmatio 包,我得到类似于以下的嵌套列表:

nestedlist <- list(
    a = list( a = list(1:10), b = list(35)),
    b = list(11:25)
)

理想情况下,我希望它看起来像这样(所有列表中的单个未命名元素都被该元素替换):

nestedlist <- list(a = list(a=1:10, b=35), b = 11:25)

我尝试了以下已经尝试过的:

unlist(nestedlist) # returns one vector with all elements

selective_unlist <- function(e)
    if(is.list(e) &&is.null(names(e))) unlist(e) else e

# only calls the function with each leaf, so nothing gets replaced
rapply(nestedlist, how='replace', selective_unlist)

# works, but only for 2 levels
lapply(nestedlist, selective_unlist)

# works, but using explicit recursion is slow for large datasets
recursive_selective_unlist <- function(e)
    if(is.list(e)) {
        if(is.null(names(e))) unlist(e)
        else lapply(e, recursive_selective_unlist)
    }   else e

有没有更好/更快的方法来简化这些嵌套列表,或者递归函数是我最好的选择?

【问题讨论】:

  • 指定 recursive=FALSE 当你只有一个嵌套程度时应该可以解决问题(如在你的示例对象中) - unlist(nestedlist,recursive=F)
  • @nrussell 感谢您的建议,但有些文件最多有 8 个级别
  • @Frank 我忘了在编辑中更新它,现在已经更正了
  • 可能与 2002 年相关的 convo:stat.ethz.ch/pipermail/r-help/2002-June/022349.html 作者说他找到了“仅在终端节点上应用功能”的解决方案,但没有提供它,而是选择了死链接并“给我发电子邮件” "。

标签: r


【解决方案1】:

按照@Pafnucy 的想法,我会使用

ff <- function(x) if (is.list(x[[1]])) lapply(x,ff) else unlist(x)

会的

ff(nestedlist)
# $a
# $a$a
#  [1]  1  2  3  4  5  6  7  8  9 10
# 
# $a$b
# [1] 35
# 
# 
# $b
#  [1] 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# check result:
identical(list(a = list(a=1:10, b=35), b = 11:25),ff(nestedlist))
# [1] TRUE

【讨论】:

  • 这行得通,但是对于一个小数据集(4 个级别,50 个元素),它比显式递归慢大约 30%。
  • 不过,这个函数需要lapply(f(nl3), as.vector) 才能在 nl2 和 nl3 上工作,因为它会在这里和那里产生矩阵
  • @Pafnucy 我刚刚从sapply 切换到lapply,所以现在不应该有任何矩阵。
  • @tstenner 如果rmatio 包给你这种格式的大对象,也许是有原因的,你应该学习如何使用它们而不是用这种方式修改它们。我从来没有使用过 R 中的列表,它太大以至于对其进行操作非常耗时。
【解决方案2】:

处理任意深度的嵌套:

f <- function(x) {
    if (is.list(x)) unname(c(sapply(unlist(x), f))) else x
}

# sample data
nl2 <- list(a = list(a = list(1:5), b = list(1:5)))
nl3 <- list(p = nl2, q = c(9,9,9))

中间输出:

> f(nl2)
 [1] 1 2 3 4 5 1 2 3 4 5
> f(nl3)
 [1] 1 2 3 4 5 1 2 3 4 5 9 9 9

添加最后一步,因为f 太深了,我们想要深度为 1 的列表

unstackList <- function(x) lapply(x, f)
unstackList(nl3)

输出:

$p
 [1] 1 2 3 4 5 1 2 3 4 5

$q
[1] 9 9 9

【讨论】:

  • 这不等于unname(unlist(x))吗?
  • @Frank 这几乎是 OP 想要的,没有最后一步。解决方案是lapply(nestedlist, f)f 定义如上。我将其添加到解决方案中
  • 它仍然不是 OP 的输出。尝试identicalunstackList(nestedlist) 反对OP 中提到的这个结果:list(a = list(a=1:10, b=35), b = 11:25)
  • 结果列表应该是嵌套的,但没有没有名称(和单个元素)的不需要的列表。
  • @tstenner 好吧,我突然想到我对上述答案完全偏离主题。真正的目标是降低具有 1 个数字元素的列表的嵌套深度。 list(1:10, a = 4:8) 怎么样,我们应该以 list(a=4:8) 结尾吗?
猜你喜欢
  • 2019-02-11
  • 2019-06-10
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
  • 2019-12-05
  • 2012-05-20
相关资源
最近更新 更多