为何do.call(c, res)与do.call(rbind, res)返回结果长度/行数不同?
R语言中do.call(c, res)与do.call(rbind, res)结果差异的原因
问题现象
执行代码后出现以下不一致:
- 列表
res长度为5713 do.call(c, res)生成的res1长度为5712,丢失了res中索引4100、名称为recursive的元素(值为"@早坂愛")do.call(rbind, res)生成的res2行数为5713,所有元素均保留
复现代码
# rm(list = ls()) dta <- read.csv("https://raw.githubusercontent.com/JoshXie0809/cchat_wife_election_1/main/final.csv") library(stringi) library(parallel) split(dta$content, dta$nameid) |> lapply( function(x) stri_extract(x[1], regex = "(@喜多郁代|@早坂愛)") ) -> res res1 <- do.call(c, res) res2 <- do.call(rbind, res) length(res) # [1] 5713 length(res1) # [1] 5712 nrow(res2) # [1] 5713 which(!names(res) %in% names(res1)) # [1] 4100 which(!names(res) %in% rownames(res2)) # integer(0) res[4100] # $recursive # [1] "@早坂愛"
原因分析
核心差异来自两个函数的参数机制:
do.call(c, res)的问题:c()函数本身带有一个名为recursive的参数(默认值FALSE,用于控制是否递归拼接嵌套列表)。当用do.call把res的元素作为参数传给c()时,列表中名为recursive的元素会被R解析为c()的函数参数,而非待拼接的列表元素,因此被排除在结果res1之外。do.call(rbind, res)无此问题:rbind()函数没有recursive这个参数,所以所有列表元素都会被当作待绑定的对象处理,不会出现元素被误解析为参数的情况,因此res2保留了所有5713个元素。
验证示例
可以用简单列表验证这个机制:
test_list <- list(a = 1, recursive = 2, b = 3) do.call(c, test_list) # 输出: # a b # 1 3 # 可见recursive元素被当作参数处理,未出现在结果中 do.call(rbind, test_list) # 输出: # [,1] # a 1 # recursive 2 # b 3 # 所有元素均保留
解决方法
如果需要用c()拼接列表且保留所有元素,可采用以下任意一种方式:
- 显式指定
recursive参数:在调用do.call时额外传递recursive=FALSE,覆盖列表中的同名元素:res1_fixed <- do.call(c, c(res, list(recursive = FALSE))) length(res1_fixed) # 结果为5713 - 重命名冲突元素:修改列表中
recursive元素的名称:names(res)[names(res) == "recursive"] <- "recursive_name" res1_fixed <- do.call(c, res) - 使用
unlist替代:当列表所有元素都是长度为1的向量时,unlist可以安全完成拼接:res1_fixed <- unlist(res, use.names = TRUE)
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

