You need to enable JavaScript to run this app. 如何在R语言ggplot2中提取geom_smooth曲线的全部局部最大值?
优惠活动
大模型
产品
解决方案
定价
更多

如何在R语言ggplot2中提取geom_smooth曲线的全部局部最大值?

从平滑序列中提取全部局部最大值的实现

你已经通过复现geom_smooth()底层的gam/loess拟合,拿到了和绘图曲线完全对齐的等间隔平滑序列,这一步已经把问题从从渲染图层中提取曲线点简化为从一维等间隔数值序列中识别所有局部峰值,完全不需要依赖逐点暴力循环搜索。

核心实现逻辑

局部最大值的判定规则非常明确:在连续采样的平滑序列中,某一点的数值严格大于其左右相邻的两个采样点,即为局部峰值。基于这个规则用R的向量化运算实现,比嵌套循环的暴力搜索效率高几个量级:

# 基于已生成的smooth_y序列计算峰值位置
# 第一步:计算相邻点的一阶差分(当前点值-下一个点值)
first_diff <- diff(df$smooth_y)
# 第二步:找差分符号从正转负的拐点(即从上升趋势切换为下降趋势的位置)
peak_index <- which(diff(sign(first_diff)) == -2) + 1
# 提取峰值对应的x坐标、平滑后的y值
peak_result <- df[peak_index, c("x", "smooth_y")]

这套逻辑的计算速度和序列长度无关,哪怕是10万点的采样序列,计算耗时也在毫秒级。因为你当前的采样密度足够高(1000个点覆盖4π的x区间,点间隔约0.012),识别到的峰值位置和真实曲线峰值的误差可以忽略,完全满足周期计算的精度要求。

可选的毛刺过滤优化

如果拟合后的序列因为残差波动出现无意义的小峰值,可以加一个简单的阈值判断,过滤掉不属于目标信号的小波刺:

# 例:仅保留高于平滑序列全局均值的峰值,过滤微小波动
peak_result <- peak_result[peak_result$smooth_y > mean(df$smooth_y), ]

效果验证

将识别到的峰值直接叠加到原始可视化结果上,即可核对准确性:

df %>% ggplot() +
  geom_point(aes(x=x, y=y), alpha=0.3) +
  geom_smooth(aes(x=x, y=y), se=FALSE) +
  # 标记所有识别到的峰值位置
  geom_point(data=peak_result, aes(x=x, y=smooth_y), color="red", size=3) +
  geom_vline(xintercept = peak_result$x, color="red", linetype="dashed")

在你的示例数据中,因为是两个完整周期的正弦信号叠加噪声,最终会在x≈π/2、x≈5π/2的位置识别到两个有效峰值,直接计算相邻峰值的x值差,即可得到信号的平均周期。

注意事项
  • 不需要刻意从ggplot的渲染对象中回抽曲线数据:只要你复现的gam/loess拟合参数和geom_smooth()的默认配置对齐,得到的平滑序列就和绘图曲线100%匹配,可控性比回抽绘图数据更高。
  • 这套逻辑可以直接复用在谷值识别上:只要把判定条件改成“差分符号从负转正”,即可提取所有局部谷值位置,不需要重写核心逻辑。

内容的提问来源于stack exchange,提问作者Christopher Carroll Smith

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.11 10:45:09