pivot_wider中weighted.mean作为values_fn时权重失效问题咨询
问题:分组加权均值计算失效(pivot_wider中weighted.mean权重被忽略)
我需要对一组因变量,按多组自变量分组计算加权均值。自己实现了cross_fun函数处理单个因变量,再通过multi函数批量处理多个因变量。代码能正常运行,但结果未应用权重——推测是在pivot_wider中使用weighted.mean作为values_fn时,权重参数未被正确传递。
原代码
library(tidyverse) library(rlang) library(scales) dv1 <- c(1, 2, 1, 2, 1, 2) # 因变量1 dv2 <- c(2, 1, 2, 1, 2, 1) # 因变量2 wt <- c(0.5, 5, 0.5, 5, 0.5, 5) # 权重变量 iv1 <- c("m", "f", "m", "f", "m", "f") # 自变量1 iv2 <- c("b", "b", "b", "a", "a", "a") # 自变量2 iv3 <- c("x", "y", "y", "x", "y", "y") # 自变量3 DATA <- dplyr::tibble(iv1, iv2, iv3, dv1, dv2, wt) %>% # 构建数据集 mutate(one = 1, # 用于计算总计 no_weight = 1) # 无权重时的权重列(全为1) IV_List = c('one', 'iv1', 'iv2', 'iv3') # 自变量列表 DV_List = c("dv1", "dv2") # 因变量列表 cross_fun <- function(.data, DV, IVs, weight, fun) { # 单个因变量按各自变量分组计算指定函数 List <- list() # 初始化列表 df <- .data %>% select(all_of({{ IVs }}), {{ DV }}, {{ weight }}) # 筛选所需变量 for (i in 1:(ncol(df) - 2)) { # 为每个自变量生成子数据集 List[[i]] <- df %>% select(all_of(i), {{ DV }}, {{ weight }}) %>% mutate(ORDER = 1) } dt <- purrr::map( # 转宽表并应用指定函数 .x = List, .f = ~ pivot_wider(.x, id_cols = "ORDER", names_from = 1, values_from = {{ DV }}, values_fn = {{ fun }}) ) %>% purrr::reduce(left_join, by = "ORDER") %>% # 合并结果 select(-any_of(c("ORDER"))) %>% rename(Total = 1) # 将第一列重命名为总计 return(dt) } # 测试单个因变量计算 DATA %>% cross_fun(dv1, IVs = IV_List, weight = wt, fun = ~weighted.mean(.x, weight = wt, na.rm = TRUE)) %>% mutate(across(where(is.numeric), ~scales::number(.x, accuracy = .1))) multi <- function(.data, DVs, IVs, weight, ...){ # 批量处理多个因变量 Answers <- .data %>% # 生成结果的第一列(因变量名称) select(all_of(DVs)) %>% colnames() %>% tibble() %>% select("Variable" = 1) dt <- .data %>% select(all_of(DVs), {{ weight }}, all_of({{ IV_List }})) %>% # 筛选所需列 map_dfr(all_of(DVs), cross_fun, .data = ., IVs = IV_List, weight = {{ weight }}, fun = ~weighted.mean(.x, weight = {{ weight }}, na.rm = TRUE)) %>% # 批量调用cross_fun cbind(Answers, .) %>% # 合并因变量名称列 mutate(across(where(is.numeric), ~scales::number(.x, accuracy = .1))) # 格式化数值 return(dt) } # 测试带权重的批量计算 DATA %>% multi(DVs = DV_List, IVs = IV_List, weight = wt) # 测试无权重的批量计算 DATA %>% multi(DVs = DV_List, IVs = IV_List, weight = no_weight) # 问题:pivot_wider中使用weighted.mean作为values_fn时,权重似乎被忽略
问题根源
pivot_wider的values_fn参数只能接收values_from指定列的向量,无法同时获取权重列的值。原代码中weighted.mean(.x, weight = wt)里的wt是全局环境中的权重向量,不是分组后的对应权重,导致加权计算完全失效,实际等同于普通均值。
解决方案
放弃在pivot_wider中使用values_fn,改为先分组计算加权均值,再转宽表。调整函数如下:
修改后的代码
library(tidyverse) library(rlang) library(scales) # 数据准备部分不变 dv1 <- c(1, 2, 1, 2, 1, 2) dv2 <- c(2, 1, 2, 1, 2, 1) wt <- c(0.5, 5, 0.5, 5, 0.5, 5) iv1 <- c("m", "f", "m", "f", "m", "f") iv2 <- c("b", "b", "b", "a", "a", "a") iv3 <- c("x", "y", "y", "x", "y", "y") DATA <- dplyr::tibble(iv1, iv2, iv3, dv1, dv2, wt) %>% mutate(one = 1, no_weight = 1) IV_List = c('one', 'iv1', 'iv2', 'iv3') DV_List = c("dv1", "dv2") # 修改后的cross_fun:先分组计算,再转宽表 cross_fun <- function(.data, DV, IVs, weight) { DV_sym <- enquo(DV) weight_sym <- enquo(weight) # 遍历每个自变量,计算分组加权均值并转宽表 map(IVs, function(iv) { .data %>% group_by(!!sym(iv)) %>% summarise(mean_val = weighted.mean(!!DV_sym, !!weight_sym, na.rm = TRUE)) %>% pivot_wider(names_from = !!sym(iv), values_from = mean_val) }) %>% reduce(left_join) %>% # 合并所有自变量的结果 rename(Total = one) # 将one对应的列重命名为Total } # 修改后的multi函数:适配新的cross_fun multi <- function(.data, DVs, IVs, weight) { weight_sym <- enquo(weight) # 批量处理每个因变量 map_dfr(DVs, function(dv) { .data %>% cross_fun(!!sym(dv), IVs, !!weight_sym) }) %>% mutate(Variable = DVs) %>% # 添加因变量名称列 relocate(Variable) %>% # 将名称列移到首位 mutate(across(where(is.numeric), ~scales::number(.x, accuracy = .1))) # 格式化数值 } # 测试带权重的计算 DATA %>% multi(DVs = DV_List, IVs = IV_List, weight = wt) # 测试无权重的计算 DATA %>% multi(DVs = DV_List, IVs = IV_List, weight = no_weight)
关键修改点
- 分组计算优先:对每个自变量先执行
group_by+summarise(weighted.mean(...)),确保权重和对应因变量值正确匹配。 - 转宽表后置:计算完分组均值后再用
pivot_wider整理格式,避免values_fn的局限性。 - 简化批量逻辑:
multi函数直接遍历因变量列表,调用修改后的cross_fun,逻辑更清晰。
内容的提问来源于stack exchange,提问作者Benjamin Fretwurst
相关产品推荐
相关产品推荐

