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

如何在ggplot2图例下方(图表外)添加汇总统计信息?

将ggplot2直方图的统计信息移至图例下方(外部)

问题背景

现有模拟数据及直方图生成代码,当前代码将均值、最大值、最小值标注在图表右上角,但添加标准差等更多统计指标时会导致图表拥挤,需要将汇总统计信息(均值、最大值、最小值、标准差等)放置在图例下方的图表外部区域。

模拟数据

library(fitdistrplus)
values <- sample(20:50, 100, replace = TRUE)
df <- data.frame(values)

原直方图代码

create_histogram <- function(data_set, column_name, bins_number) {
  colname     <- as_label(enquo(column_name))
  
  # Calculate summary statistics
  summary_stats <- data.frame(
    mean = mean(data_set[[colname]]),
    max = max(data_set[[colname]]),
    min = min(data_set[[colname]])
  )
  
  ggplot(data_set, aes(x = {{ column_name }})) +
    geom_histogram(aes(y = after_stat(density)), bins = bins_number, fill = "lightblue", colour = "black") +
    stat_function(fun = dnorm , args = list(mean = mean(data_set[[colname]]), sd = sd(data_set[[colname]])),
                  mapping = aes(colour = "Normal")) +
    scale_colour_manual("Distribution", values = c("red")) +
    annotate("text", x = Inf, y = Inf, hjust = 1.1, vjust = 1.1,
             label = paste("Mean:", round(summary_stats$mean, 2), "\nMax:", round(summary_stats$max, 2), "\nMin:", round(summary_stats$min, 2)))
}

create_histogram(df, values, 15)

解决方案

以下提供三种可行的实现方式,均能将统计信息放置在图例下方的外部区域:

方法一:调整边距+直接标注文本

通过扩大图表右侧边距,将统计文本标注在图例下方的外部位置,无需额外包:

library(ggplot2)
library(rlang)

create_histogram <- function(data_set, column_name, bins_number) {
  colname     <- as_label(enquo(column_name))
  
  # 计算包含标准差的统计量
  summary_stats <- data.frame(
    mean = mean(data_set[[colname]]),
    max = max(data_set[[colname]]),
    min = min(data_set[[colname]]),
    sd = sd(data_set[[colname]])
  )
  
  # 拼接统计文本
  stats_text <- paste0(
    "Mean: ", round(summary_stats$mean, 2), "\n",
    "Max: ", round(summary_stats$max, 2), "\n",
    "Min: ", round(summary_stats$min, 2), "\n",
    "SD: ", round(summary_stats$sd, 2)
  )
  
  ggplot(data_set, aes(x = {{ column_name }})) +
    geom_histogram(aes(y = after_stat(density)), bins = bins_number, fill = "lightblue", colour = "black") +
    stat_function(fun = dnorm, args = list(mean = summary_stats$mean, sd = summary_stats$sd),
                  mapping = aes(colour = "Normal")) +
    scale_colour_manual("Distribution", values = c("red")) +
    # 扩大右侧边距,为统计文本预留空间
    theme(plot.margin = margin(5.5, 80, 5.5, 5.5)) +
    # 将文本标注在图例下方(x=Inf对应最右侧,y=-0.01对应图表底部偏上位置,可根据需求调整)
    annotate("text", x = Inf, y = -0.01, hjust = 1, vjust = 1, label = stats_text, size = 3.5)
}

# 调用函数
create_histogram(df, values, 15)

方法二:使用grid包的文本框精准控制位置

利用grid包的textGrob结合annotation_custom,不受数据坐标轴范围影响,更精准控制文本位置:

library(ggplot2)
library(rlang)
library(grid)

create_histogram <- function(data_set, column_name, bins_number) {
  colname     <- as_label(enquo(column_name))
  
  # 计算包含标准差的统计量
  summary_stats <- data.frame(
    mean = mean(data_set[[colname]]),
    max = max(data_set[[colname]]),
    min = min(data_set[[colname]]),
    sd = sd(data_set[[colname]])
  )
  
  # 拼接统计文本
  stats_text <- paste0(
    "Mean: ", round(summary_stats$mean, 2), "\n",
    "Max: ", round(summary_stats$max, 2), "\n",
    "Min: ", round(summary_stats$min, 2), "\n",
    "SD: ", round(summary_stats$sd, 2)
  )
  
  # 创建文本框对象
  stats_grob <- textGrob(
    label = stats_text,
    x = unit(1, "npc"), y = unit(0, "npc"), # 锚定在右上角底部
    hjust = 1, vjust = 0,
    gp = gpar(fontsize = 10)
  )
  
  ggplot(data_set, aes(x = {{ column_name }})) +
    geom_histogram(aes(y = after_stat(density)), bins = bins_number, fill = "lightblue", colour = "black") +
    stat_function(fun = dnorm, args = list(mean = summary_stats$mean, sd = summary_stats$sd),
                  mapping = aes(colour = "Normal")) +
    scale_colour_manual("Distribution", values = c("red")) +
    # 扩大右侧边距
    theme(plot.margin = margin(5.5, 100, 5.5, 5.5)) +
    # 添加文本框到图表外部
    annotation_custom(stats_grob, xmin = Inf, xmax = Inf, ymin = -Inf, ymax = Inf)
}

# 调用函数
create_histogram(df, values, 15)

方法三:使用patchwork拼接图表与文本

通过patchwork包将主直方图和统计文本区域拼接,布局更灵活:

library(ggplot2)
library(rlang)
library(patchwork)

create_histogram <- function(data_set, column_name, bins_number) {
  colname     <- as_label(enquo(column_name))
  
  # 计算包含标准差的统计量
  summary_stats <- data.frame(
    mean = mean(data_set[[colname]]),
    max = max(data_set[[colname]]),
    min = min(data_set[[colname]]),
    sd = sd(data_set[[colname]])
  )
  
  # 拼接统计文本
  stats_text <- paste0(
    "Mean: ", round(summary_stats$mean, 2), "\n",
    "Max: ", round(summary_stats$max, 2), "\n",
    "Min: ", round(summary_stats$min, 2), "\n",
    "SD: ", round(summary_stats$sd, 2)
  )
  
  # 生成主直方图
  main_plot <- ggplot(data_set, aes(x = {{ column_name }})) +
    geom_histogram(aes(y = after_stat(density)), bins = bins_number, fill = "lightblue", colour = "black") +
    stat_function(fun = dnorm, args = list(mean = summary_stats$mean, sd = summary_stats$sd),
                  mapping = aes(colour = "Normal")) +
    scale_colour_manual("Distribution", values = c("red"))
  
  # 生成统计文本的空白图表
  stats_plot <- ggplot() +
    annotate("text", x = 0, y = 0, label = stats_text, size = 3.5, hjust = 1) +
    theme_void() +
    theme(plot.margin = margin(0, 0, 0, 20)) # 调整左侧边距对齐图例
  
  # 拼接两个图表,主图占4份宽度,文本区域占1份
  main_plot + stats_plot + plot_layout(widths = c(4, 1))
}

# 调用函数
create_histogram(df, values, 15)

内容的提问来源于stack exchange,提问作者Joe the Second

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:34:55