如何用Matplotlib绘制RTTM时序事件的持续时间序列图?
问题描述
我有如下Pandas DataFrame:
0 music 0.00 9.02 1 female 9.02 152.70 2 music 152.70 155.12 3 female 155.12 206.82 4 noEnergy 206.82 208.10
该数据包含ID、TYPE、START、END字段,所有事件按顺序排列且无重叠。我的目标是绘制一张事件持续时间序列图表,让music类型在0-9.02区间连续展示,female类型在9.02-152.70区间连续展示,以此类推。
我尝试了两种方法都不符合需求:
- 散点图仅展示起始时间点:
# Read data from RTTM files into data frames import matplotlib.pyplot as plt import numpy as np import pandas as pd df1 = pd.read_csv('file.rttm', sep=' ', names=['type', 'start', 'end']) df1.plot(x='start', y='type', kind='scatter', rot='vertical') plt.show()
- 折线图仍不符合需求:
df1['duration'] = (df1['end'] - df1['start']) plt.plot(df1['start'], df1['type']) plt.show()
请问正确的可视化方法是什么?
解决方案
你需要的是时序分段的类别条形图(类似时间轴事件图),下面是两种可行的实现方案:
方案一:使用matplotlib的broken_barh(推荐)
broken_barh是matplotlib专门用于绘制水平分段条形的方法,完美匹配你的需求:
import pandas as pd import matplotlib.pyplot as plt # 读取数据,补充ID列命名 df1 = pd.read_csv('file.rttm', sep=' ', names=['id', 'type', 'start', 'end']) # 计算每个事件的持续时间 df1['duration'] = df1['end'] - df1['start'] # 给每个事件类型分配唯一的y轴位置,避免重叠 type_mapping = {t:i for i,t in enumerate(df1['type'].unique())} df1['y_pos'] = df1['type'].map(type_mapping) plt.figure(figsize=(12,4)) # 遍历每个事件绘制分段条形 for idx, row in df1.iterrows(): # 仅在第一个出现的类别添加图例,避免重复 plt.broken_barh([(row['start'], row['duration'])], (row['y_pos']-0.4, 0.8), label=row['type'] if idx==0 else "") # 设置坐标轴和图例 plt.yticks(list(type_mapping.values()), list(type_mapping.keys())) plt.xlabel('时间') plt.ylabel('事件类型') plt.title('事件时序持续图') plt.legend() plt.tight_layout() plt.show()
方案二:转换数据为连续点绘制折线+填充
如果想用折线图形式实现连续展示,可以将每个事件的start和end拆分为两个点,再用steps-post绘制连续折线:
import pandas as pd import matplotlib.pyplot as plt df1 = pd.read_csv('file.rttm', sep=' ', names=['id', 'type', 'start', 'end']) # 转换数据:每个事件拆成(start, type)和(end, type)两个点 plot_data = [] for _, row in df1.iterrows(): plot_data.append({'time': row['start'], 'type': row['type']}) plot_data.append({'time': row['end'], 'type': row['type']}) plot_df = pd.DataFrame(plot_data) # 将类别映射为数值,用于y轴绘制 type_mapping = {t:i for i,t in enumerate(df1['type'].unique())} plot_df['y_val'] = plot_df['type'].map(type_mapping) plt.figure(figsize=(12,4)) # 使用steps-post确保折线在时间区间内保持水平 plt.plot(plot_df['time'], plot_df['y_val'], drawstyle='steps-post', linewidth=3) # 可选:给每个类别区间添加填充色,增强可读性 for t in type_mapping: subset = plot_df[plot_df['type']==t] plt.fill_between(subset['time'], type_mapping[t]-0.3, type_mapping[t]+0.3, alpha=0.2) plt.yticks(list(type_mapping.values()), list(type_mapping.keys())) plt.xlabel('时间') plt.ylabel('事件类型') plt.title('事件时序持续图') plt.tight_layout() plt.show()
这两种方法都能实现你要的效果:每个事件类型在对应时间区间内连续展示,不会仅显示单个时间点。
内容的提问来源于stack exchange,提问作者user3925023
相关产品推荐
相关产品推荐

