【问题标题】:How to center labels in histogram plot如何在直方图中居中标签
【发布时间】:2014-06-08 09:27:25
【问题描述】:

我有一个 numpy 数组 results,看起来像

[ 0.  2.  0.  0.  0.  0.  3.  0.  0.  0.  0.  0.  0.  0.  0.  2.  0.  0.
  0.  0.  0.  1.  0.  0.  0.  0.  0.  0.  0.  1.  0.  0.  0.  0.  0.  0.
  0.  1.  1.  0.  0.  0.  0.  2.  0.  3.  1.  0.  0.  2.  2.  0.  0.  0.
  0.  0.  0.  0.  0.  1.  1.  0.  0.  0.  0.  0.  0.  2.  0.  0.  0.  0.
  0.  1.  0.  0.  0.  0.  0.  0.  0.  0.  0.  3.  1.  0.  0.  0.  0.  0.
  0.  0.  0.  1.  0.  0.  0.  1.  2.  2.]

我想绘制它的直方图。我试过了

import matplotlib.pyplot as plt
plt.hist(results, bins=range(5))
plt.show()

这给了我一个 x 轴标记为0.0 0.5 1.0 1.5 2.0 2.5 3.0. 3.5 4.0 的直方图。

我希望将 x 轴标记为 0 1 2 3,而不是将标签放在每个条的中心。你怎么能这样做?

