You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

关键修改点

  1. 分组计算优先:对每个自变量先执行group_by + summarise(weighted.mean(...)),确保权重和对应因变量值正确匹配。
  2. 转宽表后置:计算完分组均值后再用pivot_wider整理格式,避免values_fn的局限性。
  3. 简化批量逻辑:multi函数直接遍历因变量列表,调用修改后的cross_fun,逻辑更清晰。

内容的提问来源于stack exchange,提问作者Benjamin Fretwurst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 03:45:48