求助:使用ggplot绘制跨6am-次日6am的多事件时间序列堆叠条形图
问题:创建跨日夜的事件时长堆叠条形图
需要生成堆叠条形图,展示每日3个事件的持续时长:
- X轴为日期
- Y轴时间范围设定为当日6:00至次日6:00
- 用堆叠形式区分三个事件的时长
提供的数据如下:
TS <- data.frame(Date = c("01/07/1985", "02/07/1985", "03/07/1985", "04/07/1985", "05/07/1985", "06/07/1985", "07/07/1985", "08/07/1985", "09/07/1985", "10/07/1985", "11/07/1985", "12/07/1985", "13/07/1985", "14/07/1985", "15/07/1985", "16/07/1985", "17/07/1985", "18/07/1985", "19/07/1985", "20/07/1985", "21/07/1985"), Event1_start = c("05:46", "05:45", "05:45", "05:44", "05:44", "05:43", "05:43", "05:42", "05:47", "05:47", "05:46", "05:46", "05:45", "05:45", "05:44", "05:44", "05:43", "05:43", "05:42", "05:47", "05:47"), Event1_end = c("18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:32", "18:32", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:32", "18:32", "18:33", "18:33"), Event2_start = c("19:08", "19:59", "20:52", "21:49", "22:48", "23:50", "00:52", "04:39", "05:39", "06:39", "07:39", "08:39", "09:39", "10:39", "11:39", "12:39", "13:39", "14:39", "15:39", "16:39", "17:29"), Event2_end = c("07:27", "08:12", "09:00", "09:52", "10:49", "11:49", "12:51", "17:17", "18:17", "19:17", "20:17", "21:17", "22:17", "23:17", "00:17", "01:17", "02:17", "03:17", "04:17", "05:17", "06:00"), Event3_start = c("18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:32", "18:32", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33", "18:33"), Event3_end = c("19:08", "19:59", "20:52", "21:49", "22:48", "23:50", "00:52", "01:52", "05:47", "05:46", "05:46", "05:45", "05:45", "05:44", "05:44", "05:43", "05:43", "05:42", "05:42", "05:42", "05:42"))
之前尝试的代码无法正常运行:
L1<- TS %>% gather(variable, value, 2:7) %>% mutate (variable = factor(variable, levels = c("Event1_start","Event1_end","Event2_start","Event2_end","Event3_start","Event3_end"))) %>% ggplot(aes(x = Date, y = value, fill = variable)) + scale_y_continuous(breaks = c(6,12,18,0,6)) + geom_bar(stat = "identity", width = 0.95) + scale_fill_manual(values = c("#c7e6ff","black","#f5eea6"), labels = c("Event1","Event2", "Event3"))
解决方案
1. 数据预处理:转换时间为数值并计算时长
首先需要把时间字符串转换为小时数值(如05:46转为5+46/60≈5.77),同时处理跨天的情况(结束时间在次日的,数值加24)。然后计算每个事件的有效时长(只统计当日6:00到次日6:00范围内的部分)。
library(tidyverse) # 定义时间转小时的函数 time_to_hour <- function(time_str) { hms::parse_hm(time_str) %>% period_to_seconds() %>% divide_by(3600) } # 处理数据,计算每个事件的时长(限定在6:00-次日6:00即6-30小时) processed_data <- TS %>% mutate( # 转换所有时间为小时数值 across(ends_with(c("_start", "_end")), time_to_hour), # 处理跨天的结束时间(加24) Event2_end = ifelse(Event2_end < Event2_start, Event2_end + 24, Event2_end), Event3_end = ifelse(Event3_end < Event3_start, Event3_end + 24, Event3_end), # 计算每个事件在6-30小时范围内的时长 Event1_duration = pmax(0, pmin(Event1_end, 30) - pmax(Event1_start, 6)), Event2_duration = pmax(0, pmin(Event2_end, 30) - pmax(Event2_start, 6)), Event3_duration = pmax(0, pmin(Event3_end, 30) - pmax(Event3_start, 6)) ) %>% # 保留需要的列并转为长格式 select(Date, Event1_duration, Event2_duration, Event3_duration) %>% pivot_longer(cols = -Date, names_to = "Event", values_to = "Duration") %>% mutate(Event = str_remove(Event, "_duration"))
2. 绘制堆叠条形图
使用geom_col(适合已知数值的条形图),设置Y轴范围为6到30,将刻度标签转换为时间格式(30对应次日6:00):
ggplot(processed_data, aes(x = Date, y = Duration, fill = Event)) + geom_col(width = 0.95) + # 设置Y轴:范围6-30,刻度对应时间 scale_y_continuous( limits = c(6, 30), breaks = c(6, 12, 18, 24, 30), labels = c("6:00", "12:00", "18:00", "24:00", "次日6:00") ) + # 设置填充颜色 scale_fill_manual( values = c("#c7e6ff", "#f5eea6", "#ffcccc"), labels = c("Event1", "Event2", "Event3") ) + # 调整主题和标签 labs( x = "日期", y = "时间", fill = "事件" ) + theme(axis.text.x = element_text(angle = 45, hjust = 1))
关键说明
- 原代码的问题:直接用字符串格式的时间作为Y轴数值,ggplot无法识别;
gather的方式没有正确计算事件时长,而是把起止时间都作为了条形的高度。 - 跨天处理:将次日的时间数值加24,统一到6-30的范围内,方便Y轴展示。
- 有效时长计算:只统计每个事件落在当日6:00到次日6:00之间的部分,避免超出范围的时长被计入。
内容的提问来源于stack exchange,提问作者RGR_288
相关产品推荐
相关产品推荐

