【问题标题】:How to avoid overlapping error bars in matplotlib?如何避免 matplotlib 中重叠的误差线?
【发布时间】:2026-02-07 09:25:01
【问题描述】:

我想为两个不同的数据集创建一个绘图,类似于answer 中提供的数据集:

在上图中,作者通过在新数据集中添加一些 x 中的小随机散点来解决误差条的重叠问题。

在我的问题中,我必须绘制一个类似的图形,但在 x 轴上有一些分类数据:

关于如何使用 x 轴上的分类变量稍微移动第二个数据集的误差线的任何想法?我想避免条形之间的重叠以使可视化更容易。

【问题讨论】:

    标签: python matplotlib


    【解决方案1】:

    您可以通过将默认数据转换添加到数据空间中的先前转换来转换每个错误栏。当知道类别通常彼此相距一个数据单元时,这是可能的。

    import numpy as np; np.random.seed(42)
    import matplotlib.pyplot as plt
    from matplotlib.transforms import Affine2D
    
    x = list("ABCDEF")
    y1, y2 = np.random.randn(2, len(x))
    yerr1, yerr2 = np.random.rand(2, len(x))*4+0.3
    
    fig, ax = plt.subplots()
    
    trans1 = Affine2D().translate(-0.1, 0.0) + ax.transData
    trans2 = Affine2D().translate(+0.1, 0.0) + ax.transData
    er1 = ax.errorbar(x, y1, yerr=yerr1, marker="o", linestyle="none", transform=trans1)
    er2 = ax.errorbar(x, y2, yerr=yerr2, marker="o", linestyle="none", transform=trans2)
    
    plt.show()
    

    或者,您可以在应用数据转换后翻译误差线,从而以点为单位移动它们。

    import numpy as np; np.random.seed(42)
    import matplotlib.pyplot as plt
    from matplotlib.transforms import ScaledTranslation
    
    x = list("ABCDEF")
    y1, y2 = np.random.randn(2, len(x))
    yerr1, yerr2 = np.random.rand(2, len(x))*4+0.3
    
    fig, ax = plt.subplots()
    
    trans1 = ax.transData + ScaledTranslation(-5/72, 0, fig.dpi_scale_trans)
    trans2 = ax.transData + ScaledTranslation(+5/72, 0, fig.dpi_scale_trans)
    er1 = ax.errorbar(x, y1, yerr=yerr1, marker="o", linestyle="none", transform=trans1)
    er2 = ax.errorbar(x, y2, yerr=yerr2, marker="o", linestyle="none", transform=trans2)
    
    plt.show()
    

    虽然两种情况下的结果看起来相似,但它们有着根本的不同。在交互式缩放轴或更改图形大小时,您将观察到这种差异。

    【讨论】:

      【解决方案2】:

      考虑使用以下方法来突出显示图 - errorbarfill_between 与非零透明度的组合:

      import random
      import matplotlib.pyplot as plt
      
      # create sample data
      N = 8
      data_1 = {
          'x': list(range(N)),
          'y': [10. + random.random() for dummy in range(N)],
          'yerr': [.25 + random.random() for dummy in range(N)]}
      data_2 = {
          'x': list(range(N)),
          'y': [10.25 + .5 * random.random() for dummy in range(N)],
          'yerr': [.5 * random.random() for dummy in range(N)]}
      
      # plot
      plt.figure()
      # only errorbar
      plt.subplot(211)
      for data in [data_1, data_2]:
          plt.errorbar(**data, fmt='o')
      # errorbar + fill_between
      plt.subplot(212)
      for data in [data_1, data_2]:
          plt.errorbar(**data, alpha=.75, fmt=':', capsize=3, capthick=1)
          data = {
              'x': data['x'],
              'y1': [y - e for y, e in zip(data['y'], data['yerr'])],
              'y2': [y + e for y, e in zip(data['y'], data['yerr'])]}
          plt.fill_between(**data, alpha=.25)
      

      结果:

      【讨论】:

      • 感谢您的建议。然而,这个想法并不适合我的问题。所有变量都是分类的,我不能用线连接它们。
      • @revy 分类或数字 - 没关系。它们只是标签。使用您选择的任何对象更改整数,您将获得完全相同的图形,X 或 Y 轴旁边有不同的标签。例如,将 list(range(N)) 替换为 N 字词 - alegriadesgosto 等。就这么简单。
      • 很好的答案!我喜欢将数据定义为字典的方式。从今以后我会一直这样!