按Y1/Y2/Y3列名分组实现跨行累加(跳过零值+Y1处重置)的函数修正需求
Y1/Y2/Y3列名分组实现跨行累加(跳过零值+Y1处重置)的函数修正需求
核心需求回顾
先明确你要实现的规则,避免理解偏差:
- 按列名后缀分组:每一组由
Y1.xxx、Y2.xxx、Y3.xxx组成(比如Y1.Base/Y2.Base/Y3.Base是一组,Y1.Scenario_1/Y2.Scenario_1/Y3.Scenario_1是另一组) - 每组内按Y1→Y2→Y3的顺序做带零值跳过的累加:
- 零值单元格保持原样,不参与累加计算,也不被累加结果覆盖
- 非零单元格的累加值 = 前面所有非零的同组单元格的和 + 当前单元格的值
- 每组从Y1开始重置累加器,跨组不继承累加值
原函数的问题分析
你的原函数有两个关键逻辑错误:
- 重置累加器的时机错误:原函数是在处理完Y1列后才重置累加器,导致Y2/Y3的累加器初始为0,无法继承Y1的非零值
- 跨组累加未隔离:原函数是全局遍历整行的所有列,只有遇到Y1列才重置,但重置是在处理完Y1值之后,导致Y1值会加上上一组最后残留的累加值(比如3C行的
Y1.Scenario_3被上一组Y3.Scenario_1的-1污染,变成1 + (-1) = 0)
修正方案:按分组处理(推荐用dplyr+purrr,更清晰)
我推荐先把列按后缀分组,然后对每组单独处理累加逻辑,这样能彻底避免跨组污染,逻辑也更直观:
library(dplyr) library(purrr) # 定义单组的累加函数:输入一组的Y1/Y2/Y3值,返回处理后的结果 group_cumsum <- function(group_vals) { cum_sum <- 0 result <- numeric(3) for (i in 1:3) { val <- group_vals[i] if (val != 0) { cum_sum <- cum_sum + val result[i] <- cum_sum } else { result[i] <- 0 # 保持原零值 } } result } # 处理数据: df_processed <- df %>% # 保留第一列作为标识,其他列按后缀分组 mutate(across(-Ratingstufe, .names = "{.col}")) %>% # 把列按后缀拆分:提取Y1/Y2/Y3后面的部分作为分组键 pivot_longer(-Ratingstufe, names_to = c("Y", "group"), names_sep = "\\.") %>% pivot_wider(names_from = Y, values_from = value) %>% # 对每个分组应用累加函数 mutate(across(Y1:Y3, ~ group_cumsum(c(Y1, Y2, Y3))[which(c(Y1, Y2, Y3) == .x)], .by = group)) %>% # 转回原宽格式 pivot_longer(Y1:Y3, names_to = "Y", values_to = "value") %>% unite(col = "col_name", Y, group, sep = ".") %>% pivot_wider(names_from = col_name, values_from = value) %>% # 恢复原列顺序(可选,和输入列顺序一致) select(all_of(names(df))) # 查看结果(重点看3C行) tail(df_processed)
或者修正原循环函数(保持基础R风格)
如果你更倾向于用基础R的循环,需要调整重置时机和分组逻辑:
cumsum_ignore_zeros_fixed <- function(row, col_names) { # 先把列按分组拆分,获取每个分组的列索引 groups <- unique(sub("Y[1-3]\\.", "", col_names)) result <- numeric(length(row)) for (g in groups) { # 获取当前组的三个列索引:Y1.xxx, Y2.xxx, Y3.xxx group_cols <- grep(paste0("Y[1-3]\\.", g), col_names) if (length(group_cols) != 3) next # 跳过不完整的组 cum_sum <- 0 for (i in group_cols) { val <- row[i] if (val != 0) { cum_sum <- cum_sum + val result[i] <- cum_sum } else { result[i] <- val } } } return(result) } # 应用修正后的函数 df_fixed <- df df_fixed[-1] <- t(apply(df_fixed[-1], 1, cumsum_ignore_zeros_fixed, col_names = names(df_fixed)[-1])) # 查看结果 tail(df_fixed)
验证结果
运行以上任意代码后,3C行的结果会符合你的预期:
Y3.Scenario_1:2(Y1) + (-1)(Y3)= 1Y1.Scenario_3:保持1(没有跨组累加)Y2.Scenario_3:1(Y1) + 2(Y2)= 3Y3.Scenario_3:3 + 1 = 4
完全匹配你标记的期望结果。
备注:内容来源于stack exchange,提问作者Ingrid
相关产品推荐
相关产品推荐

