如何在ggplot中设置Y轴时间范围为19:00至07:00并修正平滑线
问题:调整ggplot Y轴时间范围(19:00-次日07:00)并生成合理平滑线
之前我提问过ggplot中Y轴使用小时格式的问题,现在需要将Y轴时间范围设置为19:00至次日07:00,且从19:00开始显示——所有distress变量的时间都落在这个区间内。当前图表Y轴是00:00:00到23:59:00,生成的平滑线处于下午时段,完全不符合逻辑。以下是我使用的代码和两位参与者的示例数据,求问如何设置指定Y轴时间范围并得到符合逻辑的平滑线?
原代码
#Convert to hours format (do it after removing missings because it makes issues with missing values) data_clean$distress_time <- as_hms(paste0(data_clean$distress, ':00')) #Create plot: lines<-ggplot(data = data_clean, aes(x=survey,y=distress_time, group=id, color=id))+ facet_wrap(~PP_rec)+ geom_line()+ xlab("Survey")+ylab("Distress")+ theme_bw() lines #a smoother for the overall group lines_smooth <- lines + geom_smooth(aes(group = 1), se=FALSE, colour="black", size=1) lines_smooth #adjust plot: smooth_lines2 <- lines_smooth + labs(x = "Survey", y = "Distress", color = "id") + scale_y_time(limits = as.hms(c('00:00:00', '23:59:00')), breaks = hms(hours = seq(0, 23.99, 1))) + scale_x_continuous(breaks = seq(0, 16, 2)) + theme(text = element_text(size = 10)) smooth_lines2
示例数据
structure(list(id = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L), levels = c("400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "411", "412", "413", "414", "415", "550", "551", "552", "553", "554", "555", "558", "559", "560", "561", "563", "565", "566", "567", "568", "569", "570", "571", "572", "573", "574", "575", "576", "577", "578", "580", "590", "591"), class = "factor"), distress = c("23:45", "23:30", "23:30", "23:45", "23:30", "22:30", "23:30", "21:30", "23:45", "22:45", "0:00", "1:00", "6:15", "0:00", "1:00", "0:00", "2:00", "2:00", "2:00", "1:45" ), survey = c(1, 4, 6, 9, 11, 1, 3, 5, 8, 11, 1, 4, 7, 9, 12, 1, 4, 7, 10, 12)))
注:PP_rec用于区分事件发生的前后时段
解决方案
核心问题是hms类型的时间是线性的0-23小时区间,直接设置19:00到07:00的范围会因为07<19导致ggplot无法识别跨天的连续区间,进而导致平滑线计算逻辑错误。我们可以将时间转换为以19:00为起点的偏移小时数,把跨天区间转换成连续的0-12小时数值轴,再自定义轴标签还原时间格式。
步骤1:数据预处理
library(tidyverse) library(hms) # 加载示例数据并补充PP_rec列(原数据缺失,用于测试) data_clean <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L, 9L), levels = c("400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "411", "412", "413", "414", "415", "550", "551", "552", "553", "554", "555", "558", "559", "560", "561", "563", "565", "566", "567", "568", "569", "570", "571", "572", "573", "574", "575", "576", "577", "578", "580", "590", "591"), class = "factor"), distress = c("23:45", "23:30", "23:30", "23:45", "23:30", "22:30", "23:30", "21:30", "23:45", "22:45", "0:00", "1:00", "6:15", "0:00", "1:00", "0:00", "2:00", "2:00", "2:00", "1:45" ), survey = c(1, 4, 6, 9, 11, 1, 3, 5, 8, 11, 1, 4, 7, 9, 12, 1, 4, 7, 10, 12)), row.names = c(NA, -20L), class = "data.frame") data_clean$PP_rec <- sample(c("事件前", "事件后"), nrow(data_clean), replace = TRUE) # 转换时间为19:00起的偏移小时数 data_clean <- data_clean %>% mutate( distress_hms = as_hms(paste0(distress, ":00")), # 计算与19:00的时间差,负数则加24小时(对应次日时间) distress_offset = as.numeric(difftime(distress_hms, as_hms("19:00:00"), units = "hours")), distress_offset = ifelse(distress_offset < 0, distress_offset + 24, distress_offset) )
步骤2:绘制图表并自定义Y轴
# 定义Y轴刻度和对应时间标签 y_breaks <- seq(0, 12, 1) y_labels <- c( "19:00", "20:00", "21:00", "22:00", "23:00", "00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00" ) # 生成最终图表 smooth_lines_final <- ggplot(data = data_clean, aes(x = survey, y = distress_offset, group = id, color = id)) + facet_wrap(~PP_rec) + geom_line() + geom_smooth(aes(group = 1), se = FALSE, colour = "black", size = 1) + labs(x = "Survey", y = "Distress", color = "id") + scale_y_continuous( limits = c(0, 12), breaks = y_breaks, labels = y_labels ) + scale_x_continuous(breaks = seq(0, 16, 2)) + theme_bw() + theme(text = element_text(size = 10)) smooth_lines_final
效果说明
- 转换后的
distress_offset是连续数值:19:00对应0,23:00对应4,00:00对应5,07:00对应12,完美适配跨天区间的逻辑顺序。 - 平滑线基于连续的数值轴计算,结果符合数据的时间分布逻辑。
- Y轴标签手动替换为对应时间,视觉效果和直接使用时间轴一致。
内容的提问来源于stack exchange,提问作者o.y_80
相关产品推荐
相关产品推荐

