如何用Seaborn绘制同时清晰展示极值与常规数据的折线图(消除数据间隙)
如何用Seaborn绘制同时清晰展示极值与常规数据的折线图(消除数据间隙)
我太懂你这个痛点了!当数据集里既有小范围波动的常规数据(比如员工销售额),又有高很多数量级的极值(比如门店总销售额)时,普通折线图要么把常规数据挤成一条几乎看不清的线,要么没法完整展示极值,完全达不到分析的目的。
针对你这种中间存在无数据断层区间的情况,我给你两个实用的解决方案,都能完美兼顾极值展示和常规数据的细节:
方案一:断裂Y轴(推荐!直观展示数据断层)
这种方法把Y轴拆成上下两部分,分别对应常规数据和极值,中间用断裂标记明确提示读者这里有数据间隙,既保留了所有数据的趋势,又能让常规数据的波动清晰可见。
修改后的代码如下:
import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import numpy as np # Sample data data = { 'Date': ['2025-01-10', '2025-01-17', '2025-01-24', '2025-01-31'], 'Bob': [156, 60, 58, 62], 'Joe': [37, 40, 139, 42], 'Sally': [62, 265, 63, 67], 'Total': [3698, 3750, 3720, 3800] } # 创建DataFrame并处理格式 df = pd.DataFrame(data) df_melted = df.melt(id_vars='Date', var_name='Employee', value_name='Sales') df_melted['Date'] = pd.to_datetime(df_melted['Date']) # 创建上下两个子图,共享X轴 fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True) fig.subplots_adjust(hspace=0.05) # 缩小子图间隙,让整体更连贯 # 下轴:展示员工常规销售额(限定范围在0-350,覆盖你的常规数据区间) sns.lineplot(x='Date', y='Sales', hue='Employee', data=df_melted[df_melted['Employee'] != 'Total'], marker='o', ax=ax2) ax2.set_ylim(0, 350) ax2.set_ylabel('员工销售额') ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x):,}')) # 上轴:展示总销售额(限定范围在3500-4000,覆盖极值区间) total_line = sns.lineplot(x='Date', y='Sales', hue='Employee', data=df_melted[df_melted['Employee'] == 'Total'], marker='o', ax=ax1, color='red') ax1.set_ylim(3500, 4000) ax1.set_ylabel('总销售额') ax1.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x):,}')) ax1.get_legend().remove() # 上轴不需要重复图例 # 添加断裂标记(//),明确提示数据断层 d = 0.5 # 调整断裂线的倾斜角度 kwargs = dict(marker=[(-1, -d), (1, d)], markersize=12, linestyle="none", color='gray', mec='gray', mew=1, clip_on=False) ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs) ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs) # 设置整体标题和X轴标签 ax2.set_xlabel('日期') fig.suptitle('员工销售额与总销售额趋势对比', y=0.95) # 调整图例位置,避免遮挡数据 ax2.legend(loc='upper right') plt.show()
方案优势:
- 完全消除了无数据区间的空白,让常规数据的波动一目了然;
- 断裂标记清晰传达了数据量级的差异,不会误导读者;
- 适合你这种有明确数据断层的场景,尤其是后续扩展到多门店数据时,极值和个体数据的对比会更清晰。
方案二:双Y轴(适合观察数据关联)
如果你想在同一个坐标系里同时看常规数据和极值的趋势关联(比如总销售额的变化是否和员工销售波动有关),可以用双Y轴方案,左边对应常规数据,右边对应极值。
代码示例:
import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import numpy as np # Sample data data = { 'Date': ['2025-01-10', '2025-01-17', '2025-01-24', '2025-01-31'], 'Bob': [156, 60, 58, 62], 'Joe': [37, 40, 139, 42], 'Sally': [62, 265, 63, 67], 'Total': [3698, 3750, 3720, 3800] } # 数据预处理 df = pd.DataFrame(data) df_melted = df.melt(id_vars='Date', var_name='Employee', value_name='Sales') df_melted['Date'] = pd.to_datetime(df_melted['Date']) # 创建主图和双Y轴 fig, ax1 = plt.subplots(figsize=(12, 8)) # 左Y轴:绘制员工常规销售额 sns.lineplot(x='Date', y='Sales', hue='Employee', data=df_melted[df_melted['Employee'] != 'Total'], marker='o', ax=ax1) ax1.set_ylabel('员工销售额') ax1.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x):,}')) ax1.set_xlabel('日期') # 右Y轴:绘制总销售额 ax2 = ax1.twinx() sns.lineplot(x='Date', y='Sales', hue='Employee', data=df_melted[df_melted['Employee'] == 'Total'], marker='o', ax=ax2, color='red', legend=False) ax2.set_ylabel('总销售额') ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x):,}')) # 设置标题和图例 plt.title('员工销售额与总销售额趋势对比') ax1.legend(loc='upper left') plt.show()
方案优势:
- 可以在同一个视图里观察两个数据系列的趋势关联;
- 不需要拆分图表,整体视觉更统一。
针对大规模数据集的小技巧
当你扩展到上百个员工、多门店的数据集时,可以试试这些优化:
- 给极值数据(比如门店总销售额)设置独特的高亮颜色,方便快速识别;
- 动态计算Y轴范围:比如用
df_melted[df_melted['Employee'] != 'Total']['Sales'].max() * 1.1作为常规数据轴的上限,适配不同周的波动; - 如果图例过于拥挤,可以把图例移到图表外部,或者只显示关键系列。
备注:内容来源于stack exchange,提问作者Ryan Barnes
相关产品推荐
相关产品推荐

