如何将编码时长的broken_barh图表转换为折线图或热力图?
问题描述
我有一份编码时长数据,以字典形式存储:键是日期,值是包含编码开始时间(秒数,从0点开始算)及时长的元组列表。已经用matplotlib的broken_barh绘制了断裂水平条形图,但没法直观看到一天中哪个时段的编码天数(对应时段的会话数量)最多,想转换成折线图或热力图,Y轴为对应时段的编码天数,该怎么实现?
原条形图代码:
for i,subSessions in enumerate(sessions.values()): plt.broken_barh(subSessions, (i,1)) months = {} start = getStartMonth() for month in period_range(start=start,end=datetime.today(),freq="M"): month = str(month) months[month] = (datetime.strptime(month,'%Y-%m')-start).days plt.yticks(list(months.values()),months.keys()) plt.xticks(range(0,24*3600,3600),[str(i)+":00" for i in range(24)],rotation=45) plt.gca().invert_yaxis() plt.show()
方案1:折线图(统计每小时编码天数)
核心逻辑是统计一天中每个小时区间内,有多少天存在编码会话(避免同一天多次会话重复计数),再用折线图展示分布。
实现代码
import matplotlib.pyplot as plt import numpy as np # 初始化24小时的计数数组,每个位置对应小时的编码天数 hour_counts = np.zeros(24, dtype=int) # 遍历所有日期的会话数据 for date, subsessions in sessions.items(): # 标记当天涉及的编码小时 daily_encoded_hours = set() for start_sec, duration in subsessions: # 计算会话覆盖的小时范围 start_hour = int(start_sec // 3600) end_hour = int((start_sec + duration) // 3600) # 把会话涉及的所有小时加入集合 for hour in range(start_hour, min(end_hour + 1, 24)): daily_encoded_hours.add(hour) # 更新对应小时的计数 for hour in daily_encoded_hours: hour_counts[hour] += 1 # 绘制折线图 plt.figure(figsize=(12, 6)) hours = np.arange(24) plt.plot(hours, hour_counts, marker='o', linestyle='-', linewidth=2) plt.xticks(hours, [f"{h}:00" for h in hours], rotation=45) plt.xlabel("一天中的时段") plt.ylabel("编码天数") plt.title("各时段编码天数统计") plt.grid(axis='y', alpha=0.3) plt.tight_layout() plt.show()
如果需要统计会话总次数而非编码天数,去掉daily_encoded_hours集合,直接在遍历会话时对涉及小时累加计数即可。
方案2:热力图(日期×时段的精细统计)
热力图能直观展示每日各时段的编码分布,颜色深浅可代表编码时长或是否有编码,适合观察长期时段规律。
实现代码
import matplotlib.pyplot as plt import numpy as np # 对日期排序,保证时间顺序 sorted_dates = sorted(sessions.keys()) num_days = len(sorted_dates) # 创建24行(小时)×N列(天数)的矩阵,存储各时段编码时长 heatmap_data = np.zeros((24, num_days)) for day_idx, date in enumerate(sorted_dates): subsessions = sessions[date] for start_sec, duration in subsessions: start_hour = int(start_sec // 3600) end_sec = start_sec + duration end_hour = int(end_sec // 3600) # 计算会话在每个覆盖小时内的实际时长,填充到矩阵 for hour in range(start_hour, min(end_hour + 1, 24)): hour_start = hour * 3600 hour_end = (hour + 1) * 3600 overlap_start = max(start_sec, hour_start) overlap_end = min(end_sec, hour_end) heatmap_data[hour, day_idx] += overlap_end - overlap_start # 绘制热力图 plt.figure(figsize=(15, 8)) im = plt.imshow(heatmap_data, cmap='YlOrRd', aspect='auto') plt.yticks(np.arange(24), [f"{h}:00" for h in range(24)]) # 日期过多时,只显示每月第一天的日期 plt.xticks(np.arange(0, num_days, 30), sorted_dates[::30], rotation=45) plt.xlabel("日期") plt.ylabel("一天中的时段") plt.title("每日各时段编码时长热力图") plt.colorbar(im, label="编码时长(秒)") plt.tight_layout() plt.show()
如果只需要标记是否有编码,把heatmap_data[hour, day_idx] += overlap_end - overlap_start改成heatmap_data[hour, day_idx] = 1即可。
内容的提问来源于stack exchange,提问作者hopperelec
相关产品推荐
相关产品推荐