【问题讨论】:

  • 我不确定你想要什么。您是否希望 bin 以 1、2、3 为中心(所以围绕整数而不是 1.5、2.5 值)。或者你想用文本或其他东西标记条吗?因为如果我执行你的命令,我的输出是(array([ 4., 5., 1., 2.]), array([0, 1, 2, 3, 4])(具有不同的输入值)。所以我有不同的垃圾箱,还是我错过了什么?
  • @Mathias711 第一条是results中0的个数,第二个是1的个数(有11个),第三个是2的个数(有8个)最后一个是3的个数(一共有三个)。我想将数字0 作为第一条中间下方的标签,将数字1 作为第二条中间下方的标签,依此类推。是不是更清楚了?
  • 那么分箱没有问题,您只是想给分箱添加标签吗?
  • @Mathias711 是的,我想摆脱默认标签并添加我描述的标签。

标签: python numpy matplotlib histogram


【解决方案1】:

使用 numpy 将 bin 置于您请求的值的中心:

import matplotlib.pyplot as plt
import numpy as np
plt.hist(results, bins=np.arange(-0.5, 5))
plt.show()

【讨论】:

    【解决方案2】:

    将标签集中在离散值的 matplotlib 直方图上足以将“bins”定义为 bin 边界列表。

    import matplotlib.pyplot as plt
    %matplotlib inline
    
    example_data = [0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1]
    
    fig = plt.figure(figsize=(5,5))
    ax1 = fig.add_subplot()
    ax1_bars = [0,1]                           
    ax1.hist( 
        example_data, 
        bins=[x for i in ax1_bars for x in (i-0.4,i+0.4)], 
        color='#404080')
    ax1.set_xticks(ax1_bars)
    ax1.set_xticklabels(['class 0 label','class 1 label'])
    ax1.set_title("Example histogram")
    ax1.set_yscale('log')
    ax1.set_ylabel('quantity')
    
    fig.tight_layout()
    plt.show()
    

    这是如何工作的?

    • 直方图bins 参数可以是定义箱边界的列表。 对于可以假定值为 0 或 1 的类,这些边界应该是 [ -0.5, 0.5, 0.5, 1.5 ],大致翻译为“bin 0”是从 -0.5 到 1.5,“bin 1”是从 0.5 到 1.5。由于这些范围的中间是离散值,因此标签将位于预期位置。

    • 表达式 [x for i in ax_bars for x in (i-0.4,i+0.4)] 只是为值列表 (ax_bars) 生成边界列表的一种方式。

    • 表达式 ax1.set_xticks(ax1_bars) 对于将 x 轴设置为离散的很重要。

    • 其余的应该是不言自明的。

    【讨论】:

      【解决方案3】:

      这是一个仅使用plt.hist() 的解决方案。 让我们把它分成两部分:

      1. 将 x 轴标记为 0 1 2 3
      2. 将标签放在每个条的中心。

      要使 x 轴标记为 0 1 2 3 而没有 .5 值,您可以使用函数 plt.xticks() 并提供您想要在 x 轴上的值作为参数。在你的情况下,既然你想要0 1 2 3,你可以打电话给plt.xticks(range(4))

      要将标签放在每个条的中心,您可以将参数 align='left' 传递给 plt.hist() 函数。以下是您的代码,经过最低限度的修改即可。

      import matplotlib.pyplot as plt
      
      results = [0,  2,  0,  0,  0,  0,  3,  0,  0,  0,  0,  0,  0,  0,  0,  2,  0,  0,
                 0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,
                 0,  1,  1,  0,  0,  0,  0,  2,  0,  3,  1,  0,  0,  2,  2,  0,  0,  0,
                 0,  0,  0,  0,  0,  1,  1,  0,  0,  0,  0,  0,  0,  2,  0,  0,  0,  0,
                 0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  1,  0,  0,  0,  0,  0,
                 0,  0,  0,  1,  0,  0,  0,  1,  2,  2]
      
      plt.hist(results, bins=range(5), align='left')
      plt.xticks(range(4))
      plt.show()
      

      【讨论】:

      • 当前所选答案的变体,无添加。
      【解决方案4】:

      就像 Jarad 在他的回答中指出的那样,barplot 是一种巧妙的方法。这是使用 pandas 绘制条形图的一种简短方法。

      import pandas as pd
      import matplotlib.pyplot as plt
      
      arr = [ 0.,  2.,  0.,  0.,  0.,  0.,  3.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  2.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,
              0.,  0.,  0.,  0.,  2.,  0.,  3.,  1.,  0.,  0.,  2.,  2.,  0.,
              0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  2.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.,  0.,  0.,  3.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  1.,  0.,  0.,  0.,  1.,  2.,  2.]
      
      col = 'name'
      pd.DataFrame({col : arr}).groupby(col).size().plot.bar()
      plt.show()
      

      【讨论】:

      • "barplot 是一种巧妙的方法"。仅在某些情况下。 these cases 你会怎么做?
      • 右边的情节可以按照 Ted 在他的answer 中显示的那样创建。要获得左侧的绘图,应使用 size() 而不是 sum(),即 df.groupby(df.sold // 10 * 10).size().plot.bar()。但我认为值得将结果与其他方法进行比较。
      • 我的意思是链接案例中的困难在于使用histweight 选项。不能轻易被barplot 替换,因为它没有同等的可能性。
      【解决方案5】:

      其他答案只是不适合我。使用plt.bar 而不是plt.hist 的好处是bar 可以使用align='center'

      import numpy as np
      import matplotlib.pyplot as plt
      
      arr = np.array([ 0.,  2.,  0.,  0.,  0.,  0.,  3.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  2.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,
              0.,  0.,  0.,  0.,  2.,  0.,  3.,  1.,  0.,  0.,  2.,  2.,  0.,
              0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  2.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,
              0.,  0.,  0.,  0.,  0.,  3.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,
              0.,  0.,  1.,  0.,  0.,  0.,  1.,  2.,  2.])
      
      labels, counts = np.unique(arr, return_counts=True)
      plt.bar(labels, counts, align='center')
      plt.gca().set_xticks(labels)
      plt.show()
      

      【讨论】:

      • 怎么没有人祝贺你呢?绝对是这个确切问题的最优雅和最直接的解决方案。这应该被认为是可视化离散分布的最佳解决方案。
      • 确实如此。这要简单得多!
      • 这应该是答案。 tnx :)
      【解决方案6】:

      以下替代解决方案与plt.hist() 兼容(例如,您可以在pandas.DataFrame.hist() 之后调用它。

      import numpy as np
      
      def bins_labels(bins, **kwargs):
          bin_w = (max(bins) - min(bins)) / (len(bins) - 1)
          plt.xticks(np.arange(min(bins)+bin_w/2, max(bins), bin_w), bins, **kwargs)
          plt.xlim(bins[0], bins[-1])
      

      (最后一行不是 OP 严格要求的,但它使输出更好)

      这可以用于:

      import matplotlib.pyplot as plt
      bins = range(5)
      plt.hist(results, bins=bins)
      bins_labels(bins, fontsize=20)
      plt.show()
      

      【讨论】:

      • 你能解释一下为什么你只显示 bin 0,1,2,3 而使用range(5) 吗?
      • @Wikunia:当然。箱 0 覆盖地块中的 0 到 1,箱 1 覆盖地块中的 1 到 2...,依此类推,直到箱 3,它覆盖地块中的 3 到 4。所以箱(左和右)borders 必须是序列 [0, 1, 2, 3, 4]... 正好是range(5)。奇怪,我知道,但我看到的唯一选择(居中 bin i 从 i-1/2 到 i+1/2)会更复杂。
      • 这个答案在更一般的情况下是有效的,如果重新定义箱,例如作为bins = np.arange(2, 7, .5)
      【解决方案7】:

      您可以从np.histogram 构建bar 绘图。

      考虑一下

      his = np.histogram(a,bins=range(5))
      fig, ax = plt.subplots()
      offset = .4
      plt.bar(his[1][1:],his[0])
      ax.set_xticks(his[1][1:] + offset)
      ax.set_xticklabels( ('1', '2', '3', '4') )
      

      编辑:为了让条相互接触,必须使用宽度参数。

       fig, ax = plt.subplots()
       offset = .5
       plt.bar(his[1][1:],his[0],width=1)
       ax.set_xticks(his[1][1:] + offset)
       ax.set_xticklabels( ('1', '2', '3', '4') )
      

      【讨论】:

      • 谢谢!你如何摆脱酒吧之间的空间?
      • 啊,谢谢。我也在做这样的事情,只是没有让xticks 工作。感谢您的澄清
      • @eleanora,条形已修复。
      • 另外,如果我希望它从 0 变为 100,我该如何创建 ('0', '1', '2','3')? tuple(str(i) for i in range(101)) ?
      • @eleanora 这行得通,但我会使用np.arange(1, 101).astype(str),或者不使用numpy:map(str, range(1, 101))
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-16
      • 2012-12-28
      相关资源
      最近更新 更多