ggplot:添加不同数据框数据为额外分面网格并分离图例
问题描述
现有以下R数据与代码,用于生成分面的时间轴色块图:
# 第一个数据集 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")
该代码生成的分面网格中,每个Channel对应一行,展示不同角色的时间色块。
现在需要添加一行标签为“Episode”的分面,对应第二个数据集:
# 第二个数据集 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)
最初尝试合并两个数据框,给df2的channel统一设为“Episode”,但生成的图表中Episode和Character的图例被混在一起,需要让二者拥有独立的图例。
解决方案
核心思路是不合并数据框,分别为两个数据集调用geom_rect,使用不同的美学映射区分两类色块,再手动配置独立图例。
具体代码如下:
library(ggplot2) library(scales) library(dplyr) # 预处理第二个数据集:统一列名并添加channel标识 df2 <- df2 %>% rename(start_time = start_time_ep, stop_time = stop_time_ep) %>% mutate(channel = "Episode") # 为两类元素分配独立色系,避免视觉混淆 char_colors <- c("A" = "#1f77b4", "B" = "#ff7f0e", "C" = "#2ca02c") ep_colors <- c("episode 1" = "#9467bd", "episode 2" = "#8c564b", "episode 3" = "#e377c2", "episode 4" = "#7f7f7f") ggplot() + # 绘制Channel角色色块,使用fill映射 geom_rect(data = df1, aes(xmin = start_time, xmax = stop_time, ymin = -0.5, ymax = 0.5, fill = character), color = NA) + # 绘制Episode色块,使用自定义的fill2映射 geom_rect(data = df2, aes(xmin = start_time, xmax = stop_time, ymin = -0.5, ymax = 0.5, fill2 = episode), color = NA) + # 配置x轴 scale_x_continuous(name = "Time (sec)", breaks = pretty_breaks(n = 10)) + # 分别配置两个图例的名称与颜色 scale_fill_manual(name = "Character", values = char_colors) + scale_fill_manual(name = "Episode", values = ep_colors, aesthetics = "fill2") + # 生成包含所有分面的网格 facet_grid(channel ~ .) + # 调整主题样式 theme(axis.text.y = element_blank(), axis.ticks.y = element_blank(), legend.position = "bottom", legend.box = "horizontal") # 让两个图例横向排列
代码说明
- 数据预处理:为df2统一时间列名并添加
channel字段,确保分面逻辑一致。 - 独立美学映射:用
fill对应角色、fill2对应剧集,从根源避免图例混淆。 - 自定义色系:为两类元素分配不同色系,提升视觉区分度。
- 图例配置:通过
scale_fill_manual分别定义两个图例的名称和颜色,再用legend.box让图例横向排列,优化布局。
内容的提问来源于stack exchange,提问作者Wangana
相关产品推荐
相关产品推荐

