**ggplot2**: Difference between scale_x_continuous(breaks = ) and xlim() (or eqivalently scale_y_continouous(breaks=) and ylim)

Viewed 374

In ggplot2, I would like to specify the value labels in the graph as well as make the y-axis and x-axis over the same range, even if no data is present.

The base graph p is given below. I would like to add labels every 5 to x-axis and y-axis and extend x-axis border to 105, although no borders are present.

Can anyone help me understand how scale_x_continuous(breaks=...) and xlim() interact? Or how scale_x_continuous(breaks=..., lim = c(...)) is different?

library(ggplot2)
library(viridis)
#> Loading required package: viridisLite

ggplot_common_scatter <- function(p, size = 3, step = 1){
  q <- p +
    theme_bw() +
    geom_point(size = size) +
    scale_y_continuous(breaks = h(step)) +
    scale_fill_viridis(begin = 0.4, end = 1, direction = 1, discrete = TRUE) +
    theme(legend.position = "bottom",
          axis.text = element_text(size = 13),      #Text size of axes
          axis.title = element_text(size = 14,face = "plain"),  #Text size of axis titles
          #  axis.text.x = element_text(angle = 90)    #not if text
          #  only for MSD
          panel.grid.major = element_blank(), 
          panel.grid.minor = element_blank(),   #remove gridlines
          legend.box = "horizontal", legend.direction = "vertical"   #add if needed if legend is too wide.
    ) 
  return(q)
}
  
h <- function(k) {
  step <- k
  #  Round the minimum down to the nearest 5
  #  y.min <-  5*floor(min(y)/5
  #  Round the maximum up to the nearest 5
  #  y.max <- 5*ceiling(max(y)/5)
  
  function(y){ 
 #   cat("min: ", min(y, na.rm = TRUE), "max: ", max(y, na.rm = TRUE), "|| Labels:")
    start <- 5*floor(min(y, na.rm = TRUE)/5)
    end <-  5*ceiling(max(y, na.rm = TRUE)/5)
    A <- seq(start, end, by = step)
 #   print(A)
    return(A)
  }
}


data(anorexia, package = "MASS")

p <- ggplot_common_scatter(
  ggplot(anorexia, 
         aes(x = Prewt , 
             y = Postwt, 
             col = Treat,
             pch = Treat 
         )
         ),
         step = 10
  )+
  ggtitle("Comparisons of Aneorxia girls"
  ) +   
  geom_rect(color = "black", fill = NA,  
            aes(xmin = 70, xmax = 100, ymin = 70, ymax = 100)  #lty = "dashed", alpha = 0.5
  )  

p

enter image description here

p + xlim(70, 105)

enter image description here

p + scale_x_continuous(breaks = seq(70, 105, by = 5))

enter image description here

#Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale
p + scale_x_continuous(breaks = seq(70, 105, by = 5)) + xlim(70, 105)
#> Scale for 'x' is already present. Adding another scale for 'x', which will
#> replace the existing scale.

enter image description here

p + scale_x_continuous(breaks = seq(70, 105, by = 5), lim = c(70, 105))

enter image description here

xfun::session_info(c("ggplot2", "viridis"))
#> R version 4.0.4 (2021-02-15)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 18363)
#> 
#> Locale:
#>   LC_COLLATE=English_United States.1252 
#>   LC_CTYPE=English_United States.1252   
#>   LC_MONETARY=English_United States.1252
#>   LC_NUMERIC=C                          
#>   LC_TIME=English_United States.1252    
#> 
#> Package version:
#>   assertthat_0.2.1   cli_2.3.1          colorspace_2.0.0   crayon_1.4.1      
#>   digest_0.6.27      ellipsis_0.3.1     fansi_0.4.2        farver_2.1.0      
#>   ggplot2_3.3.3      glue_1.4.2         graphics_4.0.4     grDevices_4.0.4   
#>   grid_4.0.4         gridExtra_2.3      gtable_0.3.0       isoband_0.2.4     
#>   labeling_0.4.2     lattice_0.20.41    lifecycle_1.0.0    magrittr_2.0.1    
#>   MASS_7.3.53.1      Matrix_1.3.2       methods_4.0.4      mgcv_1.8.34       
#>   munsell_0.5.0      nlme_3.1.152       pillar_1.5.1       pkgconfig_2.0.3   
#>   R6_2.5.0           RColorBrewer_1.1.2 rlang_0.4.10       scales_1.1.1      
#>   splines_4.0.4      stats_4.0.4        tibble_3.1.0       tools_4.0.4       
#>   utf8_1.2.1         utils_4.0.4        vctrs_0.3.6        viridis_0.5.1     
#>   viridisLite_0.3.0  withr_2.4.1
Created on 2021-05-11 by the reprex package (v1.0.0)
2 Answers

