使用cols=dplyr::everything()时tidyr::unnest报“无法循环输入”错误
tidyr::unnest同时展开多列报错的原因与解决方法
问题场景
先构造测试数据:
library(magrittr) sapply(c("dplyr", "tibble", "tidyr"), requireNamespace) mytbl <- structure( list( A = list(c("A.1", "A.2")), B = list(c("B.1", "B.2")), C = list("C.1"), D = list(c("D.1", "D.2")), E = list(c("E.1", "E.2", "E.3"))), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -1L))
单独展开每一列时,代码可正常运行:
lapply(names(mytbl), function(x) tidyr::unnest(mytbl, cols = x))
但用dplyr::everything()选择所有列同时展开时,直接报错:
mytbl %>% tidyr::unnest(cols = dplyr::everything())
报错信息:
Error in `tidyr::unnest()`: ! In row 1, can't recycle input of size 2 to size 3. Run `rlang::last_trace()` to see where the error occurred.
报错原因
tidyr::unnest在单独展开列和同时展开多列时的行为逻辑完全不同:
- 单独展开某一列时,其他列属于「非展开列」,它们的单元素列表会被视为标量,自动重复填充到展开后的行数,因此不会有长度匹配问题。
- 同时展开多列时,所有指定列都会被展开,此时要求每行中所有待展开列的列表长度必须满足R的向量回收规则:要么长度完全相同,要么存在长度为1的列表(可被循环填充到其他列的长度)。
在测试数据中,第1行各列的列表长度分别是2(A)、2(B)、1(C)、2(D)、3(E)。E列的长度3和其他列的长度2不满足回收规则(3不是2的整数倍),因此触发「无法回收」的报错。
解决方法
根据你想要的最终结果,有几种不同的解决方式:
1. 生成所有列的笛卡尔积(所有元素组合)
如果需要得到各列列表元素的所有可能组合,可以先将每列转为单独的数据集,再交叉合并:
library(purrr) mytbl %>% imap(~tibble(!!.y := .x)) %>% # 把每列转为单独的tibble map(~unnest(.x, cols = everything())) %>% # 分别展开每列 reduce(crossing) # 交叉合并所有数据集
2. 按最长列表对齐,短列表重复填充
如果希望以最长的列表(E列,长度3)为基准,让短列表重复元素来对齐长度,再统一展开:
# 获取所有列中最长的列表长度 max_len <- mytbl %>% map_int(~length(.x[[1]])) %>% max() # 调整每个列的列表,短列表重复填充到最长长度 mytbl_adjusted <- mytbl %>% mutate(across(everything(), ~map(.x, ~rep(.x, length.out = max_len)))) # 展开所有列 mytbl_adjusted %>% unnest(cols = everything())
3. 保留单独展开每列的结果集合
如果只需要单独展开每列后的结果列表,直接用原来的lapply或purrr::map即可:
# 用purrr的写法更简洁 map(names(mytbl), ~unnest(mytbl, cols = .x))
内容的提问来源于stack exchange,提问作者balin
相关产品推荐
相关产品推荐

