You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将睡眠时长条形图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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 12:15:36