如何在R中实现DataFrame的加减组合聚合运算?
解决带加减规则的DataFrame聚合问题
输入数据
首先明确输入的两个数据集:
input <- structure(list(V1 = c("Team_2022", "Team_2022", "Team_2022"), V2 = c("Frank", "Mary", "John"), V3 = c("Sydney", "Sydney", "Sydney"), V4 = c(55, 76, 14)), row.names = c(NA, -3L), class = c("data.table", "data.frame")) teams <- structure(list(V1 = c("team1", "team2", "team3"), V2 = c("Mary + Frank - John","Mary + John - Frank", "John + Frank - Mary")), class = "data.frame", row.names = c(NA, -3L)) desired_output <- structure(list(V1.x = c("team1", "team2", "team3"), V1.y = c("Team_2022", "Team_2022", "Team_2022"), V2 = c("Mary + Frank - John", "Mary + John - Frank", "John + Frank - Mary"), V3 = c("Sydney", "Sydney", "Sydney"), V4 = c(117, 35, -7)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -3L))
问题分析
原函数仅能处理加法的核心问题是:拆分表达式时未保留符号信息,导致减法项被当作0处理,最终求和结果错误。要解决带加减的聚合,核心是提取每个项的符号系数,再与对应值加权求和。
方法1:解析符号与项,加权求和(推荐,安全通用)
通过正则表达式拆分带符号的项,提取系数后计算加权和,兼容多分组场景:
library(tidyverse) result <- teams %>% # 为表达式开头补+(确保所有项带符号),按正负号拆分 mutate(terms = str_replace(V2, "^(?=[A-Za-z])", "+") %>% str_split("(?=[+-])")) %>% unnest(terms) %>% # 提取系数(1或-1)和对应的姓名 mutate( coeff = ifelse(str_starts(terms, "-"), -1, 1), name = str_remove(terms, "^[+-]") ) %>% # 关联input中的数值 left_join(input, by = c("name" = "V2")) %>% replace_na(list(V4 = 0)) %>% # 按team分组计算加权和 group_by(V1, V2) %>% summarize( V1.y = first(na.omit(V1)), V3 = first(na.omit(V3)), V4 = sum(coeff * V4), .groups = "drop" ) %>% rename(V1.x = V1) # 验证结果 all.equal(result, desired_output) # 返回TRUE
方法2:动态执行表达式(简洁,适合可控场景)
将表达式中的姓名替换为对应数值,通过eval(parse)直接计算,代码更简洁,但注意仅在数据源可控时使用(避免恶意代码风险):
# 把input转换为姓名-数值的命名向量 value_vec <- deframe(input %>% select(V2, V4)) result2 <- teams %>% rowwise() %>% mutate( # 替换表达式中的姓名为数值 expr_str = str_replace_all(V2, names(value_vec), as.character(value_vec)), V4 = eval(parse(text = expr_str)), # 补充分组信息(假设input中V1/V3统一) V1.y = input$V1[1], V3 = input$V3[1] ) %>% ungroup() %>% rename(V1.x = V1) %>% select(V1.x, V1.y, V2, V3, V4) # 验证结果 all.equal(result2, desired_output) # 返回TRUE
方法对比
- 方法1:安全可靠,支持input中存在多Team、多城市的复杂场景,无代码注入风险。
- 方法2:代码简洁,执行快,但仅适用于数据源完全可控的场景,且默认input的V1/V3为统一值,若需分组需额外处理。
内容的提问来源于stack exchange,提问作者nicshah
相关产品推荐
相关产品推荐

