如何在R的ggplot2中绘制变量的平滑核求和图?
嘿,我完全get到你的需求了——你想要的是对变量b的平滑窗口求和曲线:既有stat_bin那种按区间汇总b的核心逻辑,又能像geom_density一样拥有平滑的曲线,而不是loess那种拟合单个b值的趋势对吧?下面给你几个ggplot2的解决方案,都是直接能用的:
方案1:用stat_summary_bin一步完成(最简洁)
这个方法直接在ggplot里完成「按bin求和b+平滑拟合」的操作,不需要额外预处理数据:
library(ggplot2) set.seed(123) # 加随机种子让结果可重复 mydf = data.frame(a = sample(1:100, 100, replace = T)) mydf$b = abs(sin((mydf$a)/50*2*pi) + rnorm(100, sd = 0.1)) ggplot(mydf, aes(x = a)) + # 保留你原来的柱状图和stat_bin虚线作为参考 geom_bar(aes(weight = b), alpha = 0.2) + stat_bin(geom = "line", position = "identity", binwidth = 4, aes(weight = b), lty = 2, color = "gray50") + # 核心:按bin求和b,再用loess平滑 stat_summary_bin( fun = sum, # 对每个bin的b求和 binwidth = 4, # 和stat_bin保持一致的bin宽度 geom = "smooth", # 用平滑曲线展示 method = "loess", # 平滑方法,也可以换成"gam"等 span = 0.5, # 平滑程度,数值越小越贴近原始bin求和值 color = "darkblue", size = 1 )
方案2:手动预处理数据再平滑(更灵活)
如果需要对bin的求和结果做更多自定义操作,可以先手动计算每个bin的b总和,再对这些值做平滑:
library(ggplot2) library(dplyr) set.seed(123) mydf = data.frame(a = sample(1:100, 100, replace = T)) mydf$b = abs(sin((mydf$a)/50*2*pi) + rnorm(100, sd = 0.1)) # 第一步:计算每个bin的b总和,以及bin的中点x值 bin_sums <- mydf %>% mutate(a_bin = cut(a, breaks = seq(0, 100, 4), include.lowest = TRUE)) %>% group_by(a_bin) %>% summarize(b_sum = sum(b), a_mid = mean(a)) # 用bin中点作为平滑的x轴值 # 第二步:画图 ggplot(mydf, aes(x = a)) + geom_bar(aes(weight = b), alpha = 0.2) + stat_bin(geom = "line", binwidth = 4, aes(weight = b), lty = 2, color = "gray50") + # 对预处理好的bin求和值做平滑 geom_smooth(data = bin_sums, aes(x = a_mid, y = b_sum), method = "loess", span = 0.5, color = "darkred", size = 1)
为什么你之前的尝试没达到预期?
geom_density(aes(y = b))逻辑不对:密度图的y轴默认是密度值(和计数/权重的分布相关),直接把y设为b会混淆原始值和密度统计量,自然得不到求和的结果。如果硬要基于密度调整,可以用after_stat(density) * sum(mydf$b) * 4(总b值×bin宽度)把密度值转换成近似的bin求和,但不如上面的方法准确。geom_smooth(aes(y = b))是拟合a和单个b值的关系,不是对b的区间求和后再平滑,所以和你要的“窗口求和”逻辑完全不同。
内容的提问来源于stack exchange,提问作者Alexander Shenkin
相关产品推荐
相关产品推荐

