如何在ggplot的geom_density_ridges中按ID分组可视化堆叠密度曲线?
如何在密度脊线图中按分组堆叠填充颜色?
我已经绘制了展示一周每日时间模式的密度脊线图,想要在每天的曲线内按ID列(取值如True/False或A/B)分组,用两种颜色堆叠显示,明确区分不同组对峰值的贡献,但设置color = ID, fill = ID无效,请问该怎么实现?
现有代码:
ggplot(aes(x = weekdaytime, y = weekday, fill = weekday, group = weekday, height = ..count..)) + geom_density_ridges_gradient(stat = "density", scale = 2, rel_min_height = 0.01, bw = 300 ) + scale_fill_viridis(name = "", option = "C") + scale_x_datetime(date_breaks = "2 hour", date_labels = "%H", limits = as.POSIXct(strptime(c(paste(Sys.Date(), "00:00", sep=" "), paste(Sys.Date(), "24:00", sep=" ")), format = "%Y-%m-%d %H:%M"))) + scale_y_discrete(limits=c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))+ theme( legend.position="none", panel.spacing = unit(0.1, "lines"), panel.grid.major = element_line(colour = "grey"), panel.grid.minor = element_line(colour = "grey"), panel.background = element_blank(), strip.text.x = element_text(size = 7) )
解决方案
geom_density_ridges_gradient本身不支持堆叠填充逻辑,要实现分组堆叠的密度脊线,需要先手动计算各组密度并累加,再用支持堆叠的图层绘制:
1. 预处理数据:计算分组密度并累加
先按weekday和ID分组计算密度,统一x轴范围保证数据对齐,再对每个时间点的密度值按分组累加,得到堆叠的高度:
library(dplyr) library(purrr) library(ggplot2) library(ggridges) # 假设你的数据集名为df,包含weekdaytime、weekday、ID三列 dens_data <- df %>% group_by(weekday, ID) %>% nest() %>% # 计算密度,统一x轴的起止范围和断点数量 mutate( dens = map(data, ~density( as.numeric(.x$weekdaytime), from = as.numeric(as.POSIXct(paste(Sys.Date(), "00:00"))), to = as.numeric(as.POSIXct(paste(Sys.Date(), "24:00"))), n = 1000, bw = 300 # 和原代码保持一致的带宽 )), # 提取密度的x(时间)和y(密度值)并转换格式 dens_df = map(dens, ~tibble( x = as.POSIXct(.x$x, origin = "1970-01-01"), y = .x$y )) ) %>% select(-data, -dens) %>% unnest(dens_df) %>% ungroup() %>% # 按星期和时间点,对密度值按ID分组累加,得到堆叠高度 group_by(weekday, x) %>% arrange(ID) # 控制堆叠顺序,可根据需求调整 mutate(cum_y = cumsum(y)) %>% ungroup()
2. 绘制堆叠密度脊线图
用geom_ridgeline图层,基于预处理好的累加密度值绘制堆叠效果,填充色绑定ID:
ggplot(dens_data, aes(x = x, y = weekday, group = interaction(weekday, ID), height = cum_y, fill = ID)) + geom_ridgeline(scale = 2, rel_min_height = 0.01, alpha = 0.7) + # 自定义两种分组颜色,可替换为你需要的配色 scale_fill_manual(values = c("#2c3e50", "#e74c3c")) + scale_x_datetime( date_breaks = "2 hour", date_labels = "%H", limits = as.POSIXct(strptime(c(paste(Sys.Date(), "00:00"), paste(Sys.Date(), "24:00")), format = "%Y-%m-%d %H:%M")) ) + scale_y_discrete(limits = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")) + theme( panel.spacing = unit(0.1, "lines"), panel.grid.major = element_line(colour = "grey"), panel.grid.minor = element_line(colour = "grey"), panel.background = element_blank(), strip.text.x = element_text(size = 7) )
关键说明
- 必须手动计算密度:
geom_density_ridges_gradient的内置统计只会计算整体密度,无法按分组拆分并堆叠,因此需要提前处理数据。 - 统一密度参数:计算密度时的
from、to、n参数必须一致,确保不同组的时间点完全对齐,否则累加会出错。 - 分组交互:
interaction(weekday, ID)确保每个星期的每个分组都生成独立的脊线,避免图层重叠混乱。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

