通过所有数据点绘制平滑曲线

35
我想绘制一条平滑的曲线,通过所有数据点并基于另一个变量具有一个梯度。理论上多项式插值可以完成任务,但我不确定如何在ggplot中实现。目前我想到的是这样的代码:

数据:

 dayofweek hour impressions conversions      cvr
         1    0     3997982       352.0 8.80e-05
         1    1     3182678       321.2 1.01e-04
         1    2     2921004       248.6 8.51e-05
         1    3     1708627       115.6 6.77e-05
         1    4     1225059        98.4 8.03e-05
         1    5     1211708        62.0 5.12e-05
         1    6     1653280       150.0 9.07e-05
         1    7     2511577       309.4 1.23e-04
         1    8     3801969       397.8 1.05e-04
         1    9     5144399       573.0 1.11e-04
         1   10     5770269       675.6 1.17e-04
         1   11     6936943       869.8 1.25e-04
         1   12     7953053       996.4 1.25e-04
         1   13     8711737      1117.8 1.28e-04
         1   14     9114872      1217.4 1.34e-04
         1   15     9257161      1155.2 1.25e-04
         1   16     8437068      1082.0 1.28e-04
         1   17     8688057      1047.2 1.21e-04
         1   18     9200450      1114.0 1.21e-04
         1   19     8494295      1086.8 1.28e-04
         1   20     9409142      1092.6 1.16e-04
         1   21    10500000      1266.8 1.21e-04
         1   22     9783073      1196.4 1.22e-04
         1   23     8225267       812.0 9.87e-05

R 代码:

ggplot(d) + 
  geom_line(aes(y=impressions, x=hour, color=cvr)) +
  stat_smooth(aes(y=impressions, x=hour), method = lm, formula = y ~ poly(x, 10), se = FALSE)

使用geom_line可以获得想要的渐变,但它不是平滑的。使用stat_smooth可以获得平滑的线条,但它不会穿过所有数据点,并且没有我想要的渐变。有什么想法可以实现这一点吗?

输入图像描述

1个回答

64

如果你想让插值曲线经过所有的点,按照你使用的多项式插值方法可能不是最佳选择。因为你有24个数据点,如果要求插值曲线穿过这些点,则需要一个23次多项式。但是我似乎不能使用23次的多项式,使用更低次数的多项式已经足够说明这种方法不可行:

ggplot(d) + 
  geom_point(aes(x = hour, y = impressions, colour = cvr), size = 3) +
  stat_smooth(aes(x = hour, y = impressions), method = "lm",
              formula = y ~ poly(x, 21), se = FALSE) +
  coord_cartesian(ylim = c(0, 1.5e7))

进入图像描述

这基本上涵盖了所有点(如果我尝试使用更高阶的多项式,它确实可以),但除此之外,它可能不是您想要的平滑曲线类型。更好的选择是使用插值法和样条。这也是一种使用多项式进行插值的方法,但它不仅使用一个多项式(就像您尝试的那样),而是使用多个多项式。它们被强制通过所有数据点,以使您的曲线连续。

据我所知,ggplot无法直接完成此操作,但可以使用ggalt :: geom_xspline 来完成。

在这里,我展示了一个基础解决方案,其中样条插值是在单独的步骤中生成的:

spline_int <- as.data.frame(spline(d$hour, d$impressions))
你需要使用 as.data.frame,因为 spline 返回的是一个列表。现在,你可以在图表中使用 geom_line() 来展示这些新数据:
ggplot(d) + 
  geom_point(aes(x = hour, y = impressions, colour = cvr), size = 3) +
  geom_line(data = spline_int, aes(x = x, y = y))

在此输入图片描述


啊,非常酷!我一直在研究样条曲线,但不知道如何将它们整合到我的图表中。 - moku
9
我认为您可以使用method="gam"并更改节点数(或平滑项的维度)来使用stat_smooth获得类似的结果:stat_smooth(aes(y=impressions, x=hour), formula = y ~ s(x, k = 24), method = "gam", se = FALSE)。请注意,这不会改变原始含义。 - aosmith
1
@aosmith 现在最好有一种自动引用x轴值数量的方法,而不是硬编码 k = 24 数字。有什么想法吗? - slhck
@slhck 或许像 k = nrow(data) 这样的代码是你需要的? - aosmith
2
使用我的数据,我无法使 gam 方法起作用,而上面答案中的样条方法倾向于超调。这个答案(https://dev59.com/57bna4cB1Zd3GeqPh9zI)使用了 spline_shape=-0.5,非常有效。 - Mark Neal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接