Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine plots that have a legend with one that doesn't

I can combine three plots that don't have any legends, but when I add legends to two of the plots I can't combine them. How can I combine some plots with legends with a plot without a legend?

Here's an example of how I'm combining plots without legends:

library(ggplot2)
map_data <- data.frame(lon = c(-177.5, -172.5, -167.5, -162.5, -157.5, -152.5),
                       lat = c(87.5, 87.5, 87.5, 87.5, 87.5, 87.5),
                       ssta = c(-2.78999996, 0.09999999,  0.19999999,  0.06000000,  1.65999997, -0.41000000))

# one map
p.1 <- ggplot() + 
    borders("world", 
          colour="black", 
          fill="white") +
  geom_tile(data = map_data,
            aes(lon,
                lat)) + # no fill value so no legend
  coord_fixed() + 
  scale_fill_viridis(na.value = "transparent") +
  theme_minimal() 

# a second map, the same general shape
p.2 <- p.1

# a line plot
library(lubridate)
time_sequence <-  seq(dmy("01-01-1800"), 
                      dmy("01-01-1960"), 
                      by = 'month')
long_plot_data <- data.frame(variable = seq(dmy("01-01-1800"), 
                                            dmy("01-01-1960"), 
                                            by = 'month'),
                             value = runif(length(time_sequence)),
                             col = runif(length(time_sequence)))

p.3 <-  ggplot(long_plot_data, 
              aes(variable, 
                  value)) +
  geom_line() +
  theme_minimal()


# combine plots
library(grid)
grid.newpage()
grid.draw(rbind(ggplotGrob(p.1), 
                ggplotGrob(p.2),
                ggplotGrob(p.3),
                size = "last"))

enter image description here

And now if I add a fill value to the map plots, I get a legend, and an error when I try to combine in the same way:

# one map
p.1 <- ggplot() + 
    borders("world", 
          colour="black", 
          fill="white") +
  geom_tile(data = map_data,
            aes(lon,
                lat,
                fill = ssta)) + # gives a legend
  coord_fixed() + 
  scale_fill_viridis(na.value = "transparent") +
  theme_minimal() 

Here's what this looks like:

enter image description here

# a second map, the same general shape
p.2 <- p.1

# a line plot
library(lubridate)
time_sequence <-  seq(dmy("01-01-1800"), 
                      dmy("01-01-1960"), 
                      by = 'month')
long_plot_data <- data.frame(variable = seq(dmy("01-01-1800"), 
                                            dmy("01-01-1960"), 
                                            by = 'month'),
                             value = runif(length(time_sequence)),
                             col = runif(length(time_sequence)))

p.3 <-  ggplot(long_plot_data, 
              aes(variable, 
                  value)) +
  geom_line() +
  theme_minimal()

Now I try to combine the two maps with legends and the line plot without a legend:

# combine plots
library(grid)
grid.newpage()
grid.draw(rbind(ggplotGrob(p.1), 
                ggplotGrob(p.2),
                ggplotGrob(p.3),
                size = "last"))

The output is Error: ncol(x) == ncol(y) is not TRUE

But if I add a legend to the line plot, so that all three plots have legends, I can combine them:

p.3 <-  ggplot(long_plot_data, 
              aes(variable, 
                  value, 
                  colour = col)) +
  geom_line() +
  theme_minimal()


# combine plots
library(grid)
grid.newpage()
grid.draw(rbind(ggplotGrob(p.1), 
                ggplotGrob(p.2),
                ggplotGrob(p.3),
                size = "last"))

enter image description here

How can I combine two plots with legends with one without a legend?

like image 876
Ben Avatar asked Jun 24 '16 15:06

Ben


3 Answers

The reason you are getting the error is because one of your 3 gtable objects has 5 columns while the other has 6 (the p.3 object has 5 because you exclude the legend). You can check out the different layouts of the gtable objects by doing:

library(gtable)
gtable_show_layout(ggplotGrob(p.1))
gtable_show_layout(ggplotGrob(p.2))
gtable_show_layout(ggplotGrob(p.3))

I won't post the pictures here because they'd detract from the answer, but I think thats worthwhile to check out on your own.

Anyway, what we can do to remedy this problem is simply add an extra column onto your p.3 gtable and then use the size="first" option (because we want the legend to be in the right spot for the plots where we have legends).

This should work:

library(grid)
library(gtable)

grid.draw(rbind(ggplotGrob(p.1), 
                ggplotGrob(p.2),
                gtable_add_cols(ggplotGrob(p.3),unit(1,"lines")),
                size = "first"))

enter image description here

One final thing is the y-axis title on the last plot just slightly overlaps with the labels, this is because we took the sizing from the first plot and the labels are smaller on the first plot (so the y-axis labels need less space). To remedy this you could slightly alter your p.3 code to:

p.3<-  ggplot(long_plot_data, 
               aes(variable, 
                   value)) +
  geom_line() +
  theme_minimal() %+replace% theme(axis.title.y=element_text(margin=margin(0,20,0,0),angle=90))

Rerunning gives us:

grid.draw(rbind(ggplotGrob(p.1), 
                ggplotGrob(p.2),
                gtable_add_cols(ggplotGrob(p.3),unit(1,"lines")),
                size = "first"))

enter image description here

like image 164
Mike H. Avatar answered Nov 04 '22 14:11

Mike H.


Using plot_grid() from package cowplot may help -- at least it does manage to combine plots with and without legends, but it seems tricky to get the plots to align perfectly. I post anyway, as this may be helpful and maybe someone else knows how to sort out the alignment issue

Note, added coord_fixed() also to the line plot, to get make it same size as the maps.

p.3 <-  ggplot(long_plot_data, aes(variable, value)) +
  geom_line() +
  coord_fixed() + 
  theme_minimal() 

library(cowplot)
plot_grid(p.1,p.2,p.3,ncol=1, align='h')

enter image description here

like image 4
dww Avatar answered Nov 04 '22 14:11

dww


The gtable_add_cols Answer no longer works because the 3 gtable objects now have 11, 11 & 9 columns (when the above was written they had 6, 6 & 5). So you have to add TWO columns to the last plot (p.3), for example:

g1 <- ggplotGrob(p.1)
g2 <- ggplotGrob(p.2)
g3 <- ggplotGrob(p.3)
g3 <- gtable_add_cols(g3, g1$widths[5:6])
grid.newpage()
grid.draw(rbind(g1,g2,g3,
                size = "max"))
like image 3
NathanP Avatar answered Nov 04 '22 15:11

NathanP