rggplot2splineggforce

How to extend geom_bspline in a bar chart?


I study extinctions of species (=y) across decades of the 19th century (=x). I am plotting a smoothed spline, which shows a cumulative value of extinct species, across a set of several bars which show the absolute value per decade. Now, the smoothed spline ends at the center of the last bar as the value occurs at the center of the X-postion. As this is visually unpleasant, I would like to extend the spline so that it continues to border of the plot.

I tried the position argument but dodge does not help. The geom_bspline function does not seem to offer a solution either (e.g. type="open" does not help).

So I am looking for a solution that allows to extend the geom_bspline from the ggforce package (or a similar solution).

Below I added the plot and the code example for the plot.

enter image description here

library("tidyverse")
library("ggforce")


## DATASET
dftest <- tibble(
  x = seq(1,11,1),
  y1 = c(3,   0 ,  1  , 0 ,  2   ,0,  20, 126, 426, 400 , 37),
  y2 = c( 3 ,3   , 4 ,   4 ,   6 ,   6 ,  26,  152 , 578 , 978, 1015)
)


###### Figure ------
ggplot(dftest, aes(x=x)) + 
  
  ### BARS
  geom_bar(aes(y= y1), fill="#FC301A", stat="identity") +
  
  ### SPLINE
  geom_bspline(aes(x=x,y=y2),data=dftest)+
  
  ### SCALES
  scale_x_discrete(name ="",  drop = FALSE) + 
  scale_y_continuous(expand = c(0,0),
                     limits = c(0, 1200),
                     breaks = seq(0,1200,200),name = "Kumulativ",
                     sec.axis = sec_axis(~ ((. - a)/b)+2,
                                         name = "Absolut"),
                     labels=scales::comma_format(big.mark = ".")) +
  
  ### THEME
  theme_classic()+
  theme(
    panel.grid.major.y = element_line(colour = "grey70", size = 0.2),  # linien
    
    ###axis
    axis.ticks.length = unit(1, "mm"),
    
    axis.text.x = element_text(family = "Helvetica",  size=10,angle = 90,vjust = 0.5,
                               margin = unit(c(0.2,0,0,0),"cm")),
    axis.text.y = element_text(family = "Helvetica",  size=10),
    axis.text.y.right = element_text(color = "red",family = "Helvetica",  size=10),
    
    
    axis.title.y.right =  element_text(color = "red",family = "Helvetica",  size=10,
                                       margin = unit(c(0,0,0,0.25),"cm")),
    axis.title.y.left =  element_text(family = "Helvetica",  size=10, 
                                      margin = unit(c(0,0.25,0,0),"cm"))
    
  )

Solution

  • A spline has to interpolate between knots and can't extrapolate unless you extrapolate the end knots, so the easiest way to do what you want is to create a copy of your data frame and add an extra row with an extra time measurement at, say x = 11.5 or x = 11.7 (wherever you feel "looks right"), but with the same y2 value:

    dftest2 <- rbind(dftest, tail(dftest, 1))
    dftest2$x[nrow(dftest2)] <- 11.7
    

    Then you can do:

    ggplot(dftest, aes(x = x)) + 
      geom_bar(aes(y= y1), fill="#FC301A", stat="identity") +
      geom_bspline(aes(x = x, y = y2), data = dftest2) +
      scale_x_discrete(name ="",  drop = FALSE) + 
      scale_y_continuous(expand = c(0,0),
                         limits = c(0, 1200),
                         breaks = seq(0,1200,200),name = "Kumulativ",
                         sec.axis = sec_axis(~ ((. - 0)/2.3)+2,
                                             name = "Absolut"),
                         labels=scales::comma_format(big.mark = ".")) +
      theme_classic()+
      theme(
        panel.grid.major.y = element_line(colour = "grey70", size = 0.2),
        axis.ticks.length = unit(1, "mm"),
        
        axis.text.x = element_text(family = "Helvetica",  
                                   size=10,angle = 90, vjust = 0.5,
                                   margin = unit(c(0.2,0,0,0),"cm")),
        axis.text.y = element_text(family = "Helvetica",size=10),
        axis.text.y.right = element_text(color = "red",
                                         family = "Helvetica",  size = 10),
        axis.title.y.right =  element_text(color = "red",family = "Helvetica",  
                                           size=10,
                                           margin = unit(c(0,0,0,0.25),"cm")),
        axis.title.y.left =  element_text(family = "Helvetica",  size=10, 
                                          margin = unit(c(0,0.25,0,0),"cm"))
      )
    

    enter image description here

    Note that your code was not fully reproducible because you didn't include the variables a and b for your secondary axis, so I had to approximate your secondary axis here.