【问题标题】:How to avoid overlapping of labels & autopct in a matplotlib pie chart?如何避免 matplotlib 饼图中的标签和 autopct 重叠?
【发布时间】:2014-05-10 05:37:21
【问题描述】:

我的 Python 代码是:

values = [234, 64, 54,10, 0, 1, 0, 9, 2, 1, 7, 7]
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
          'Jul','Aug','Sep','Oct', 'Nov','Dec']

colors = ['yellowgreen', 'red', 'gold', 'lightskyblue', 
          'white','lightcoral','blue','pink', 'darkgreen', 
          'yellow','grey','violet','magenta','cyan']

plt.pie(values, labels=labels, autopct='%1.1f%%', shadow=True, 
        colors=colors, startangle=90, radius=1.2)

plt.show()

是否可以显示“一月”、“二月”、“三月”等标签和百分比:

  • 不重叠,或
  • 使用arrow mark?

【问题讨论】:

    标签: python numpy matplotlib plot pie-chart


    【解决方案1】:

    您也可以将图例放在饼图旁边:

    import matplotlib.pyplot as plt
    import numpy as np
    
    x = np.char.array(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct', 'Nov','Dec'])
    y = np.array([234, 64, 54,10, 0, 1, 0, 9, 2, 1, 7, 7])
    colors = ['yellowgreen','red','gold','lightskyblue','white','lightcoral','blue','pink', 'darkgreen','yellow','grey','violet','magenta','cyan']
    porcent = 100.*y/y.sum()
    
    patches, texts = plt.pie(y, colors=colors, startangle=90, radius=1.2)
    labels = ['{0} - {1:1.2f} %'.format(i,j) for i,j in zip(x, porcent)]
    
    sort_legend = True
    if sort_legend:
        patches, labels, dummy =  zip(*sorted(zip(patches, labels, y),
                                              key=lambda x: x[2],
                                              reverse=True))
    
    plt.legend(patches, labels, loc='left center', bbox_to_anchor=(-0.1, 1.),
               fontsize=8)
    
    plt.savefig('piechart.png', bbox_inches='tight')
    


    编辑:如果你想保持原来的顺序,如你在 cmets 中提到的,你可以在上面的代码中设置sort_legend=False,给出:

    【讨论】:

    • 嗨@Saullo Castro,这是我想要的,但还有一个问题我想要按数组顺序排列的图例.. 即一月,二月,三月,四月,五月,六月..十月,十一月,十二月。这里是按数据(y值)的顺序绘制的
    • 太棒了。通过这个答案,我实际上在 Python 中学到了很多新功能。 10 月 10 日。
    • 惊人的演示! 10/10 肯定!
    • 5 年后,这仍然解决了我的问题。看起来很棒!
    • @ProtimaRaniPaul 请为此打开一个新问题
    【解决方案2】:

    首先; avoid pie charts whenever you can!

    其次,想一想对象在 python 中是如何工作的。我相信这个例子应该是不言自明的,但是,您显然不需要手动移动标签。

    from matplotlib import pyplot as plt
    
    fig, ax = plt.subplots()
    ax.axis('equal')
    
    patches, texts, autotexts = ax.pie([12,6,2,3], 
                                  labels=['A', 'B', 'C', 'no data'],
                                  autopct='%1.1f%%', 
                                  pctdistance=0.5, 
                                  labeldistance=1.1)
    
    
    
    # Move a label
    texts[1]._x =-0.5
    texts[1]._y =+0.5
    
    # E.g. change some formatting
    texts[-1]._color = 'blue'
    

    有一些选项可以修改标签:

    # Check all options
    print(texts[0].__dict__)
    

    返回

    {'_stale': False,
     'stale_callback': <function matplotlib.artist._stale_axes_callback(self, val)>,
     '_axes': <AxesSubplot:>,
     'figure': <Figure size 432x288 with 1 Axes>,
     '_transform': <matplotlib.transforms.CompositeGenericTransform at 0x7fe09bedf210>,
     '_transformSet': True,
     '_visible': True,
     '_animated': False,
     '_alpha': None,
     'clipbox': <matplotlib.transforms.TransformedBbox at 0x7fe065d3dd50>,
     '_clippath': None,
     '_clipon': False,
     '_label': '',
     '_picker': None,
     '_contains': None,
     '_rasterized': None,
     '_agg_filter': None,
     '_mouseover': False,
     'eventson': False,
     '_oid': 0,
     '_propobservers': {},
     '_remove_method': <function list.remove(value, /)>,
     '_url': None,
     '_gid': None,
     '_snap': None,
     '_sketch': None,
     '_path_effects': [],
     '_sticky_edges': _XYPair(x=[], y=[]),
     '_in_layout': True,
     '_x': -0.07506663683168735,
     '_y': 1.097435647331897,
     '_text': 'A',
     '_color': 'black',
     '_fontproperties': <matplotlib.font_manager.FontProperties at 0x7fe065d3db90>,
     '_usetex': False,
     '_wrap': False,
     '_verticalalignment': 'center',
     '_horizontalalignment': 'right',
     '_multialignment': None,
     '_rotation': 'horizontal',
     '_bbox_patch': None,
     '_renderer': <matplotlib.backends.backend_agg.RendererAgg at 0x7fe08b01fd90>,
     '_linespacing': 1.2,
     '_rotation_mode': None}
    

    【讨论】:

      【解决方案3】:

      如果有人只想自动偏移标签,而不是使用图例,我编写了这个函数来做到这一点(是的,我真的很努力)。它使用 numpy,但可以很容易地用纯 python 重写。

      import numpy as np
      
      def fix_labels(mylabels, tooclose=0.1, sepfactor=2):
          vecs = np.zeros((len(mylabels), len(mylabels), 2))
          dists = np.zeros((len(mylabels), len(mylabels)))
          for i in range(0, len(mylabels)-1):
              for j in range(i+1, len(mylabels)):
                  a = np.array(mylabels[i].get_position())
                  b = np.array(mylabels[j].get_position())
                  dists[i,j] = np.linalg.norm(a-b)
                  vecs[i,j,:] = a-b
                  if dists[i,j] < tooclose:
                      mylabels[i].set_x(a[0] + sepfactor*vecs[i,j,0])
                      mylabels[i].set_y(a[1] + sepfactor*vecs[i,j,1])
                      mylabels[j].set_x(b[0] - sepfactor*vecs[i,j,0])
                      mylabels[j].set_y(b[1] - sepfactor*vecs[i,j,1])
      

      所以像这样使用它:

      wedges, labels, autopct = ax1.pie(sizes, labels=groups, autopct='%1.1f%%',
                                        shadow=False, startangle=90)
      
      fix_labels(autopct, sepfactor=3)
      fix_labels(labels, sepfactor=2)
      

      如果您只有几个标签重叠,则此方法效果很好。如果你有一大堆像 OP 一样的东西,你可能想在vecs[i,j,:] = a-b 行中添加一个随机方向向量。这可能会很好用。

      【讨论】:

        【解决方案4】:

        尝试紧凑布局。

        plt.tight_layout()
        

        在代码的末尾。它可能会稍微防止重叠。

        【讨论】:

        • 在我的情况下没有
        • ibb.co/5xwvcnW 嗨,谢谢!有什么办法可以制作类似于下图的图例?
        猜你喜欢
        • 1970-01-01
        • 2018-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-06
        相关资源
        最近更新 更多