如何用rapply提取深度嵌套列表特定子层级,高效合并向量为矩阵?
高效提取嵌套列表中的c和p并合并为矩阵(Base R方案)
首先先放出生成测试数据的代码,方便复现场景:
set.seed(42) L <- replicate(10, list(list(c=sample(10, 2), x1=rnorm(10), x2=rnorm(10)), p=sample(10, 10)), simplify=FALSE)
现有方案回顾
你已经找到了提取p的简洁方法:
p_mat <- do.call(rbind, sapply(L, "[", 2))
而提取c用了双重sapply:
c_mat <- do.call(rbind, sapply(sapply(L, `[`, 1), `[`, 1))
这两种方法都能生效,但确实可以优化得更高效、更易读。
更高效的直接索引方案(推荐)
因为你的列表结构是固定的——每个顶层元素都是list(内部列表, p向量),而内部列表里的c是明确的子元素,我们可以直接用[[精准访问,避免嵌套sapply的冗余操作:
提取c
# 直接定位每个顶层元素的第一个子元素中的c c_mat <- do.call(rbind, lapply(L, function(x) x[[1]]$c))
如果你的R版本在4.1及以上,还可以用更简洁的箭头函数写法:
c_mat <- do.call(rbind, lapply(L, \(x) x[[1]]$c))
提取p
同样,直接访问顶层元素的第二个位置即可,用lapply比sapply更稳定(避免自动简化带来的意外问题):
p_mat <- do.call(rbind, lapply(L, `[[`, 2))
这种直接索引的方式比双重sapply快得多,因为它跳过了不必要的递归遍历,直接定位到目标元素。
用rapply实现的方案
如果你一定要用rapply,其实也可以做到,但rapply是递归遍历所有叶子节点,我们需要额外过滤出名字为c的元素:
# 递归遍历所有元素,以列表形式收集结果 c_list <- rapply(L, function(x) x, how = "list") # 过滤出名字为c的元素并合并成矩阵 c_mat_rapply <- do.call(rbind, unlist(c_list, recursive = FALSE)[names(unlist(c_list, recursive = FALSE)) == "c"])
不过说实话,这种方法不如直接索引高效,因为rapply会遍历所有的x1、x2元素,做了很多无用功,只适合结构不固定的场景。
后续计算列均值
拿到合并后的矩阵后,直接用colMeans就能计算每列的均值:
# 计算c矩阵的列均值 colMeans(c_mat) # 计算p矩阵的列均值 colMeans(p_mat)
效率对比(可选)
如果想直观看到不同方法的速度差异,可以用microbenchmark包测试:
library(microbenchmark) microbenchmark( double_sapply = do.call(rbind, sapply(sapply(L, `[`, 1), `[`, 1)), direct_index = do.call(rbind, lapply(L, \(x) x[[1]]$c)), rapply_method = do.call(rbind, unlist(rapply(L, function(x) x, how = "list"), recursive = FALSE)[names(unlist(rapply(L, function(x) x, how = "list"), recursive = FALSE)) == "c"]), times = 1000 )
测试结果会显示,直接索引的方法是最快的,双重sapply次之,rapply方法最慢。
内容的提问来源于stack exchange,提问作者jay.sf
相关产品推荐
相关产品推荐