The warning message gives the hint:

#> Scale for 'x' is already present. Adding another scale for 'x', which will
#> replace the existing scale.

xlim() and ylim() replace the existing scale in the plot instead of modifying its limits.

The code below using expand_limits() modifies the limits of the scale already present in the plot. (With the added advantage that the scale will grow past the requested limits if needed to accommodate all observations.)

p + scale_x_continuous(breaks = seq(70, 105, by = 5)) + 
   expand_limits(x = c(70, 105))

The resulting plot

library(ggplot2)
library(viridis)
#> Loading required package: viridisLite

ggplot_common_scatter <- function(p, size = 3, step = 1){
  q <- p +
    theme_bw() +
    geom_point(size = size) +
    scale_fill_viridis(begin = 0.4, end = 1, direction = 1, discrete = TRUE) +
    theme(legend.position = "bottom",
          axis.text = element_text(size = 13),      #Text size of axes
          axis.title = element_text(size = 14,face = "plain"),  #Text size of axis titles
          #  axis.text.x = element_text(angle = 90)    #not if text
          #  only for MSD
          panel.grid.major = element_blank(), 
          panel.grid.minor = element_blank(),   #remove gridlines
          legend.box = "horizontal", legend.direction = "vertical"   #add if needed if legend is too wide.
    ) 
  return(q)
}


data(anorexia, package = "MASS")

p <- ggplot_common_scatter(
  ggplot(anorexia, 
         aes(x = Prewt , 
             y = Postwt, 
             col = Treat,
             pch = Treat 
         )
  ),
  step = 10
)+
  ggtitle("Comparisons of Aneorxia girls"
  ) +   
  geom_rect(color = "black", fill = NA,  
            aes(xmin = 70, xmax = 100, ymin = 70, ymax = 100)  #lty = "dashed", alpha = 0.5
  )  

If you need to use scale_*_continuous() to specify the breaks, you should try and do everything relating to that axis in that function.

p +
  scale_x_continuous(breaks = seq(70, 105, 5)) +
  scale_y_continuous(breaks = seq(70,105,5)) 

xlim()/ylim() are shorthands for the limit arguments in scale_*_continuous() they will specify the limits for your data, e.i. any data points outside the range of the limits will become NAs. This occurs BEFORE any geometries are drawn, and thus can affect certain geometries to be unable to plot. See example

p +
  scale_x_continuous(breaks = seq(70, 105, 5), limits = c(80,90)) +
  scale_y_continuous(breaks = seq(70,105,5)) +
  #ylim is shorthand but will replace previous specification
  ylim(c(70,105)) 
#> Scale for 'y' is already present. Adding another scale for 'y', which will
#> replace the existing scale.
#> Warning: Removed 25 rows containing missing values (geom_point).
#> Warning: Removed 72 rows containing missing values (geom_rect).

xlim/ylim arguments in coord_cartesian() will enforce a zoom on your plot this happens after everything has been plotted and will not remove any data.

p +
  scale_x_continuous(breaks = seq(70, 105, 5), limits = c(70,105)) +
  scale_y_continuous(breaks = seq(70,105,5)) +
  coord_cartesian(xlim = c(80,90), ylim = c(70,105))

final note, just because you specify limits for the plot does not mean it will plot like so. Each axis has an expansion factor. If you want the plotting region to end at where you specify the limits, then you can use the expand argument in the scale_*_*() functions

p +
  scale_x_continuous(breaks = seq(70, 105, 5), limits = c(70,105), expand = expansion()) +
  scale_y_continuous(breaks = seq(70,105,5), limits = c(70,105), expand = expansion())

Created on 2021-05-11 by the reprex package (v1.0.0)

Related