I've reviewed other questions along these sames lines about combining plot legends with the patchwork package, but my question is different in that 3 of the 4 combined graphs have overlapping values. I.e., the three plots share some values, but some plots have unique values. And none of the plots has the complete set of values.
Thus, showing three different legends is redundant, because they share much of the same information. I would like to combine the three "character" legends somehow, or else find some way to make a legend independent of a particular plot.
Now you might think that it would be better to simply combine the first three plots into a single plot with three facets. I did that too, but for some reason, it put a lot of blank space between the facted triple plot and the 4th plot, they also don't scale together very well. I've spent all messing with that, so I'm thinking that one plot per data set is the best way to go.
Now, in this reproducible example, the legends of the first three plots have different names "character1, character2, etc." because I had to manually create the data here. In my real data, it all comes from an external csv file, and is then subsetted. So when I run the plot, the three Legends all have the same "character" title.
So my question here is, can I combine the first three character legends, while leaving the "episode" legend separate?
code:
library(ggplot2)
#dataset 1
channel1 <- c("Channel 1", "Channel 1", "Channel 1", "Channel 1", "Channel 1", "Channel 1", "Channel 1", "Channel 1", "Channel 1", "Channel 1")
start_time1 <- c(0.000, 9.719, 11.735, 14.183, 16.554, 18.482, 0.000, 11.693, 12.310, 13.912)
stop_time1 <- c(9.719, 11.735, 14.183, 16.554, 18.482, 19.553, 11.693, 12.310, 13.912, 15.406)
character1 <- c("A", "B", "C", "C", "B", "A", "A", "B", "C", "B")
df1 <- data.frame(channel1, start_time1, stop_time1, character1)
#dataset1 plot
plot1 <- ggplot(df1, aes(fill = character1, color = character1)) +
geom_rect(color = NA, aes(xmin = start_time1, xmax = stop_time1, ymin = -0.5, ymax = 0.5)) +
scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) +
facet_grid(channel1 ~ .) +
theme(axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank()) +
theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) +
scale_fill_manual(values=c("#F8766D", "#871282", "#ffe119", "#0072B2", "#00C094", "#00A9FF", "#C77CFF"))
#dataset 2
channel2 <- c("Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 2")
start_time2 <- c(0, 3, 4, 7, 9, 10, 12, 15, 17, 19)
stop_time2 <- c(3, 4, 6, 8, 11, 12, 14, 17, 18, 20)
character2 <- c("A", "D", "C", "E", "B", "A", "C", "D", "C", "B")
df2 <- data.frame(channel2, start_time2, stop_time2, character2)
#dataset2 plot
plot2 <- ggplot(df2, aes(fill = character2, color = character2)) +
geom_rect(color = NA, aes(xmin = start_time2, xmax = stop_time2, ymin = -0.5, ymax = 0.5)) +
scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) +
facet_grid(channel2 ~ .) +
theme(axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank()) +
theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) +
scale_fill_manual(values=c("#F8766D", "#871282", "#ffe119", "#0072B2", "#00C094", "#00A9FF", "#C77CFF"))
#dataset 3
channel3 <- c("Channel 3", "Channel 3", "Channel 3", "Channel 3", "Channel 3", "Channel 3")
start_time3 <- c(0, 3, 6, 9, 15,18)
stop_time3 <- c(3, 6, 9, 12, 17, 20)
character3 <- c("A", "D", "C", "G", "B", "F")
df3 <- data.frame(channel3, start_time3, stop_time3, character3)
#dataset3 plot
plot3 <- ggplot(df3, aes(fill = character3, color = character3)) +
geom_rect(color = NA, aes(xmin = start_time3, xmax = stop_time3, ymin = -0.5, ymax = 0.5)) +
scale_x_continuous(name = "Time (sec)", breaks = scales::pretty_breaks(n = 10)) +
facet_grid(channel3 ~ .) +
theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) + theme(legend.position="bottom") +
scale_fill_manual(values=c("#F8766D", "#871282", "#ffe119", "#0072B2", "#00C094", "#00A9FF", "#C77CFF"))
#dataset 4
ep_Channel <- c("Episode", "Episode", "Episode", "Episode")
ep_start_time <- c(0, 5, 10, 15)
ep_stop_time <- c(5, 10, 15, 20)
Episode <- c("ep1", "ep2", "ep3", "ep4")
ep_df <- data.frame(ep_Channel, ep_start_time, ep_stop_time, Episode)
#dataset4 plot
plot4 <- ggplot(ep_df, aes(fill = Episode, color = Episode)) +
geom_rect(color = NA, aes(xmin = ep_start_time, xmax = ep_stop_time, ymin = -0.5, ymax = 0.5)) +
scale_x_continuous(name = "Time (sec)", breaks = scales::pretty_breaks(n = 10)) +
facet_grid(ep_Channel ~ .) +
theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) + theme(legend.position="bottom") +
scale_fill_manual(values=c("#F8F8F8", "#F5F5F5", "#F0F0F0", "#E8E8E8"))
#combine plots
library(patchwork)
(plot1 / plot2 / plot3 / plot4) +
plot_annotation(title = 'TITLE GOES HERE', theme = theme(plot.title = element_text(hjust = 0.5))) +
plot_layout(guides = "collect") & theme(legend.position = 'bottom')
Plot:
To merge the three legends when the categories differ use the limits=
argument to display all categories in each legend and set the same name=
for each legend. Doing so you also ensure that each letter is always assigned the same color in each plot, which is not the case in your original code. Additionally, to avoid duplicating the code for each plot I use a plotting function, which could be simplified even further if all the datasets shared the same column names.
UPDATE Due to a change in the guide system introduced with ggplot2 3.5.0
one now has to add show.legend = TRUE
to the geom_rect
s to ensure that a key is displayed for missing categories and to ensure that the legends get merged.
library(patchwork)
library(ggplot2)
# Plotting function
plot_fun <- function(.data, i = 1) {
fill <- color <- paste0("character", i)
xmin <- paste0("start_time", i)
xmax <- paste0("stop_time", i)
facet <- paste0("channel", i)
drop_x <- if (i %in% 1:2) {
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank()
)
}
ggplot(.data, aes(fill = .data[[fill]], color = .data[[color]])) +
geom_rect(
color = NA,
aes(xmin = .data[[xmin]], xmax = .data[[xmax]], ymin = -0.5, ymax = 0.5),
show.legend = TRUE
) +
scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) +
scale_fill_manual(
name = "Character",
values = c(
"#F8766D", "#871282", "#ffe119", "#0072B2",
"#00C094", "#00A9FF", "#C77CFF"
),
limits = LETTERS[1:7]
) +
facet_grid(rows = vars(.data[[facet]])) +
theme(
axis.text.y = element_blank(),
axis.ticks.y = element_blank()
) +
drop_x
}
# plots
plot1 <- plot_fun(df1, 1)
plot2 <- plot_fun(df2, 2)
plot3 <- plot_fun(df3, 3)
# dataset4 plot
plot4 <- ggplot(ep_df, aes(fill = Episode, color = Episode)) +
geom_rect(
color = NA,
aes(xmin = ep_start_time, xmax = ep_stop_time, ymin = -0.5, ymax = 0.5),
show.legend = TRUE
) +
scale_x_continuous(name = "Time (sec)", breaks = scales::pretty_breaks(n = 10)) +
facet_grid(ep_Channel ~ .) +
theme(
axis.text.y = element_blank(),
axis.ticks.y = element_blank()
) +
theme(legend.position = "bottom") +
scale_fill_manual(values = c("#F8F8F8", "#F5F5F5", "#F0F0F0", "#E8E8E8"))
# combine plots
(plot1 / plot2 / plot3 / plot4) +
plot_annotation(title = "TITLE GOES HERE", theme = theme(plot.title = element_text(hjust = 0.5))) +
plot_layout(guides = "collect") &
theme(legend.position = "bottom")