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

如何用ggplot2按分组变量实现分面分色直方图(无外部依赖)

分面直方图按组内阈值分色(不依赖ggplot外部环境)

需求:创建分面直方图,每个分面基于自身组的自定义阈值(以中位数为例)将直方图柱子分为不同颜色,且不能引用ggplot()外部的环境变量。单直方图已实现该效果,但分面尝试未成功。


单直方图成功实现示例

以下代码可实现单直方图按中位数分色:

set.seed(123)

value = stats::rnorm(100, mean = 0, sd = 1)

df = data.frame(value)

df %>%
  {
    ggplot(data = ., aes(x = value, fill = ifelse(value > median(value), "0", "1"))) +
      geom_histogram(boundary = median(.$value), alpha = 0.5, position = "identity") +
      theme(legend.position = "none")
  }

单分色直方图示例


分面尝试失败案例

尝试为每个分面使用组内中位数阈值,但boundary参数全局计算,导致分面未按组内阈值拆分:

set.seed(456)

value = stats::rnorm(200, mean = 0, sd = 1)
group = c(rep(1,100), rep(2,100))
    
df = data.frame(value, group)

df %>%
  dplyr::mutate(value = ifelse(group == 2, value + 1, value)) %>%
  dplyr::group_by(group) %>%
  dplyr::mutate(above_median = value > median(value)) %>%
  {
    ggplot(data = ., aes(x = value, fill = above_median)) +
      facet_grid(rows = group) +
      geom_histogram(boundary = median(.$value), alpha = 0.5, position = "identity") +
      theme(legend.position = "none")
  }

失败的分面直方图示例


解决方案

核心问题是geom_histogram的boundary参数默认全局计算,未按组拆分。以下两种方法均可实现需求,且不依赖ggplot外部环境:

方法1:按组生成图层后组合

利用group_split拆分数据,为每个组单独生成直方图图层,再组合到分面图中:

set.seed(456)

value = stats::rnorm(200, mean = 0, sd = 1)
group = c(rep(1,100), rep(2,100))
    
df = data.frame(value, group) %>%
  dplyr::mutate(value = ifelse(group == 2, value + 1, value)) %>%
  dplyr::group_by(group) %>%
  dplyr::mutate(above_median = value > median(value))

# 按组生成直方图图层
layer_list <- df %>%
  dplyr::group_split() %>%
  purrr::map(function(sub_df) {
    group_median <- median(sub_df$value)
    geom_histogram(
      data = sub_df,
      aes(x = value, fill = above_median),
      boundary = group_median,
      alpha = 0.5,
      position = "identity"
    )
  })

# 组合图层并分面
ggplot() +
  layer_list +
  facet_grid(rows = vars(group)) +
  theme(legend.position = "none")

方法2:提前计算组内阈值,结合stat_bin

先为每个组计算阈值,再在stat_bin中绑定组内阈值作为boundary:

set.seed(456)

value = stats::rnorm(200, mean = 0, sd = 1)
group = c(rep(1,100), rep(2,100))
    
df = data.frame(value, group) %>%
  dplyr::mutate(value = ifelse(group == 2, value + 1, value)) %>%
  dplyr::group_by(group) %>%
  dplyr::mutate(group_median = median(value)) %>%
  ungroup()

ggplot(df, aes(x = value, fill = value > group_median, group = group)) +
  facet_grid(rows = vars(group)) +
  stat_bin(
    boundary = aes(x = group_median),
    alpha = 0.5,
    position = "identity"
  ) +
  theme(legend.position = "none")

两种方法均能让每个分面独立使用自身组的阈值,实现柱子分色的效果,且未引用ggplot()外部的环境变量。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 09:55:16