如何将睡眠时长条形图X轴改为时钟时间(跨午夜适配)
跨午夜睡眠时段条形图绘制解决方案
核心思路
针对跨午夜睡眠记录(如22:00-次日4:30)的显示问题,核心是拆分跨时段记录:将一条跨午夜的睡眠数据拆分为「当日起始时间到24:00」和「00:00到次日结束时间」两段,再按常规方式绘制条形图,彻底避免顺序颠倒问题;同时直接将X轴设置为0-24小时时钟格式。
步骤1:数据预处理
遍历每条睡眠记录,判断是否跨午夜(起始时间晚于结束时间),拆分跨时段记录:
- 跨午夜记录:拆分为两段独立的睡眠时段
- 非跨午夜记录:保持原数据结构不变
步骤2:绘图实现(两种可选方案)
方案一:Matplotlib 绘制(灵活可控)
import matplotlib.pyplot as plt import pandas as pd # 示例数据集 data = pd.DataFrame({ 'user_id': ['user001', 'user002', 'user003'], 'start': ['22:00', '23:15', '21:30'], 'stop': ['04:30', '07:00', '06:10'] }) # 定义数据预处理函数 def split_cross_midnight_records(df): processed_list = [] for _, row in df.iterrows(): start_time = pd.to_datetime(row['start']).time() stop_time = pd.to_datetime(row['stop']).time() if start_time > stop_time: # 拆分跨午夜记录 # 第一段:当日start到24:00 processed_list.append({ 'user_id': row['user_id'], 'start_min': start_time.hour*60 + start_time.minute, 'duration': (pd.Timedelta('24:00') - pd.Timedelta(str(start_time))).seconds // 60 }) # 第二段:00:00到次日stop processed_list.append({ 'user_id': row['user_id'], 'start_min': 0, 'duration': pd.Timedelta(str(stop_time)).seconds // 60 }) else: # 非跨午夜记录直接处理 duration = (pd.Timedelta(str(stop_time)) - pd.Timedelta(str(start_time))).seconds // 60 processed_list.append({ 'user_id': row['user_id'], 'start_min': start_time.hour*60 + start_time.minute, 'duration': duration }) return pd.DataFrame(processed_list) # 处理数据 processed_data = split_cross_midnight_records(data) # 绘图配置 plt.figure(figsize=(14, 7)) users = sorted(processed_data['user_id'].unique()) user_y_pos = {user: idx for idx, user in enumerate(users)} # 绘制每个睡眠时段的条形 for _, row in processed_data.iterrows(): plt.barh(user_y_pos[row['user_id']], row['duration'], left=row['start_min'], height=0.7, alpha=0.8) # 设置X轴为24小时时钟格式 plt.xticks(ticks=[i*60 for i in range(25)], labels=[f"{i:02d}:00" for i in range(25)], fontsize=10) plt.yticks(ticks=range(len(users)), labels=users, fontsize=10) plt.xlabel('时间(24小时制)', fontsize=12) plt.ylabel('用户ID', fontsize=12) plt.title('各用户睡眠时段分布', fontsize=14) plt.grid(axis='x', linestyle='--', alpha=0.6) plt.tight_layout() plt.show()
方案二:Plotly 绘制(交互式图表)
如果需要交互式图表,同样基于预处理后的数据,用px.bar替代timetable:
import plotly.express as px # 复用预处理后的processed_data,添加时间字符串用于hover提示 processed_data['start_str'] = pd.to_datetime(processed_data['start_min'], unit='m').dt.strftime('%H:%M') # 绘制交互式条形图 fig = px.bar(processed_data, y='user_id', x='duration', orientation='h', color='user_id', hover_data={'start_str': True, 'duration': False}, title='各用户睡眠时段分布') # 设置X轴为24小时时钟格式 fig.update_layout( xaxis=dict( tickmode='array', tickvals=[i*60 for i in range(25)], ticktext=[f"{i:02d}:00" for i in range(25)], title='时间(24小时制)' ), yaxis_title='用户ID' ) # 设置条形的起始位置(对应X轴的分钟数) fig.update_traces(base=processed_data['start_min']) fig.show()
方案优势
- 彻底解决跨午夜时段的顺序颠倒问题,睡眠时段在X轴上显示准确
- X轴直接显示0-24小时时钟时间,无需转换为距午夜分钟数
- 支持Matplotlib(静态)和Plotly(交互式)两种场景,可根据需求选择
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

