如何在ggplot2的geom_smooth拟合曲线上高亮指定区间片段
实现geom_smooth全局拟合曲线指定区间高亮的方法
原有gghighlight方案不符合预期的核心原因:gghighlight会对geom_smooth层的输入数据做过滤,仅用筛选区间内的原始数据重新拟合曲线,而非基于全局拟合结果高亮对应片段,因此会出现高亮段和原曲线走势不一致的问题。
以下提供两种通用实现方案:
方案1:手动预计算全局拟合结果
可自主控制所有拟合参数,灵活性更高:
# 构造示例数据 Dates <- seq(as.POSIXct("2018-01-01 00:00:00"), as.POSIXct("2021-01-01 00:00:00"), by = "days") values <- rnorm(length(Dates), 1000, 500) df <- data.frame(Dates, values) library(ggplot2) # 全局拟合,和geom_smooth默认参数保持一致(默认n<1000时用loess拟合,span=0.75) global_fit <- loess(values ~ as.numeric(Dates), data = df, span = 0.75) # 生成所有日期对应的拟合值 df$fit <- predict(global_fit, newdata = df) # 定义高亮区间 highlight_start <- as.POSIXct("2020-03-24 00:00:00") highlight_end <- as.POSIXct("2020-04-14 00:00:00") ggplot(data = df, aes(Dates, values)) + scale_x_datetime(breaks = scales::breaks_width("years"), date_labels = "%Y") + geom_point(alpha = 0.3) + # 底层绘制全局拟合线 geom_line(aes(y = fit), color = "grey50", linewidth = 1) + # 叠加高亮区间的拟合线段 geom_line(aes(y = fit), data = ~subset(.x, Dates >= highlight_start & Dates <= highlight_end), color = "blue", linewidth = 1.5)
方案2:提取ggplot内置拟合数据
无需手动重复拟合,兼容geom_smooth所有拟合方法(lm/gam/loess等均可直接使用):
# 构造示例数据同上 Dates <- seq(as.POSIXct("2018-01-01 00:00:00"), as.POSIXct("2021-01-01 00:00:00"), by = "days") values <- rnorm(length(Dates), 1000, 500) df <- data.frame(Dates, values) library(ggplot2) # 先绘制基础图,包含原始散点和全局拟合线 p_base <- ggplot(data = df, aes(Dates, values)) + scale_x_datetime(breaks = scales::breaks_width("years"), date_labels = "%Y") + geom_point(alpha = 0.3) + geom_smooth(se = FALSE) # 可根据需求开启置信区间se=T # 从ggplot构建结果中提取全局拟合的坐标数据 smooth_data <- ggplot_build(p_base)$data[[2]] # 转换x轴为POSIXct格式,和原始数据时间维度匹配 smooth_data$x <- as.POSIXct(smooth_data$x, origin = "1970-01-01") # 定义高亮区间 highlight_start <- as.POSIXct("2020-03-24 00:00:00") highlight_end <- as.POSIXct("2020-04-14 00:00:00") # 叠加高亮线段层 p_base + geom_line(data = subset(smooth_data, x >= highlight_start & x <= highlight_end), aes(x = x, y = y), color = "blue", linewidth = 1.5, inherit.aes = FALSE)
如果需要同时高亮置信区间,只需对提取的拟合数据中的ymin/ymax字段做同样的区间筛选,再叠加geom_ribbon层即可。
内容的提问来源于stack exchange,提问作者Ahek
相关产品推荐
相关产品推荐

