如何为含288条观测值的用户DataFrame绘制多时间序列子图?
嘿,这个需求我经常碰到!每个用户288条观测(大概率是一天按5分钟采样一次对吧?),要画多子图时间序列的话,有几个非常实用的方法,我给你详细讲讲:
方法一:用Matplotlib手动创建子图
这种方法适合你需要完全控制子图布局的场景,比如自定义每个子图的大小、颜色、刻度细节。
先给你整个模拟数据,方便你直接跑代码测试:
import pandas as pd import numpy as np import matplotlib.pyplot as plt # 模拟数据:5个用户,每个288条5分钟间隔的观测 np.random.seed(42) users = [f'user_{i}' for i in range(5)] timestamps = pd.date_range(start='2024-01-01', periods=288, freq='5min') data = [] for user in users: # 模拟带趋势的时间序列 values = np.random.randn(288).cumsum() data.extend([(user, ts, val) for ts, val in zip(timestamps, values)]) df = pd.DataFrame(data, columns=['user_id', 'timestamp', 'value'])
然后是绘制多子图的代码:
# 获取所有唯一用户ID unique_users = df['user_id'].unique() n_users = len(unique_users) # 计算子图的行列数(比如每行放3个,行数向上取整) n_cols = 3 n_rows = (n_users + n_cols - 1) // n_cols # 创建画布和子图,设置整体大小 fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, n_rows*4)) # 把二维的子图数组转成一维,方便循环遍历 axes = axes.flatten() # 循环每个用户绘制时间序列 for i, user in enumerate(unique_users): user_data = df[df['user_id'] == user] axes[i].plot(user_data['timestamp'], user_data['value'], linewidth=1.5) axes[i].set_title(f'Time Series: {user}', fontsize=10) # 旋转x轴标签避免重叠 axes[i].tick_params(axis='x', rotation=45, labelsize=8) axes[i].set_xlabel('Timestamp', fontsize=8) axes[i].set_ylabel('Value', fontsize=8) # 如果用户数不是行列数的整数倍,隐藏多余的空图 for j in range(i+1, n_rows*n_cols): axes[j].axis('off') # 自动调整子图间距,避免标题和标签重叠 plt.tight_layout() plt.show()
方法二:用Seaborn的FacetGrid自动生成子图
Seaborn的FacetGrid是处理这种分面绘图的神器,代码更简洁,自动帮你搞定布局,适合快速出图。
import seaborn as sns # 设置全局绘图风格 sns.set_style('whitegrid') # 创建分面网格,按user_id拆分,每行最多3个子图 g = sns.FacetGrid(df, col='user_id', col_wrap=3, height=4, aspect=1.2) # 映射折线图到每个子图 g.map(sns.lineplot, 'timestamp', 'value', linewidth=1.5) # 设置子图标题和轴标签 g.set_titles('{col_name}', fontsize=10) g.set_xlabels('Timestamp', fontsize=8) g.set_ylabels('Value', fontsize=8) # 旋转x轴标签,避免拥挤 for ax in g.axes.flat: ax.tick_params(axis='x', rotation=45, labelsize=8) plt.tight_layout() plt.show()
方法三:用Plotly做交互式子图(适合需要交互的场景)
如果想要交互式的图表——比如缩放查看细节、悬停查看具体数值,Plotly是个绝佳选择:
import plotly.subplots as sp import plotly.graph_objects as go n_cols = 3 n_rows = (n_users + n_cols - 1) // n_cols # 创建子图布局,设置每个子图的标题 fig = sp.make_subplots(rows=n_rows, cols=n_cols, subplot_titles=unique_users) # 循环添加每个用户的时间序列轨迹 for i, user in enumerate(unique_users): row = (i // n_cols) + 1 col = (i % n_cols) + 1 user_data = df[df['user_id'] == user] fig.add_trace( go.Scatter(x=user_data['timestamp'], y=user_data['value'], name=user), row=row, col=col ) # 调整整体布局大小和标题 fig.update_layout(height=n_rows*400, width=1500, title_text="User Time Series Dashboard") # 旋转x轴标签,优化显示 fig.update_xaxes(tickangle=45) # 生成交互式图表 fig.show()
几个小提示
- 如果用户数量特别多(比如几十上百个),别硬画所有子图——太挤根本看不清。可以抽样几个典型用户,或者按用户行为分组绘图,甚至换用热力图这类的可视化方式会更合适。
- 288条观测是天级的5分钟数据,你可以把x轴刻度改成小时格式,让图表更直观,比如在Matplotlib里加
plt.gcf().autofmt_xdate()自动优化日期显示。
内容的提问来源于stack exchange,提问作者Zalan
相关产品推荐
相关产品推荐

