tidyr::crossing能否在函数中配合curly-curly语法使用?
问题解决与分析
修复代码让它正常运行
原代码的核心问题是未正确处理分组内的数据生成笛卡尔积,且未适配summarise的返回规则。以下是修复后的可运行代码:
library(dplyr) library(tidyr) f <- function(db, entity, trait) { db %>% summarise( # 先提取分组内的trait唯一值,将交叉结果包装为列表适配summarise i = list(crossing({{ trait }}, {{ trait }})), .by = {{ entity }} ) %>% unnest(i) # 展开列表列得到扁平化结果 } db <- data.frame( entity = c("A", "A", "B", "B"), beta = c("X", "Y", "X", "Z") ) f(db, entity, beta)
运行后会输出每个entity对应的trait笛卡尔积组合:
entity beta beta1 1 A X X 2 A X Y 3 A Y X 4 A Y Y 5 B X X 6 B X Z 7 B Z X 8 B Z Z
原代码的问题点
summarise默认要求每个分组返回单个值(或长度为1的对象),直接返回crossing生成的数据框会报错,需要用list()包装后再通过unnest展开。- 未针对分组内的
trait值生成交叉,原写法会使用整个列的所有值,而非当前分组内的唯一值。
关于crossing和expand_grid的选择
两者都是生成笛卡尔积的工具,核心差异仅在于:
crossing会自动对输入去重并排序;expand_grid保留原始顺序和重复值。- 函数式编程中两者表现无本质差异,问题不在工具本身,而在于使用逻辑是否适配场景。如果需要保留原始顺序或重复值,可以直接替换
crossing为expand_grid,示例如下:
f <- function(db, entity, trait) { db %>% summarise( i = list(expand_grid({{ trait }}, {{ trait }})), .by = {{ entity }} ) %>% unnest(i) }
总结
tidyr在函数式编程中的表现不存在固有问题,只需正确理解分组逻辑和summarise的返回规则即可。crossing和expand_grid可根据需求灵活选择,无需刻意替换。
内容的提问来源于stack exchange,提问作者GiulioGCantone
相关产品推荐
相关产品推荐

