You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用dplyr::summarise结合across出现异常行为的原因排查

dplyr中summarise内across影响后续计算的原因分析

这不是异常行为,而是dplyr的设计规则导致的:summarise中的表达式严格按书写顺序执行,若新创建的列名与原始列名重复,会直接覆盖原始列,后续表达式引用该列时,使用的是已经计算出的汇总后的值(单值),而非分组前的原始分组内数据(向量)。

具体原因拆解

  1. across前置时的覆盖问题
    当你把across(matches("amount"), sum)放在summarise的开头,它会生成amount、another_amount等汇总后的列(每个组对应一个单值),直接覆盖了原始的amount列(每个组内是长度为10的向量):

    • sum_if_yes_1 = sum(amount[ yes_no == "Yes"]):此时amount是单值,而yes_no还是原始分组内的长度10的向量,索引操作因长度不匹配返回NA,求和后仍是NA。
    • sum_if_yes_2 = sum(ifelse(yes_no == "Yes", amount, 0)):单值amount会被自动循环匹配长度10的yes_no,相当于把汇总值重复10次,再对其中5个"Yes"对应的位置求和,结果就是汇总值 * 5(比如第一组5.47*5≈27.4)。
    • sum_if_yes_3正常:因为amount_if_yes的汇总值和原始列的分组总和完全一致,对单值求和自然得到正确结果。
  2. 调整顺序后恢复正常
    把自定义的sum计算放在across前面时,这些表达式引用的都是原始的分组内列数据(还没被覆盖),计算完成后across再生成汇总列,不会影响前面的结果。

  3. 与data.table的行为差异
    data.table在分组计算的j表达式中,所有列引用默认指向原始的分组内数据,即使先创建了同名列,后续引用仍会使用原始数据(除非显式指定新列),因此顺序不会影响结果。

正确解决方法

有两种可靠的方式避免这个问题:

方法1:使用.data代词明确引用原始列

.data代词会强制引用原始的输入数据列,不受后续列覆盖的影响:

test_data |>
    mutate( amount_if_yes = ifelse(yes_no == "Yes", amount, 0) ) |>
    group_by(group) |>
    summarise(
        across(matches("amount"), sum),
        sum_all = sum(.data$amount),
        sum_if_yes_1 = sum(.data$amount[.data$yes_no == "Yes"]),
        sum_if_yes_2 = sum(ifelse(.data$yes_no == "Yes", .data$amount, 0)),
        sum_if_yes_3 = sum(.data$amount_if_yes)
    )

方法2:修改across生成的列名,避免覆盖

通过.names参数给汇总列加前缀/后缀,和原始列名区分开:

test_data |>
    mutate( amount_if_yes = ifelse(yes_no == "Yes", amount, 0) ) |>
    group_by(group) |>
    summarise(
        across(matches("amount"), sum, .names = "sum_{.col}"),
        sum_all = sum(amount),
        sum_if_yes_1 = sum(amount[yes_no == "Yes"]),
        sum_if_yes_2 = sum(ifelse(yes_no == "Yes", amount, 0)),
        sum_if_yes_3 = sum(amount_if_yes)
    )

内容的提问来源于stack exchange,提问作者rosscova

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 00:33:09