1

I am trying to plot three different plots together to form a larger figure. The problem is that plot.margin(c(0,0,0,0), "cm") doesn't remove the whitespace as expected. The figures are close together as expected if I remove the coord_fixed.

Example dataset, code to generate dataset and code used producing the example.

Incorrect image

Code for dataset.

library(tidyverse)
library(cowplot)
n_pat <- 25
patient <- 1:n_pat
censoring <- ceil(rexp(n_pat, 1/30))
tumour_shrink <- (rbeta(n_pat, 2, 2) - 0.5) * 100

n_params <- 15
parameters <- paste("Parameter", 1:n_params)

response <- sample(c("PR", "NE", "CR", "PD", "SD"), size=n_pat,
                   replace = T)

missing_combination <- sample(c(TRUE, FALSE), size=n_pat, replace=T)

changes <- matrix(runif(n_pat * n_params, 1, 100), nrow=n_pat, ncol=n_cytokines)
changes[sample(1:dim(changes)[1], 4, replace = FALSE), sample(1:dim(changes)[2], 5, replace = F)] <- NA

df <- data.frame(patient, censoring, tumour_shrink, changes, missing_combination)
colnames(df) <- c("patient", "censoring", "tumour_shrink", cytokines, "missing_combination")

Code for plots:

p1 <- df %>%
  mutate(color = case_when(
    response == "PR" ~ "lightgreen",
    response == "NE" ~ "white",
    response == "CR" ~ "darkgreen",
    response == "PD" ~ "red",
    response == "SD" ~ "yellow"
  )) %>% 
  arrange(tumour_shrink) %>% 
  mutate(patient = factor(patient, levels=patient)) %>% 
  ggplot(aes(x=patient, y=1, fill=color)) +
  geom_raster() +
  coord_fixed() +
  geom_tile(color="black", size=1) +
  geom_text(aes(label=response), size=3) +
  theme(axis.text = element_blank(),
        axis.ticks = element_blank(),
        legend.position = "none",
        axis.ticks.length = unit(0, "mm"),
        axis.title.y = element_text(angle = 0, vjust=0.57, size = 12),
        plot.margin = unit(c(0, 0, 0, 0), "cm")) +
  scale_fill_identity() +
  labs(y="Best ov. resp", x=NULL)

p2 <- df %>%
  arrange(tumour_shrink) %>% 
  mutate(patient = factor(patient, levels=patient)) %>% 
  mutate(color = ifelse(missing_combination, "white", "gray")) %>% 
  ggplot(aes(x=patient, y=1, fill=color)) +
  geom_raster() +
  geom_tile(color="black", size=1) +
  geom_text(aes(label=censoring), size=3) +
  scale_fill_identity() + 
  labs(y="Censoring", x=NULL) +
  coord_fixed() +
  theme(axis.text = element_blank(),
        axis.title = element_blank(),
        axis.ticks = element_blank(),
        axis.ticks.length = unit(0, "mm"),
        legend.position = "none",
        axis.title.y = element_text(angle = 0, vjust=0.57, size=12),
        plot.margin = unit(c(0, 0, 0, 0), "pt"))

p3 <- df %>% 
  arrange(tumour_shrink) %>% 
  mutate(patient = factor(patient, levels=patient)) %>% 
  ggplot(aes(x=factor(patient), y=1, fill=tumour_shrink)) +
  geom_raster(alpha=0.8) +
  geom_tile(color="black", size=1) +
  coord_fixed() +
  geom_text(aes(label=formatC(tumour_shrink, 0, format="f")), 
            size=3) +
  theme(axis.text = element_blank(),
        axis.title = element_blank(), 
        axis.ticks = element_blank(),
        axis.ticks.length = unit(0, "mm"),
        axis.title.y = element_text(angle = 0, vjust=0.57, size=12),
        plot.margin = unit(c(0, 0, 0, 0), "cm"),
        legend.position = "none") +
  scale_fill_gradient(low="green", high="red") +
  labs(y="Tumour shrink", x=NULL)

plot_grid(p1, p2, p3, ncol=1, align="v", axis="lr")
MesRot
  • 133
  • 1
  • 9

2 Answers2

1

A simple solution is provided by the patchwork package:

library(patchwork)
wrap_plots(p1, p2, p3, ncol=1)

enter image description here

Marco Sandri
  • 23,289
  • 7
  • 54
  • 58
1

Another option: plot all together in one plot. The benefit of this is that you have complete control over the spacing, and it reduces your need for cowplot/patchwork.

This requires a few changes to your plotting methods:

  • define different colors for each line, facilitated/enabled by your use of scale_fill_identity;
  • separate y= for each geom;
  • use rank(-tumour_shrink) as the x value instead of patient;
  • remove the axis.ticks theme, since I'm (ab)using them for the "Censoring" (etc) y-axis labels (and manually assigning those); and
  • adding more theme elements to remove the background grid.

