如何在ggplot中合并两张normal probability plot并添加图例?
合并两张正态分布概率图(共用X轴+添加图例)的ggplot实现方案
问题描述
我有如下两张正态概率图的代码:
library(ggplot2) # Plot 1 ggplot(data.frame(x = c(-4, 4)), aes(x)) + stat_function(fun = dnorm, args = list(mean = 0, sd = 1), col='red') + stat_function(fill='red', fun = dnorm, xlim = c(-4, -1), geom = "area") + stat_function(fill='red', fun = dnorm, xlim = c(-1, 4), geom = "area", alpha = 0.3) # Plot 2 ggplot(data.frame(x = c(-4, 4)), aes(x)) + stat_function(fun = dnorm, args = list(mean = 0, sd = 2), col='blue') + stat_function(fill='blue', fun = dnorm, args = list(mean = 0, sd = 2), xlim = c(-4, -1), geom = "area") + stat_function(fill='blue', fun = dnorm, args = list(mean = 0, sd = 2), xlim = c(-1, 4), geom = "area", alpha = 0.3)
单独展示这两张图正常,但我希望将它们合并到同一绘图窗口,共用x轴,同时基于填充颜色添加图例来区分两者。请问能否用ggplot实现?盼相关指引。
实现方案
可以用ggplot实现,以下提供两种可靠的实现方式:
方式一:基于stat_function循环扩展
核心思路是通过分组变量映射颜色/填充属性,利用循环批量生成曲线和填充区域,自动关联图例:
library(ggplot2) # 定义分布参数:均值、标准差、分组名称、对应颜色 dist_params <- data.frame( mean = c(0, 0), sd = c(1, 2), group = c("SD=1", "SD=2"), color = c("red", "blue") ) # 创建平滑的x轴数据 x_range <- data.frame(x = seq(-4, 4, length.out = 1000)) ggplot(x_range, aes(x)) + # 绘制两条正态分布曲线 lapply(1:nrow(dist_params), function(i) { stat_function( fun = dnorm, args = list(mean = dist_params$mean[i], sd = dist_params$sd[i]), aes(color = dist_params$group[i]), linewidth = 1 ) }) + # 绘制左侧填充区域(x ≤ -1) lapply(1:nrow(dist_params), function(i) { stat_function( fun = dnorm, args = list(mean = dist_params$mean[i], sd = dist_params$sd[i]), aes(fill = dist_params$group[i]), xlim = c(-4, -1), geom = "area", alpha = 0.8 ) }) + # 绘制右侧填充区域(x ≥ -1) lapply(1:nrow(dist_params), function(i) { stat_function( fun = dnorm, args = list(mean = dist_params$mean[i], sd = dist_params$sd[i]), aes(fill = dist_params$group[i]), xlim = c(-1, 4), geom = "area", alpha = 0.3 ) }) + # 绑定自定义颜色映射 scale_color_manual(values = setNames(dist_params$color, dist_params$group)) + scale_fill_manual(values = setNames(dist_params$color, dist_params$group)) + # 合并颜色和填充的图例,统一标题 guides( color = guide_legend(title = "正态分布"), fill = guide_legend(title = "正态分布") ) + # 设置图表标签 labs( x = "X值", y = "密度", title = "合并后的正态分布概率图" ) + theme_minimal()
方式二:预计算数据后用geom_ribbon绘制
先计算所有分布的密度值,再用geom_line和geom_ribbon分别绘制曲线和填充区域,代码结构更直观:
library(ggplot2) library(dplyr) # 生成包含所有分布数据的数据集 x_vals <- seq(-4, 4, length.out = 1000) plot_data <- expand.grid(x = x_vals, group = c("SD=1", "SD=2")) %>% mutate( mean = 0, sd = ifelse(group == "SD=1", 1, 2), density = dnorm(x, mean, sd), # 区分左右区域的密度值(用于填充) density_left = ifelse(x <= -1, density, 0), density_right = ifelse(x >= -1, density, 0) ) ggplot(plot_data, aes(x)) + # 绘制正态分布曲线 geom_line(aes(y = density, color = group), linewidth = 1) + # 绘制左侧填充区域 geom_ribbon(aes(ymin = 0, ymax = density_left, fill = group), alpha = 0.8) + # 绘制右侧填充区域 geom_ribbon(aes(ymin = 0, ymax = density_right, fill = group), alpha = 0.3) + # 自定义颜色映射 scale_color_manual(values = c("SD=1" = "red", "SD=2" = "blue")) + scale_fill_manual(values = c("SD=1" = "red", "SD=2" = "blue")) + # 合并图例并设置标题 guides(color = guide_legend(title = "正态分布"), fill = guide_legend(title = "正态分布")) + labs(x = "X值", y = "密度", title = "合并后的正态分布概率图") + theme_minimal()
关键说明
- 两种方式都通过
aes映射group变量实现图例自动生成,避免手动添加颜色导致的图例失效问题 - 自定义颜色映射确保曲线和填充色一致,提升图表可读性
- 使用高密度的x轴数据(
length.out=1000)保证曲线和填充区域的平滑度
内容的提问来源于stack exchange,提问作者Bogaso
相关产品推荐
相关产品推荐

