如何在循环中设置stat_function绘制双正态分布交互式直方图?
解决方案
1. 先模拟长格式数据集(适配你的场景)
先造一个和你需求匹配的长格式数据,包含分组变量和数值变量:
library(tidyverse) library(plotly) # 模拟长格式数据,两个分组各500条数据 set.seed(123) df_long <- tibble( group = rep(c("分组1", "分组2"), each = 500), value = c(rnorm(500, mean = 10, sd = 2), rnorm(500, mean = 12, sd = 2.5)) )
2. 提前计算各组统计量(均值/标准差)
先把每个分组的均值、标准差算好,避免后续重复计算:
group_stats <- df_long %>% group_by(group) %>% summarize( mean_val = mean(value), sd_val = sd(value), .groups = "drop" )
3. 两种实现方式任选
方式一:循环生成子图再拼接(适合个性化调整每个图)
循环遍历每个分组,单独生成带正态曲线和统计竖线的直方图,最后拼成交互式并行图:
# 空列表存每个分组的图 plot_list <- list() # 循环处理每个分组 for (i in seq_len(nrow(group_stats))) { current_group <- group_stats$group[i] current_mean <- group_stats$mean_val[i] current_sd <- group_stats$sd_val[i] # 生成单个分组的ggplot p <- df_long %>% filter(group == current_group) %>% ggplot(aes(x = value)) + geom_histogram(aes(y = ..density..), bins = 20, fill = "#619CFF", alpha = 0.7) + # 传入当前分组的均值/标准差画正态曲线 stat_function( fun = dnorm, args = list(mean = current_mean, sd = current_sd), color = "#F8766D", linewidth = 1.2 ) + # 画均值竖线 geom_vline(xintercept = current_mean, color = "#00BA38", linetype = "dashed", linewidth = 1) + labs(title = current_group, x = "数值", y = "密度") + theme_minimal() # 转成交互式图存入列表 plot_list[[i]] <- ggplotly(p) } # 水平拼接两个图,共享Y轴 subplot(plot_list[[1]], plot_list[[2]], nrows = 1, shareY = TRUE)
方式二:用分面+分组映射(无需循环,更简洁)
如果不需要单独调整每个图,用分面结合stat_function的分组映射更高效:
# 把统计量合并到原数据集,方便映射 df_long_with_stats <- df_long %>% left_join(group_stats, by = "group") # 绘制分面交互式直方图 p_facet <- df_long_with_stats %>% ggplot(aes(x = value, group = group)) + geom_histogram(aes(y = ..density..), bins = 20, fill = "#619CFF", alpha = 0.7) + # 自动提取每个分组的均值/标准差画正态曲线 stat_function( fun = dnorm, aes(color = group), args = function(data) list(mean = unique(data$mean_val), sd = unique(data$sd_val)), linewidth = 1.2 ) + # 分组画均值竖线 geom_vline(aes(xintercept = mean_val, color = group), linetype = "dashed", linewidth = 1) + facet_wrap(~group, nrow = 1) + scale_color_manual(values = c("分组1" = "#F8766D", "分组2" = "#00BA38")) + labs(x = "数值", y = "密度") + theme_minimal() + theme(legend.position = "none") # 转成交互式图 ggplotly(p_facet)
关键注意点
stat_function的args参数支持动态传值:循环时直接给当前分组的统计量,分面时通过函数提取每个分面的统计量即可。- 循环完全可行:适合需要对每个分组做特殊调整(比如不同配色、不同分箱数)的场景。
- 交互式部分用
ggplotly()直接转换ggplot对象,所有图层都会保留交互效果。
内容的提问来源于stack exchange,提问作者12666727b9
相关产品推荐
相关产品推荐