For reproducibility, I replaced ceil with ceiling, and set the random seed:

set.seed(42)
# df <- ...
head(df)
#   patient censoring tumour_shrink Cytokine 1 Cytokine 2 Cytokine 3 Cytokine 4 Cytokine 5
# 1       1         6     -4.554208   45.73676  78.626829   57.88910  56.582358   87.05652
# 2       2        20     42.752870   32.38828  13.758344   35.98169   8.001468   21.29991
# 3       3         9     10.031523         NA  13.779839   55.19518         NA         NA
# 4       4         2      8.484090   19.42411   8.153058   89.37914  55.412421   88.78861
# 5       5        15    -11.012928   73.24328   6.259819   49.50907  48.716163   14.49329
# 6       6        44     21.390972   41.77534  53.655569   17.99158  16.787516   78.74959
#   Cytokine 6 Cytokine 7 Cytokine 8 Cytokine 9 Cytokine 10 Cytokine 11 Cytokine 12 Cytokine 13
# 1  73.439052   30.38779  10.000912  61.971232    52.95427   26.663044    92.21165   32.736054
# 2  32.210819   65.66861  80.061067  92.588889    66.02428   78.010740    36.89758   19.686203
# 3  39.258947   90.20598         NA  39.612267          NA   43.220164    85.65836   39.989134
# 4  33.912153   99.08878   5.008270  29.504049    72.40138    6.944785    30.78971   86.340576
# 5   9.888019   43.60299   5.067548   9.982861    49.58429   12.368421    47.10058   35.443229
# 6  75.948505   39.98392  95.488990  32.881356    96.56030   48.792933    15.13034    1.141956
#   Cytokine 14 Cytokine 15 missing_combination
# 1    48.93261    16.28906                TRUE
# 2    84.69391    27.37059               FALSE
# 3    42.21307    35.72471                TRUE
# 4    49.44491    58.26738                TRUE
# 5    19.14549    81.05158                TRUE
# 6    76.15699    93.39376               FALSE

I'm arbitrarily using 1, 3, and 5 as the y values, you can obviously change them to suit your needs.

library(dplyr)
library(ggplot2)
df %>%
  arrange(tumour_shrink) %>% 
  mutate(
    xval = rank(-tumour_shrink),
    patient = factor(patient),
    color1 = case_when(
      response == "PR" ~ "lightgreen",
      response == "NE" ~ "white",
      response == "CR" ~ "darkgreen",
      response == "PD" ~ "red",
      response == "SD" ~ "yellow"
    ),
    color2 = if_else(missing_combination, "white", "gray"),
    color3 = do.call(rgb, asplit(colorRamp(colors = c("green", "red"))(
      (tumour_shrink - min(tumour_shrink)) / diff(range(tumour_shrink))
    )/255, 2))
  ) %>%
  ggplot(aes(x = xval)) +
  coord_fixed() +
  # plot 1
  geom_tile(aes(y = 5, fill = color1), color="black") +
  geom_text(aes(y = 5, label = response), size=3) +
  # plot 2
  geom_tile(aes(y = 3, fill=color2), color="black") +
  geom_text(aes(y = 3, label=response), size=3) +
  # plot 3
  geom_tile(aes(y = 1, fill = color3), color = "black") +
  geom_text(aes(y = 1, label = formatC(tumour_shrink, 0, format="f")), 
            size=3) +
  theme(
    legend.position = "none",
    axis.ticks.length = unit(0, "mm"),
    axis.title.y = element_text(angle = 0, vjust=0.57, size = 12),
    plot.margin = unit(c(0, 0, 0, 0), "cm"),
    panel.background = element_blank(),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank()) +
  scale_fill_identity() +
  scale_y_continuous(
    name = NULL, breaks = c(5,3,1),
    labels = c("Best ov. resp", "Censoring", "Tumour shrink")) +
  scale_x_discrete(name = NULL)

three plots done at once, no combining required

I admit that the do.call(rgb, ...) is a bit over the top, but it provides the same effect, and I've never really been fond of the matrix return-value of colorRamp. (Again, that part was enabled by your use of scale_fill_identity, without which this would not be easy/feasible.)

r2evans
  • 141,215
  • 6
  • 77
  • 149
  • I might have reversed the green/red color ramp order ... – r2evans Aug 11 '21 at 12:06
  • One bug I found that in plot2 label=response -> label=censoring. Overall a very good solution although its a bit more complex but more compact! – MesRot Aug 11 '21 at 12:14
  • Sorry for the bug, glad you found that. I actually find it *less* complex personally, as it reduces the code significantly and controls everything in one swoop, but then again it's very much in the eye of the beholder. Glad you liked it, I wanted to provide another perspective. Good luck! – r2evans Aug 11 '21 at 12:16