R语言基于列索引条件对含NA的dataframe列求和方法咨询
R 数据框按条件列求和解决方案
函数修正实现
首先注意不要使用str作为函数名,会与R基础包自带的结构查看函数str()冲突,修正后的完整函数如下:
calc_adj_agg <- function(df, overwrite = TRUE, na.rm = TRUE){ # 匹配目标列并按年份升序排序,避免列顺序错乱 nms <- grep("adj_avg_[0-9]+$", names(df), value = TRUE) nms <- nms[order(as.integer(sub("adj_avg_", "", nms)))] # 生成新列名 revnms <- gsub("adj_avg", "agg_pass1", nms) if(!overwrite) { revnms <- setdiff(revnms, names(df)) # 过滤对应要计算的原始列 nms <- nms[gsub("adj_avg", "agg_pass1", nms) %in% revnms] } # 按规则计算求和值 df[, revnms] <- lapply(seq_along(nms), function(i){ cur_col <- df[[nms[i]]] if(i == 1){ # 首列直接求和 sum_val <- sum(cur_col, na.rm = na.rm) } else { prev_col <- df[[nms[i-1]]] # 筛选当前列和前一列均非NA的行求和 valid_idx <- !is.na(cur_col) & !is.na(prev_col) sum_val <- sum(cur_col[valid_idx], na.rm = na.rm) } # 求和结果填充整列 rep(sum_val, nrow(df)) }) return(df) }
lapply 逻辑说明
你原来的写法是直接遍历列数据,无法获取前一列的对应值,因此改为遍历列的索引位置:
- 当索引为1时,对应首列直接对全列求和
- 索引大于1时,先提取当前列和前一列的向量,取两者同时非NA的行索引,再对当前列的有效行求和
- 最后将求和得到的单个值重复n次(n为数据框行数),填充为新列
更简便的实现方式
如果你使用tidyverse生态,可通过dplyr+purrr实现更简洁的代码,性能与基础包写法基本一致:
library(dplyr) library(purrr) calc_adj_agg_tidy <- function(df, overwrite = TRUE, na.rm = TRUE){ nms <- grep("adj_avg_[0-9]+$", names(df), value = TRUE) nms <- nms[order(as.integer(sub("adj_avg_", "", nms)))] revnms <- gsub("adj_avg", "agg_pass1", nms) if(!overwrite) { revnms <- setdiff(revnms, names(df)) nms <- nms[gsub("adj_avg", "agg_pass1", nms) %in% revnms] } # 生成求和规则的命名列表 sum_rules <- set_names( map(seq_along(nms), function(i){ if(i == 1){ ~sum(.data[[nms[i]]], na.rm = na.rm) } else { ~sum(.data[[nms[i]]][!is.na(.data[[nms[i]]]) & !is.na(.data[[nms[i-1]]])], na.rm = na.rm) } }), revnms ) # 批量生成新列 df %>% mutate(!!!sum_rules) }
效果验证
使用你提供的示例数据运行函数,得到的各年求和结果如下:
- agg_pass1_2016:131561293.9
- agg_pass1_2017:138895480.3
- agg_pass1_2018:133534898.2
- agg_pass1_2019:133108702.6
- agg_pass1_2020:136639690.1
完全符合你描述的计算规则。
内容的提问来源于stack exchange,提问作者jvalenti
相关产品推荐
相关产品推荐

