基于多列条件计算数据框列值的均值与标准差(R语言)
解决方案:可扩展的条件统计计算
针对你的需求,我提供一个基于tidyverse的可扩展方案,它能轻松适配新增的条件列或价格列,同时处理"-"表示忽略条件的逻辑。
步骤1:加载依赖包并定义关键变量
首先我们加载tidyverse工具集,然后明确哪些列是条件列(用于筛选),哪些是价格列(用于计算均值和标准差)——这两个变量是实现扩展性的核心,后续新增列只需修改这里即可。
library(tidyverse) # 定义条件列和价格列,可根据需求扩展 condition_cols <- c("Colour", "Type", "Size") price_cols <- c("PriceOne", "PriceTwo")
步骤2:编写可复用的统计计算函数
我们写一个函数,输入一行条件(来自my.df2),输出对应的价格列统计值。函数会自动跳过值为"-"的条件列,处理无匹配结果的情况(返回NA)。
calculate_stats <- function(...) { # 获取当前行的条件参数 current_conditions <- tibble(...) %>% select(all_of(condition_cols)) # 逐步筛选my.df1:仅保留符合非"-"条件的行 filtered_df <- my.df1 for (col in condition_cols) { val <- current_conditions[[col]] if (val != "-") { filtered_df <- filtered_df %>% filter(.data[[col]] == val) } } # 处理无匹配结果的情况,否则计算均值和标准差 if (nrow(filtered_df) == 0) { stats <- set_names(rep(NA_real_, 2 * length(price_cols)), c(paste0(price_cols, "Mean"), paste0(price_cols, "StDev"))) } else { stats <- filtered_df %>% summarise(across(all_of(price_cols), list(Mean = mean, StDev = sd), .names = "{.col}{.fn}")) %>% unlist() } return(stats) }
步骤3:应用函数到my.df2的每一行
用pmap_dfr遍历my.df2的每一行条件,计算统计值并合并回原数据框:
my.df2_result <- my.df2 %>% mutate(pmap_dfr(select(., all_of(condition_cols)), calculate_stats))
验证结果(以第5行为例)
my.df2第5行的条件是Colour=Blue、Type=2、Size="-",即筛选my.df1中所有Colour=Blue且Type=2的行:
- PriceOne均值:(20+18+11+20+21+11)/6 ≈ 16.8333
- PriceOne标准差:≈4.717
- PriceTwo均值:(10+18+14+28+19+11)/6 ≈16.6667
- PriceTwo标准差:≈6.922
查看my.df2_result的第5行,会得到完全匹配的结果。
大数据集优化:data.table版本
如果你的数据集非常大,推荐使用data.table实现,效率更高:
library(data.table) setDT(my.df1) setDT(my.df2) calculate_stats_dt <- function(row) { # 构建筛选表达式 i_expr <- Reduce(function(x, y) { val <- row[[y]] if (val == "-") x else paste(x, y, "==", shQuote(val), sep = " & ") }, condition_cols, init = "") i_expr <- sub("^ & ", "", i_expr) # 筛选数据 filtered <- if (i_expr == "") my.df1 else my.df1[eval(parse(text = i_expr))] # 计算统计值 if (nrow(filtered) == 0) { stats <- as.list(set_names(rep(NA_real_, 2*length(price_cols)), c(paste0(price_cols, "Mean"), paste0(price_cols, "StDev")))) } else { stats <- filtered[, lapply(.SD, function(x) list(Mean = mean(x), StDev = sd(x))), .SDcols = price_cols] stats <- unlist(stats, recursive = FALSE) names(stats) <- paste0(rep(price_cols, each=2), c("Mean", "StDev")) } return(stats) } # 应用到每一行 my.df2_result_dt <- my.df2[, c(.SD, calculate_stats_dt(.SD)), by = seq_len(nrow(my.df2))] my.df2_result_dt[, seq_len := NULL]
扩展性说明
- 新增条件列:比如加
Brand列,只需把"Brand"添加到condition_cols向量 - 新增价格列:比如加
PriceThree列,只需把"PriceThree"添加到price_cols向量 - 函数会自动处理所有新增列,无需修改核心逻辑
内容的提问来源于stack exchange,提问作者Robin Nico
相关产品推荐
相关产品推荐

