Search code examples
rggplot2splineggpubrggalt

Blank Plot Output when using "geom_xspline" in ggalt package


When trying to use geom_xspline from ggalt in conjunction with ggarrange from ggpubr, the output is blank and no other plot can be made before clearing with dev.off().

In my use-case I wanted the geom_xspline to replace some exisitng geom_line in my ggplot object. Is anyone aware of issues using geoms added from other R packages?

Here is some code to compare, nothing of interest really, just to give a reproducible example:

Initial Working Code w/o geom_xspline

library(ggplot2)
library(ggpubr)
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_line()
ggarrange(myplot, myplot) # Works and outputs fine

Code that fails with ggalt package

library(ggalt)
library(ggplot2)
library(ggpubr)
myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_xspline()
ggarrange(myplot, myplot) # Output becomes blank and freezes the plot panel

Alternative Method Instead of using ggarrange I tried the function grid_arrange_shared_legend from this link, which uses grid and gridExtra. However, I am still curious as to why ggarrange does not work.

Here is my session info:

R version 3.5.1 (2018-07-02)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                           LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggpubr_0.1.8  magrittr_1.5  ggplot2_3.0.0

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.18       pillar_1.3.0       compiler_3.5.1     RColorBrewer_1.1-2 plyr_1.8.4         bindr_0.1.1       
 [7] tools_3.5.1        extrafont_0.17     tibble_1.4.2       gtable_0.2.0       pkgconfig_2.0.1    rlang_0.2.1       
[13] rstudioapi_0.7     yaml_2.2.0         bindrcpp_0.2.2     Rttf2pt1_1.3.7     withr_2.1.2        dplyr_0.7.6       
[19] maps_3.3.0         grid_3.5.1         ggalt_0.4.0        tidyselect_0.2.4   cowplot_0.9.3      glue_1.3.0        
[25] R6_2.2.2           purrr_0.2.5        extrafontdb_1.0    scales_1.0.0       MASS_7.3-50        assertthat_0.2.0  
[31] proj4_1.0-8        colorspace_1.3-2   labeling_0.3       KernSmooth_2.23-15 ash_1.0-15         lazyeval_0.2.1    
[37] munsell_0.5.0      crayon_1.3.4

Quick addition, if I convert the object to a ggplotGrob(), it will work with ggarrange, but it will fail when I attempt to use common.legend = T.


Solution

  • The xspline function, upon whichgeom_xspline is based, typically automatically plots using graphics. This led the ggalt package authors to find a few work-arounds to ensure it would play nicely with ggplot. My rough solutions both involve creating or adjusting a geom or stat from ggplot without using xspline. This makes it easier to use without a lot of pre-processing the data prior to ingesting with ggplot.

    (1) New stat using splines

    Using spline for interpolation of points instead of xspline.

    # Create a new stat (adjusted from ggalt GitHub page)
    stat_spline <- function(mapping = NULL, data = NULL, geom = "line",
                             position = "identity", na.rm = TRUE, show.legend = NA, inherit.aes = TRUE,
                             n=200, method = "fmm", ...) { # Just picking a rough default for n
      layer(
        stat = StatSpline,
        data = data,
        mapping = mapping,
        geom = geom,
        position = position,
        show.legend = show.legend,
        inherit.aes = inherit.aes,
        params = list(n=n,
                      method=method,
                      na.rm = na.rm,
                      ...
        )
      )
    }
    
    StatSpline <- ggproto("StatSpline", Stat,
    
                           required_aes = c("x", "y"),
    
                           compute_group = function(self, data, scales, params,
                                                    n=200, method = "fmm") {
    
                             tmp <- spline(data$x, data$y, n = n, method = method, ties = mean)
    
                             data.frame(x=tmp$x, y=tmp$y)
                           }
    
    )
    
    # Plot with ggarrange
    myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
    stat_spline(mapping = aes(x = wt, y = mpg)) +
    geom_point()
    
    ggpubr::ggarrange(myplot, myplot)
    

    This method isn't ideal if you want splines similar to Catmull-Rom instead of Cubic; you can see some large bends between control points.

    enter image description here

    (2) New geom using xsplineGrob

    This is a slightly adjusted version of geom_xspline2 from ggalt

    # Create new geom based upon code from ggalt GitHub page
    GeomXSpline3 <- ggproto("GeomXSpline3", Geom,
                            required_aes = c("x", "y"),
                            default_aes = aes(colour = "black", shape=-1, open=T),
                            draw_key = draw_key_point,
    
                            draw_panel = function(data, panel_params, coord) {
                              coords <- coord$transform(data, panel_params)
                              grid::xsplineGrob(
                                coords$x, coords$y,
                                shape = coords$shape, 
                                open = coords$open[1],
                                gp = grid::gpar(col = coords$colour)
                              )
                            }
    )
    
    geom_xspline3 <- function(mapping = NULL, data = NULL, stat = "identity",
                              position = "identity", na.rm = FALSE, show.legend = NA,
                              inherit.aes = TRUE, ...) {
      layer(
        geom = GeomXSpline3, mapping = mapping,  data = data, stat = stat,
        position = position, show.legend = show.legend, inherit.aes = inherit.aes,
        params = list(na.rm = na.rm, ...)
      )
    }
    
    # Plot with ggarrange
    myplot = ggplot(data = mtcars, aes(x = wt, y = mpg)) +
      geom_xspline3(shape = -.25) + geom_point()
    ggpubr::ggarrange(myplot, myplot) 
    
    

    There were a couple issues with ensuring the shape parameter still accepted inputs between -1 and 1, however, this seems to be working okay now with ggarrange.

    enter image description here

    I used the following resources while writing this solution: