ggplot2聚合总收益后如何按不同类型列填充直方图?
适配“个体年度总收益”统计逻辑的分色直方图实现方案
核心问题本质
宽表场景下无法直接给geom_histogram()加颜色填充,核心原因是ggplot的fill审美映射要求绑定一维离散分类变量,你当前宽表中的三个独立哑变量(typeA/typeB/typeC)是三个平行的0/1列,不满足单维度分类的映射要求,需要先基于研究目标的分类规则,给每一行“个体-年度”记录匹配对应的收入类型标签,再绑定到fill参数即可。
分类规则前置(绘图的核心前提)
分色逻辑必须先匹配研究目标,不能直接硬编码分类,两种常用的符合统计逻辑的规则如下:
- 规则1(匹配“按收入类型分区展示总收益分布”的核心需求):定义每个“个体-年度”记录的主导收入类型作为分类维度,即取该个体当年三类收入中总金额占比最高的类型,作为该条总收益记录的填充色分类。该规则下的直方图计数逻辑为:每个收益区间的总计数是区间内所有个体-年度样本数,区间内不同颜色的分层对应该区间内不同主导收入类型的样本占比,完全避免长表单笔收入计入统计的偏差。
- 规则2(匹配“不同收入类型对应的总收益覆盖范围”的展示需求):将当年存在对应类型收入的个体-年度记录,全部纳入对应类型的直方图统计。该规则下因为个体可能同时有多种收入类型,不同类型的直方图会存在重叠,适合用半透明效果展示分布差异。
可直接复用的代码实现
基于规则1(主导收入类型)的堆叠分色直方图
首先从原始长表data直接聚合得到带分类标签的数据集(避免只存笔数的聚合偏差,推荐直接从原始长表重新计算,不要基于已有宽表的哑变量列硬推):
library(dplyr) library(ggplot2) library(tidyr) agg_df <- data %>% # 按个体+年度聚合 group_by(ID, year) %>% summarise( income_sum = sum(income), # 同步计算每类收入的年度总金额 typeA_amt = sum(income[type == "A"]), typeB_amt = sum(income[type == "B"]), typeC_amt = sum(income[type == "C"]), .groups = "drop" ) %>% rowwise() %>% # 计算主导收入类型 mutate( dominant_type = case_when( which.max(c_across(c(typeA_amt, typeB_amt, typeC_amt))) == 1 ~ "A", which.max(c_across(c(typeA_amt, typeB_amt, typeC_amt))) == 2 ~ "B", TRUE ~ "C" ) )
完成数据处理后,直接将分类列绑定到fill参数绘图,即可得到符合统计逻辑的分色直方图:
h <- ggplot(agg_df, aes(x = income_sum, fill = dominant_type)) + # 调整position为"stack"实现堆叠分色,如需并列分色可改为"dodge" geom_histogram(binwidth = 你设定的组距, position = "stack", alpha = 0.8) + labs(x = "个人年度总收益", fill = "主导收入类型")
基于规则2(有该类收入即纳入)的重叠分色直方图
如果需要展示只要有某类收入的个体总收益分布,可将聚合后的宽表转成长格式筛选后绘图:
long_plot_df <- agg_df %>% # 将三个类型的金额列转成长格式 pivot_longer( cols = c(typeA_amt, typeB_amt, typeC_amt), names_to = "income_type", values_to = "type_amt" ) %>% # 仅保留当年有对应类型收入的记录 filter(type_amt > 0) # 绘图 h <- ggplot(long_plot_df, aes(x = income_sum, fill = income_type)) + # position设为"identity"实现重叠效果,alpha调整透明度避免色块遮挡 geom_histogram(binwidth = 你设定的组距, position = "identity", alpha = 0.5) + labs(x = "个人年度总收益", fill = "收入类型")
常见避坑提醒
- 不要直接基于宽表的三个0/1哑变量映射颜色:平行的多列哑变量无法直接作为单维度分类值传入fill参数,强行转换会导致分类逻辑错乱。
- 不要用收入笔数判断主导类型:同样是2笔A类、1笔C类收入,很可能C类总金额远高于A类,仅靠笔数计算的主导类型存在统计偏差,优先按金额占比计算分类。
- 存在边界情况(三类收入金额完全相等)时,可在
case_when中新增判断逻辑,将这类样本归为“混合收入类型”,避免分类遗漏。
内容的提问来源于stack exchange,提问作者pragiedruliai
相关产品推荐
相关产品推荐

