6

Sometimes I will make two plots that are similair, but have different data. As such, the X- and Y-axis will have different ranges.

library(ggplot2)
library(ggpubr)
#> Loading required package: magrittr
df1 <- data.frame(x=runif(10)*2,y = runif(10)*2)
df2 <- data.frame(x=runif(10)*3,y = runif(10)*1)
p1 <- qplot(x = x, y = y, data = df1, geom = "line")
p2 <- qplot(x = x, y = y, data = df2, geom = "line")

ggarrange(p1,p2)

Created on 2020-07-09 by the reprex package (v0.3.0)

This can manually be overcome by explicitly stating a range with xlim and ylim, but this is both tiresome and could lead to some data being outside of the specified range if one is not careful.

An ideal solution would be to dynamically obtain the limits from the p1, and if these are larger than those of p2, use those instead for p2. For example p2 + xlim(getLimits(p1)).

Is something like this supported?

EDIT: This question was suggested, but the answer applies to an older version of ggplot2. Additionally, the supported p1$coordinates$limits only return the manually specified limits, which defeats the purpose.

mhovd
  • 3,724
  • 2
  • 21
  • 47
  • I took a look at it, but I wasn't able to get the limits using either the `coordinates$limits` object, or the other that were proposed. It seems like it was an older version of `ggplot2`. Additionally, `p1$coordinates$limits` only return the limits that were specified with `xlim` etc. – mhovd Jul 09 '20 at 16:14
  • [The second answer](https://stackoverflow.com/a/35372274/8366499) in that duplicate question includes the newer `layer_scales` function while the 3rd discussed the `ggplot_build` option, both of which should do what you want to do, I think – divibisan Jul 09 '20 at 17:20

3 Answers3

9

ggplot now has layer_*** convenience functions for extracting information from ggplots. In this case you can use the layer_scales function:

layer_scales(p1)$y$get_limits()

[1] 0.1499588 1.9527970

So you could do something like:

library(tidyverse)
library(patchwork)
library(ggpubr)
theme_set(theme_bw())

set.seed(2)
df1 <- data.frame(x=runif(10)*2,y = runif(10)*2)
df2 <- data.frame(x=runif(10)*3,y = runif(10)*1)
p1 <- qplot(x = x, y = y, data = df1, geom = "line")
p2 <- qplot(x = x, y = y, data = df2, geom = "line")

fnc = function(...) {
  
  p = list(...)
  
  yr = map(p, ~layer_scales(.x)$y$get_limits()) %>% 
    unlist %>% range
  
  xr = map(p, ~layer_scales(.x)$x$get_limits()) %>% 
    unlist %>% range
  
  p %>% map(~.x + xlim(xr) + ylim(yr))
}

wrap_plots(fnc(p1, p2))

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
2

Just draw one graph instead of two:

library(dplyr)
library(ggplot2)

# 1. create datasets
df1 <- data.frame(x=runif(10)*2, y = runif(10)*2)
df2 <- data.frame(x=runif(10)*3, y = runif(10)*1)


# 2. union datasets and plot
df1 %>% 
  mutate(dataset_name = "Dataframe #1") %>% 
  bind_rows(df2 %>% mutate(dataset_name = "Dataframe #2")) %>% 

  ggplot(aes(x = x, y = y)) +
    geom_line() +
    facet_grid(~ dataset_name)

enter image description here

codez0mb1e
  • 706
  • 6
  • 17
  • This assumes the data is bindable, e.g. no unique columns (which could be filtered out, but certainly complicates the process). – mhovd Jul 09 '20 at 16:37
  • 1
    Why? `dplyr::bind_rows` doesn't require that merged dateframes contain the same number or names of columns. – codez0mb1e Jul 09 '20 at 16:44
  • I stand corrected - I thought it was little but a wrapper for `rbind`, but I tested it and it seems to work! – mhovd Jul 09 '20 at 16:51
1

Sounds like you're looking for ggplot_build()? The output of that function includes a panel object which contains axis limits. You can read more about it here. This is to get the panel object:

panel1 <- ggplot_build(p1)$panel
panel2 <- ggplot_build(p2)$panel

And then you can extract the ranges for x and y from there. The exact specifics depends on your installation version of ggplot2. Hope this helps.

Kel Varnsen
  • 314
  • 2
  • 8