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

如何基于点密度为分组条形图添加渐变着色(从每组最小值开始)

分组范围条形图的密度着色实现

需求说明

需要创建展示每组数据点范围的条形图,并用数据点的核密度(或其他密度方法)为条形着色,替代原示例中的离散点;要求密度着色从每组数据的最小值开始,而非从0起始。

原示例代码

原代码用线段展示每组数据的min-max范围,叠加抖动点展示数据分布:

library(dplyr)
library(tidyr)
library(ggplot2)

# 生成示例数据
set.seed(100)
df <- data.frame(a=rnorm(100, mean=5, sd=1), b=rnorm(100, mean=10, sd=2), c=rnorm(100, mean=15, sd=3)) %>%
  pivot_longer(everything(), names_to = "group", values_to = "value")

# 计算每组的min和max
df_minmax <- df %>% group_by(group) %>%
  summarize(min=min(value), max=max(value))

# 绘制基础图
ggplot() + 
  geom_segment(data=df_minmax, aes(x=group, y=min, yend=max), size=18, color='black') +
  geom_jitter(data=df, aes(x=group, y=value), color='white', width=0.1)

遇到的问题

参考示例中使用stat_ydensity时,存在两个问题:

  1. 自定义列名(如value而非ydat)时,报错找不到对应列;
  2. 参考示例的密度着色从0开始,不符合“从每组最小值开始”的需求。

解决方案一:修复stat_ydensity的使用

核心是正确映射变量,调整参数确保密度覆盖从min到max的范围:

library(dplyr)
library(tidyr)
library(ggplot2)

# 生成示例数据
set.seed(100)
df <- data.frame(a=rnorm(100, mean=5, sd=1), b=rnorm(100, mean=10, sd=2), c=rnorm(100, mean=15, sd=3)) %>%
  pivot_longer(everything(), names_to = "group", values_to = "value")

ggplot(df, aes(x = group, y = value, group = group)) +
  # 用stat_ydensity生成密度着色的矩形条
  stat_ydensity(
    aes(
      fill = after_stat(density),
      xmin = as.numeric(group) - 0.4,  # 控制条形宽度,适配字符型分组
      xmax = as.numeric(group) + 0.4,
      ymin = after_stat(y) - 0.05,     # 调整每个密度块的高度,根据数据缩放
      ymax = after_stat(y) + 0.05
    ),
    geom = "rect",
    trim = FALSE,  # 不截断密度曲线,确保覆盖数据全范围
    scale = 10     # 缩放密度值,让填充区域匹配数据的min-max范围
  ) +
  # 可选:添加每组的上下限线段,强化范围展示
  stat_summary(
    aes(y = value, yend = value),
    fun.min = min,
    fun.max = max,
    geom = "segment",
    size = 1,
    color = "black"
  ) +
  scale_fill_viridis_c(name = "密度") +
  theme_minimal()

关键说明

  • 用as.numeric(group)将字符型分组转换为数值,方便计算条形的左右边界;
  • after_stat(y)对应映射的y变量(即value),避免列名不匹配的错误;
  • trim=FALSE确保密度计算包含所有数据点,不会截断在分布的两端;
  • scale参数调整密度的缩放比例,让填充区域刚好覆盖每组的min-max范围。

解决方案二:预先计算核密度再绘图

手动计算每组的核密度,完全控制密度范围从min到max,灵活性更高:

library(dplyr)
library(tidyr)
library(ggplot2)

# 生成示例数据
set.seed(100)
df <- data.frame(a=rnorm(100, mean=5, sd=1), b=rnorm(100, mean=10, sd=2), c=rnorm(100, mean=15, sd=3)) %>%
  pivot_longer(everything(), names_to = "group", values_to = "value")

# 预先计算每组的核密度(从min到max)
df_density <- df %>%
  group_by(group) %>%
  group_modify(~{
    # 自定义密度计算范围:从组内最小值到最大值
    dens <- density(.x$value, from = min(.x$value), to = max(.x$value), adjust = 1)
    tibble(y = dens$x, density = dens$y)
  }) %>%
  ungroup()

# 为分组分配数值位置,方便绘制条形
group_positions <- df %>%
  distinct(group) %>%
  mutate(x_num = row_number())

# 合并密度数据与分组位置
df_density <- df_density %>%
  left_join(group_positions, by = "group")

# 绘制图形
ggplot() +
  # 用geom_rect绘制密度填充的条形
  geom_rect(
    data = df_density,
    aes(
      xmin = x_num - 0.4,
      xmax = x_num + 0.4,
      ymin = y,
      ymax = lead(y),  # 用lead(y)生成连续的矩形块
      fill = density
    ),
    color = NA  # 去掉矩形边框,让填充更平滑
  ) +
  # 添加每组的上下限线段
  geom_segment(
    data = df %>% 
      group_by(group) %>% 
      summarize(min=min(value), max=max(value)) %>% 
      left_join(group_positions, by="group"),
    aes(x = x_num, xend = x_num, y = min, yend = max),
    size = 1,
    color = "black"
  ) +
  # 替换x轴刻度为分组名称
  scale_x_continuous(breaks = group_positions$x_num, labels = group_positions$group) +
  scale_fill_viridis_c(name = "密度") +
  theme_minimal()

关键说明

  • 手动指定density()的from和to参数,确保密度计算严格从组内最小值到最大值;
  • 可通过adjust参数调整核密度的平滑度,适配不同的数据分布;
  • 完全控制条形的宽度、填充范围,避免stat类函数的参数适配问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:07:09