在凸起图表中使用曲线

时间:2017-05-04 00:19:26

标签: r ggplot2 spline

我正在尝试制作一个颠簸图表(如parallel coordinates,但有一个有序的x轴)以显示随时间的排名。我可以很容易地制作一个直线图表:

library(ggplot2)
set.seed(47)

df <- as.data.frame(as.table(replicate(8, sample(4))), responseName = 'rank')
df$Var2 <- as.integer(df$Var2)

head(df)
#>   Var1 Var2 rank
#> 1    A    1    4
#> 2    B    1    2
#> 3    C    1    3
#> 4    D    1    1
#> 5    A    2    3
#> 6    B    2    4

ggplot(df, aes(Var2, rank, color = Var1)) + geom_line() + geom_point()

奇妙。但是,现在,我想让连接线弯曲。尽管从未超过一y y,geom_smooth提供了一些可能性。 loess似乎应该有效,因为它可以忽略除最近点之外的点。然而,即使调整到最好,我仍然会错过很多积分并超过其他应该平坦的点:

ggplot(df, aes(Var2, rank, color = Var1)) + 
    geom_smooth(method = 'loess', span = .7, se = FALSE) + 
    geom_point()

我尝试过其他一些样条曲线,例如ggalt::geom_xspline,但它们仍然超出或错过了这些点:

ggplot(df, aes(Var2, rank, color = Var1)) + ggalt::geom_xspline() + geom_point()

有没有简单的方法来曲线化这些线?我需要建立自己的S形样条吗?为了澄清,我正在寻找像D3.js's d3.curveMonotoneX这样的东西,它击中每个点,其局部最大值和最小值不超过y值:

d3.curveMonotoneX image

理想情况下,每个点的坡度也可能为0,但这并非绝对必要。

2 个答案:

答案 0 :(得分:23)

signal::pchip与X值网格结合使用,至少在您的示例中使用数字轴。一个合适的geom_会很好,但是嘿......

library(tidyverse)
library(signal)
set.seed(47)

df <- as.data.frame(as.table(replicate(8, sample(4))), responseName = 'rank')
df$Var2 <- as.integer(df$Var2)

head(df)
#>   Var1 Var2 rank
#> 1    A    1    4
#> 2    B    1    2
#> 3    C    1    3
#> 4    D    1    1
#> 5    A    2    3
#> 6    B    2    4

ggplot(df, aes(Var2, rank, color = Var1)) +
  geom_line(data = df %>%
              group_by(Var1) %>%
              do({
                tibble(Var2 = seq(min(.$Var2), max(.$Var2),length.out=100),
                       rank = pchip(.$Var2, .$rank, Var2))
              })) +
  geom_point()

结果:Result

答案 1 :(得分:1)

在Henrik的答案的基础上,它包装了pchip(我在这里使用pracma中的一个,但结果是相同的),因此可以更轻松地与现有的平滑方法一起使用:< / p>

ggpchip = function(formula, data, weights) structure(pracma::pchipfun(data$x, data$y), class='ggpchip')
predict.ggpchip = function(object, newdata, se.fit=F, ...) {
  fit = unclass(object)(newdata$x)
  if (se.fit) list(fit=data.frame(fit, lwr=fit, upr=fit), se.fit=fit * 0) else fit
}

然后实际的ggplot调用很简单:

ggplot(df, aes(Var2, rank, color=Var1)) + geom_smooth(method='ggpchip', se=F) + geom_point()

然后可以使用pchip平滑其他几何图形,例如面积图:

ggplot(df, aes(Var2, rank, fill=Var1)) + stat_smooth(method='ggpchip', geom='area', position='fill')