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

在R的survey包中处理单元格计数为零的问题

解决survey包中svyby处理含零单元格列联表的报错问题

问题背景

我正在学习R语言的survey包,需为58×4的列联表计算单元格比例(行百分比)和置信区间,同时考虑复杂抽样设计的方差估计。行对应加州58个县,列对应四类“性取向”变量。部分小县存在零单元格计数,使用svyby方法时触发错误:

data length is not a sub-multiple or multiple of the number of rows

可复现代码

# 设置试验次数和各结果概率
set.seed(16)
probs <- c(.94, .03, .02, .01)
n <- 10000

# 构造含零单元格的数据集
sogi <- data.frame(t(rmultinom(n, 1, prob = probs)))
names(sogi) <- c("straight","gay","bisexual","other")
df_sogi <- sogi %>% 
  mutate(sexual_orientation = 
           as.factor(case_when(
             straight == 1 ~ 0,
             gay == 1 ~ 1,
             bisexual == 1 ~ 2,
             other == 1 ~ 3))) %>%
  mutate(pw = runif(n, 0.2, 3.5)) %>%
  mutate(county = as.factor(rep(LETTERS[1:10], each = n/length(LETTERS[1:10])))) %>%
  mutate(ind = row_number()) %>%
  select(ind, county, sexual_orientation, pw) 

# 查看原始计数
table(df_sogi$county, df_sogi$sexual_orientation)

# 创建调查设计对象
df_svy_z1 <- svydesign(id=~ind, data = df_sogi, weights = ~pw)

# 估计单元格均值
svyby(formula = ~county, by = ~sexual_orientation, design = df_svy_z1, FUN = svymean)


# 修改数据集,构造零单元格
df_sogi$sexual_orientation <- ifelse(df_sogi$county %in% c("G","I") & df_sogi$sexual_orientation==3, 
                                     0, df_sogi$sexual_orientation)

# 查看修改后的计数
table(df_sogi$county, df_sogi$sexual_orientation)

df_svy_z2 <- svydesign(id=~ind, data = df_sogi, weights = ~pw)
# 运行此代码会触发报错
svyby(~factor(county), ~factor(sexual_orientation), df_svy_z2, svymean)

问题原因

当部分县在某类性取向中无观测值(零单元格)时,factor(sexual_orientation)在不同县的子集中会丢失对应水平,导致svyby合并结果时出现维度不匹配,触发报错。

解决方案

方案1:强制保留因子的完整水平

修改数据后,重新指定sexual_orientation的所有可能水平,确保每个子集的因子维度一致:

# 修改数据后重置因子水平,保留全部4类性取向
df_sogi$sexual_orientation <- factor(df_sogi$sexual_orientation, levels = c(0,1,2,3))

# 重新创建调查设计并运行svyby
df_svy_z2 <- svydesign(id=~ind, data = df_sogi, weights = ~pw)
svyby(~county, ~sexual_orientation, df_svy_z2, svymean)

方案2:直接计算行百分比与置信区间

若需直接得到行百分比,可结合svytable和svyciprop实现:

# 生成加权列联表
svytab <- svytable(~county + sexual_orientation, df_svy_z2)
# 计算行百分比(按县分组)
row_percent <- prop.table(svytab, margin = 1) * 100

# 批量计算每个单元格的置信区间
library(purrr)
library(tidyr)

# 生成所有县和性取向的组合
all_combinations <- expand.grid(
  county = levels(df_sogi$county),
  sexual_orientation = levels(df_sogi$sexual_orientation),
  stringsAsFactors = FALSE
)

# 计算每个组合的置信区间
ci_results <- all_combinations %>%
  mutate(
    ci = pmap(list(county, sexual_orientation), function(c, so) {
      # 构造子集设计
      subset_design <- subset(df_svy_z2, county == c)
      # 若子集无数据,返回NA
      if (nrow(subset_design$variables) == 0) {
        return(tibble(lower = NA, upper = NA))
      }
      # 计算比例的置信区间(logit方法更适合极端比例)
      prop_est <- svyciprop(~I(sexual_orientation == so), subset_design, method = "logit")
      tibble(
        lower = confint(prop_est)[1] * 100,
        upper = confint(prop_est)[2] * 100
      )
    })
  ) %>%
  unnest(ci)

# 合并百分比与置信区间结果
final_results <- cbind(as.data.frame(row_percent), ci_results[,3:4])
colnames(final_results) <- c("row_percent", "ci_lower", "ci_upper")

内容的提问来源于stack exchange,提问作者David Crow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:33:20