dplyr管道中summarise内多个across的求值时序问题
dplyr summarise中多个across的求值逻辑与解决方案
问题背景
你遇到的核心问题是:在dplyr的summarise()中,多个across()调用的执行顺序会影响结果——调换两个across的顺序后输出不同。
两段对比代码:
第一段代码:
data(iris) iris_summary <- iris %>% group_by(Species) %>% summarise( across( .cols = starts_with("Sepal"), .fns = mean ), across( .cols = starts_with("Petal"), .fns = ~ .x[which.max(Sepal.Length)] ) )
第二段代码:
iris_summary_2 <- iris %>% group_by(Species) %>% summarise( across( .cols = starts_with("Petal"), .fns = ~ .x[which.max(Sepal.Length)] ), across( .cols = starts_with("Sepal"), .fns = mean ) )
你的疑问:这是否是求值时序问题?原本期望两个across都基于管道上一步的原始数据求值,但结果显示第二个across似乎用了第一个across生成的Sepal.Length而非原始数据。如何在不改变代码结构的前提下强制使用原始分组数据?
原因分析
是的,这完全是求值顺序导致的问题。在dplyr的summarise()中:
- 所有表达式(包括
across()调用)按书写顺序依次执行 - 每执行完一个表达式,生成的新列会立即加入到当前的临时结果数据框中
- 后续的表达式默认从这个更新后的临时数据框中读取列,而非始终引用分组前的原始数据
具体到你的例子:
- 第一段代码中,第一个
across先执行,生成了Sepal.Length(分组均值,单值);第二个across里的which.max(Sepal.Length)会读取这个单值,which.max()对单值返回1,因此取的是每组Petal列的第一个值,而非原始数据中Sepal.Length最大值对应的Petal值。 - 第二段代码中,第一个
across先执行时,临时数据框里还没有新的Sepal.Length列,所以which.max(Sepal.Length)读取的是原始分组后的Sepal.Length向量,能正确找到最大值对应的位置,结果符合预期。
解决方案(不改变代码结构)
要强制后续across引用分组后的原始数据,可以使用cur_data()函数——它会返回当前分组的原始数据(不受前面summarise步骤生成的新列影响)。
修改第一段代码如下:
data(iris) iris_summary <- iris %>% group_by(Species) %>% summarise( across( .cols = starts_with("Sepal"), .fns = mean ), across( .cols = starts_with("Petal"), .fns = ~ .x[which.max(cur_data()$Sepal.Length)] ) )
这样无论两个across的顺序如何,第二个across都会从原始分组数据中读取Sepal.Length向量,结果会和第二段代码完全一致。
内容的提问来源于stack exchange,提问作者emaoca
相关产品推荐
相关产品推荐

