如何在R中计算百分比的标准误与95%置信区间并绘图
问题描述
我先按项目状态分组,计算不同人口统计群体中高中毕业受访者的百分比,使用的代码如下:
d_perc <- d %>% group_by(group, levels, program_cat, highschool) %>% summarize(n = n()) %>% mutate(percent = n/sum(n)*100) %>% select(-n)
接下来想为这些百分比计算误差项,请问计算标准误(SE)及对应95%置信区间(CI)的最佳方法是什么?最终目标是用geom_point()和geom_errorbar()做可视化,绘图代码已准备好。
我尝试了以下代码,但运行后se列全为NaN:
d_perc$se <- sqrt(d_perc$percent*(1-d_perc$percent)/d_perc$percent)
原本计划通过±1.96*d_perc$se得到置信区间上下限。
附前100行数据:
d_perc <- structure(list(highschool= structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L), levels = c("no", "yes"), class = "factor"), program_cat = structure(c(2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 1L), levels = c("0", "1", "2"), class = "factor"), group = c("gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender", "race", "cohort", "gender"), levels = structure(c(1L, 3L, 7L, 2L, 5L, 7L, 1L, 3L, 6L, 2L, 4L, 6L, 1L, 5L, 7L, 1L, 3L, 7L, 1L, 3L, 6L, 1L, 3L, 6L, 1L, 3L, 7L, 1L, 5L, 6L, 2L, 5L, 7L, 1L, 5L, 6L, 1L, 3L, 6L, 2L, 3L, 7L, 1L, 3L, 6L, 1L, 4L, 6L, 1L, 5L, 6L, 1L, 5L, 6L, 1L, 4L, 6L, 2L, 3L, 6L, 2L, 3L, 7L, 1L, 3L, 7L, 1L, 3L, 6L, 1L, 4L, 7L, 1L, 4L, 7L, 1L, 3L, 7L, 1L, 3L, 7L, 1L, 4L, 7L, 1L, 3L, 7L, 1L, 3L, 6L, 1L, 3L, 7L, 2L, 3L, 7L, 2L, 5L, 6L, 2L), levels = c("Female", "Male", "Black", "Hispanic", "White", "CohortA", "CohortB"), class = "factor")), row.names = c(NA, -100L), class = c("tbl_df", "tbl", "data.frame"))
问题原因
你当前的标准误计算公式存在两个核心错误:
- 分母误用了百分比而非分组总样本量,当百分比为0时会触发除以0的情况,直接导致NaN;
- 直接用百分比(0-100)代入公式,而公式要求的是0-1区间的比例,数值逻辑完全错误。
正确解决步骤
1. 保留分组总样本量
修改初始的百分比计算代码,保留每组的总人数(这是计算误差的核心依据):
d_perc <- d %>% group_by(group, levels, program_cat) %>% mutate(total_n = n()) %>% # 计算每组总人数 group_by(group, levels, program_cat, highschool) %>% summarize(n = n(), total_n = first(total_n)) %>% # 保留总人数 mutate( p = n/total_n, # 先计算0-1区间的比例 percent = p*100 # 转换为百分比形式 )
2. 计算标准误与95%置信区间
常规正态近似法(适用于大样本,total_n ≥30)
用比例计算标准误,再转换为百分比对应的误差:
d_perc <- d_perc %>% mutate( se = sqrt(p*(1-p)/total_n), # 比例的标准误 se_percent = se*100, # 百分比的标准误 ci_low = percent - 1.96*se_percent, # 95% CI下限 ci_high = percent + 1.96*se_percent # 95% CI上限 )
威尔逊区间法(适用于小样本或极端比例)
如果存在小样本(total_n <30)或比例接近0%/100%的情况,推荐用威尔逊区间避免不合理的区间范围:
d_perc <- d_perc %>% mutate( ci_low_wilson = (p + (1.96^2)/(2*total_n) - 1.96*sqrt((p*(1-p) + (1.96^2)/(4*total_n))/total_n)) / (1 + (1.96^2)/total_n) * 100, ci_high_wilson = (p + (1.96^2)/(2*total_n) + 1.96*sqrt((p*(1-p) + (1.96^2)/(4*total_n))/total_n)) / (1 + (1.96^2)/total_n) * 100 )
3. 可视化适配
直接用计算好的字段做可视化即可:
ggplot(d_perc, aes(x = levels, y = percent, color = highschool)) + geom_point() + geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.2) + facet_wrap(~group + program_cat)
内容的提问来源于stack exchange,提问作者user9458954
相关产品推荐
相关产品推荐

