thomasp85 / patchwork

The Composer of ggplots
https://patchwork.data-imaginist.com
Other
2.44k stars 159 forks source link

Annotate patchwork with line segments to create semantic plot relationships #216

Open mattwarkentin opened 3 years ago

mattwarkentin commented 3 years ago

Hi @thomasp85,

I am wondering if it is currently possible to add line segments (such as those created with geom_segment(), for example) or any other ggplot annotations, on top of a patchwork to be able to create arbitrary relationships between plots. This could be useful for both standard patchworks or inset patchworks to create more obvious relationships between plots. Basically I'm trying to avoid doing any figure post-processing outside of R.

It is my understanding that the patchwork itself is a ggplot object, so I am wondering if this is possible, and if it isn't currently possible, if this feature is feasible. Maybe treating the full patchwork as having x and y dimensions of [0, 1] - so lines could somehow be drawn from and to arbitrary points on the patchwork. patchwork support for ggplot2::annotate() acting on the full patchwork would allow for some very flexible customization.

Here is an illustrative example for what I'm hoping to achieve (I'm hoping to be able to add the dashed lines to arbitrarily connect the subplots in a patchwork):

Screen Shot 2020-11-18 at 4 55 17 PM
thomasp85 commented 3 years ago

I agree that that would be very useful, but also non-trivial if at all possible. The reasons for this are pretty technical, but basically the different plots does not occupy a shared coordinate system and figuring out their relative locations are not straight-forward.

Don't hold your breath for it 🙂

mattwarkentin commented 3 years ago

I definitely do not have any of the technical knowledge to pretend to understand how patchwork or ggplot2 work internally, but would it be possible to "flatten" or rasterize the entire patchwork and then treat that as a single grob upon which additional annotations could be plotted?

Obviously vector graphics are preferred for various reasons. I'm just thinking out loud.

thomasp85 commented 3 years ago

Sure, you can just treat it as one big canvas - that is already possible. But there is no way to reference any existing information in the plot so you'd have to position the lines through trial and error. I'd advice just doing it in illustrator/inkscape instead

mattwarkentin commented 3 years ago

Congrats on the CRAN release, Thomas. To follow-up on your above comment, could you point me in the right direction for doing what you described above? I don't actually mind a bit of trial-and-error as long as the end result is reproducible.

How does one "flatten" the patchwork into a single big canvas? I have tried using annotate() on a patchwork, but it seems to just add the annotation to the sub-plot canvases using the sub-plot coordinate systems. This would prohibit spanning annotations across/between plots.

library(magrittr)
library(ggplot2)
library(patchwork)

p1 <- 
  mtcars %>% 
  ggplot(aes(mpg, hp)) +
  geom_point()

(p1 / p1) + annotate("text", 1, 1, label = "foo")

(p1 / p1) & annotate("text", 1, 1, label = "foo")

stoneyv commented 3 years ago

@mattwarkentin There is a simple example at the end of this documentation that will yield annotations between ggplots grobs in different viewports in a grid using the package gggrid. https://www.stat.auckland.ac.nz/~paul/Reports/gggrid/gggrid.html

gginards geom_debug(), ggpmisc https://docs.r4photobiology.info/gginnards/ https://docs.r4photobiology.info/ggpmisc/ https://pparacch.github.io/2017/09/25/plotting_in_R_ggplot2_part_5.html https://bookdown.org/rdpeng/RProgDA/the-grid-package.html

A great inspiration for visualizing relationships between data has been implemented by Susie Lu in d3-annotations d3js and react-annotations of the semiotic framework. https://react-annotation.susielu.com/

japhir commented 5 months ago

I recently ran into a similar issue (at least I think?) where I'd like to draw lines between subplots using both the subplot's coordinate systems.

For example:

library(ggplot2)
library(patchwork)

# let's say we have two sites a and b, where each has its own "depth" scale:
dat <- tibble::tibble(depth = 1:200, a = rnorm(length(depth)), b = rnorm(length(depth)))

# we can make a nice patchwork showing  both (in this case the depth scale is identical but it could be very different)
(ggplot(dat) +
   aes(x = depth, y = a) + geom_line() + 
   # where these vertical lines show which parts are related to which part of the other site
   geom_vline(xintercept = c(0, 20, 30, 75, 150))) /
(ggplot(dat) +
   aes(x = depth, y = b) + geom_line() + 
   # so here we get different values
   geom_vline(xintercept = c(25, 50, 90, 120)))

ggsave("imgs/patchwork_annotation.png")

which results in the following image:

patchwork_annotation

I'd like to be able to draw segments that connect these different elements in the different plots. patchwork_annotation_tweaked

I think at present there is no way to draw geoms that take into account the x-axis values from both subplots, right? And hacking stuff together with grids itself seems to prefer relative units? So far I've had to do this kind of thing in Inkscape, which is a bit of a hassle if I need to update my figures.

I realize now that I shouldn't have drawn the vline at x = 0 for plot a, because it doesn't connect