如何用R函数或循环批量统计各列中超出指定组范围的样本数
批量统计分组数据中各列的异常值数量
问题根源
你遇到的错误是因为函数中直接使用column参数时,dplyr无法识别它对应数据框的列,加上调用函数时传入的是裸变量(如x),没有做非标准求值处理,导致column被当成整个列向量,和分组后的向量长度不匹配,从而触发长度错误。
修复后的单列处理函数
使用rlang的非标准求值语法({{}}和!!)解决列参数识别问题,同时让结果列名自动匹配传入的列:
library(dplyr) count_outliers <- function(data, column, min_val, max_val) { # 获取传入列的名称 col_name <- rlang::as_name(rlang::enquo(column)) data %>% filter(!is.na({{ column }})) %>% group_by(change) %>% mutate( lower_bound = min_val, upper_bound = max_val, is_pos_outlier = {{ column }} > upper_bound, is_neg_outlier = {{ column }} < lower_bound ) %>% summarise( # 动态生成列名 !!paste0(col_name, "_pos_outliers") := sum(is_pos_outlier), !!paste0(col_name, "_neg_outliers") := sum(is_neg_outlier), .groups = "drop" # 取消分组,避免后续操作警告 ) }
调用示例
先优化normal组极值的计算(无需多余分组):
normal <- c('normal1', 'normal2', 'normal3', 'normal4') minmax_normal <- data %>% filter(change %in% normal) %>% summarise(across(c(x, y, z), list(min = min, max = max), na.rm = TRUE)) # 处理x列 x_outliers <- count_outliers(data, x, minmax_normal$x_min, minmax_normal$x_max)
批量处理所有列
如果要一次性处理所有数值列,推荐两种高效方案:
方法1:用purrr批量遍历列
library(purrr) # 指定需要处理的列(排除sample和change) target_cols <- setdiff(names(data), c("sample", "change")) # 批量生成所有列的异常值统计 all_outliers <- map_dfr(target_cols, function(col) { # 获取当前列的normal组极值 min_val <- minmax_normal[[paste0(col, "_min")]] max_val <- minmax_normal[[paste0(col, "_max")]] # 调用函数,用!!sym()将字符串转成列对象 count_outliers(data, !!sym(col), min_val, max_val) })
方法2:一步到位的dplyr流程(无需单独函数)
通过数据格式转换,直接完成批量统计:
all_outliers <- data %>% # 提取normal组的各列极值,转成窄表方便合并 filter(change %in% normal) %>% summarise(across(c(x, y, z), list(min = min, max = max), na.rm = TRUE)) %>% pivot_longer( everything(), names_to = c("column", ".value"), names_pattern = "(.*)_(min|max)" ) %>% # 将极值合并到原数据的每一行 right_join(data, by = character()) %>% # 过滤掉各列的NA值 filter(across(c(x, y, z), ~!is.na(.x))) %>% # 按分组和列名统计异常值 group_by(change, column) %>% summarise( pos_outliers = sum(.data[[column]] > max, na.rm = TRUE), neg_outliers = sum(.data[[column]] < min, na.rm = TRUE), .groups = "drop" ) %>% # 转成宽表格式(可选,更易读) pivot_wider( names_from = column, values_from = c(pos_outliers, neg_outliers), names_glue = "{column}_{.value}" )
内容的提问来源于stack exchange,提问作者Mitchel
相关产品推荐
相关产品推荐

