如何绘制兼顾展示10000以下散点分布与异常极值的matplotlib散点图
解决方案
你可以通过以下两种常见方案实现需求:
方案1:固定Y轴范围+极值特殊标记
实现逻辑最简单,不改变原有正常数据的展示比例,仅对超出阈值的极值做特殊标记和数值标注:
import pandas as pd import matplotlib.pyplot as plt from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() dates = ["2021-01-01", "2021-01-01", "2021-01-06", "2021-01-08", "2021-01-12", "2021-02-01", "2021-02-11", "2021-02-12", "2021-02-15", "2021-02-16", "2021-03-11", "2021-03-21", "2021-03-22", "2021-03-23", "2021-03-24", "2021-04-02", "2021-04-12", "2021-04-22", "2021-04-26", "2021-04-30"] # 加入异常极值的测试数据 numbers= [6400, 5100,5000, 4000,3686, 9000,8050, 8000,6050, 6000,9000, 8500,7800, 7000,6000, 10000,9600, 8000,7883, 66860] dates = [pd.to_datetime(d) for d in dates] threshold = 10000 # 设定正常数值上限阈值 # 拆分正常数据和异常数据 normal_x = [] normal_y = [] outlier_x = [] outlier_y_true = [] for d, num in zip(dates, numbers): if num <= threshold: normal_x.append(d) normal_y.append(num) else: outlier_x.append(d) outlier_y_true.append(num) # 绘制正常散点 plt.scatter(normal_x, normal_y, s=100, c='red', label='正常数值') # 绘制异常散点:统一放在阈值上方位置,用不同样式标记 outlier_y_plot = [threshold + 200] * len(outlier_y_true) plt.scatter(outlier_x, outlier_y_plot, s=150, c='blue', marker='*', label='异常极值') # 给异常点加实际数值标注 for x, y, true_val in zip(outlier_x, outlier_y_plot, outlier_y_true): plt.text(x, y + 150, str(true_val), ha='center', fontsize=10) # 固定Y轴范围,保持正常数据的展示比例 plt.ylim(3000, threshold + 1000) plt.xticks(rotation=45) plt.legend() plt.tight_layout() plt.show()
方案2:Y轴截断(断轴图)
可以同时完整展示正常数据区间和极值区间,通过断轴符号区分两个数值区间,避免正常数据被压缩:
import pandas as pd import matplotlib.pyplot as plt from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() dates = ["2021-01-01", "2021-01-01", "2021-01-06", "2021-01-08", "2021-01-12", "2021-02-01", "2021-02-11", "2021-02-12", "2021-02-15", "2021-02-16", "2021-03-11", "2021-03-21", "2021-03-22", "2021-03-23", "2021-03-24", "2021-04-02", "2021-04-12", "2021-04-22", "2021-04-26", "2021-04-30"] numbers= [6400, 5100,5000, 4000,3686, 9000,8050, 8000,6050, 6000,9000, 8500,7800, 7000,6000, 10000,9600, 8000,7883, 66860] dates = [pd.to_datetime(d) for d in dates] # 创建上下两个子图,共享X轴 fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(8,6)) fig.subplots_adjust(hspace=0.05) # 调整子图间距 # 两个子图都画所有散点 ax1.scatter(dates, numbers, s=100, c='red') ax2.scatter(dates, numbers, s=100, c='red') # 分别设置两个子图的Y轴范围:上面显示极值,下面显示正常区间 ax1.set_ylim(65000, 68000) ax2.set_ylim(3000, 11000) # 隐藏两个子图相邻的边框 ax1.spines['bottom'].set_visible(False) ax2.spines['top'].set_visible(False) ax1.xaxis.tick_top() ax1.tick_params(labeltop=False) ax2.xaxis.tick_bottom() # 添加断轴斜线标记 d = 0.015 kwargs = dict(transform=ax1.transAxes, color='k', clip_on=False) ax1.plot((-d, +d), (-d, +d), **kwargs) ax1.plot((1 - d, 1 + d), (-d, +d), **kwargs) kwargs.update(transform=ax2.transAxes) ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs) ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs) plt.xticks(rotation=45) plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者Mark K
相关产品推荐
相关产品推荐

