I have the following data and code:
#first data set
channel <- c("Channel 1", "Channel 1", "Channel 1", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 3", "Channel 3", "Channel 3")
start_time <- c(0.000, 9.719, 11.735, 14.183, 16.554, 18.482, 0.000, 11.693, 12.310, 13.912)
stop_time <- c(9.719, 11.735, 14.183, 16.554, 18.482, 19.553, 11.693, 12.310, 13.912, 15.406)
character <- c("A", "B", "C", "C", "B", "A", "A", "B", "C", "B")
df1 <- data.frame(channel, start_time, stop_time, character)
library(ggplot2)
ggplot(df1, aes(fill = character, color = character)) +
geom_rect(color = NA, aes(xmin = start_time, xmax = stop_time, ymin = -0.5, ymax = 0.5)) +
scale_x_continuous(name = "Time (sec)", breaks = scales::pretty_breaks(n = 10)) +
facet_grid(channel ~ .) +
theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) + theme(legend.position="bottom")
This produces a plot that looks like this:
I would like to add, another row (facet?) to the grid. The data for that row looks like this:
#second data set
start_time_ep <- c(0, 5, 10, 15)
stop_time_ep <- c(5, 10, 15, 20)
episode <- c("episode 1", "episode 2", "episode 3", "episode 4")
df2 <- data.frame(start_time_ep, stop_time_ep, episode)
I want this data to be its own row with the label "Episode", it would likewise show a set of coloured blocks.
At, first, I tried just integrating this data into the first data frame, and giving them all the same "channel" vector value, as follows:
#trying all together
channel <- c("Channel 1", "Channel 1", "Channel 1", "Channel 2", "Channel 2", "Channel 2", "Channel 2", "Channel 3", "Channel 3", "Channel 3", "Episode", "Episode", "Episode", "Episode")
start_time <- c(0.000, 9.719, 11.735, 14.183, 16.554, 18.482, 0.000, 11.693, 12.310, 13.912, 0, 5, 10, 15)
stop_time <- c(9.719, 11.735, 14.183, 16.554, 18.482, 19.553, 11.693, 12.310, 13.912, 15.406, 5, 10, 15, 20)
character <- c("A", "B", "C", "C", "B", "A", "A", "B", "C", "B", "episode 1", "episode 2", "episode 3", "episode 4")
df1 <- data.frame(channel, start_time, stop_time, character)
library(ggplot2)
ggplot(df1, aes(fill = character, color = character)) +
geom_rect(color = NA, aes(xmin = start_time, xmax = stop_time, ymin = -0.5, ymax = 0.5)) +
scale_x_continuous(name = "Time (sec)", breaks = scales::pretty_breaks(n = 10)) +
facet_grid(channel ~ .) +
theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank() ) + theme(legend.position="bottom")
This produces the following plot:
This is kind of what I want, but I don't like that it treats the "episode" values the same as the "character" values. I want them in their own legends.
My own reserach suggests using either geom_step
or geom_line
, specifiying different data frames for each lines data argument. (Cf. this post). But I cannot, for the life of me, get the syntax right with a facet_grid
style plot like I'm shooting for here.
What is the the best way to do this?
If you want to avoid other packages, you could use a combination of geom_linerange
with a colour aesthetic and geom_rect
with a fill aesthetic to get two separate legends:
df1$episode <- NA
df3 <- rbind(df1, cbind(channel = "Episode", df2[1:2], char = NA, df2[3]) |>
setNames(names(df1)))
df3 <- within(df3, channel <- factor(channel, rev(unique(channel))))
ggplot(df3, aes(y = channel)) +
geom_blank() +
geom_linerange(aes(xmin = start_time, xmax = stop_time,
color = character),
data = subset(df3, !is.na(character)), linewidth = 18) +
geom_rect(aes(xmin = start_time, xmax = stop_time,
ymin = 0.8, ymax = 1.2,
fill = episode),
data = subset(df3, is.na(character))) +
scale_x_continuous(name = "Time (sec)",
breaks = scales::pretty_breaks(n = 10)) +
scale_fill_manual(values = c( "yellow", "orange", "red", "purple")) +
guides(color = guide_legend(override.aes = list(linewidth = 7),
title.position = "top", direction = "vertical"),
fill = guide_legend(title.position = "top", direction = "vertical")) +
theme_minimal(base_size = 16) +
theme(legend.position = "bottom")