【问题标题】:Merge histograms with different ranges合并不同范围的直方图
【发布时间】:2017-11-02 22:33:44
【问题描述】:

有没有什么快速的方法可以合并两个具有不同 bin 范围和 bin 编号的 numpy 直方图?

例如:

x = [1,2,2,3]
y = [4,5,5,6]

a = np.histogram(x, bins=10)  
#  a[0] = [1, 0, 0, 0, 0, 2, 0, 0, 0, 1]
#  a[1] = [ 1. ,  1.2,  1.4,  1.6,  1.8,  2. ,  2.2,  2.4,  2.6,  2.8,  3. ]

b = np.histogram(y, bins=5)
#  b[0] = [1, 0, 2, 0, 1]
#  b[1] = [ 4. ,  4.4,  4.8,  5.2,  5.6,  6. ]

现在我想要一些这样的功能:

def merge(a, b):
    # some actions here #
    return merged_a_b_values, merged_a_b_bins

其实我还没有xyab只知道。 但是merge(a, b)的结果必须等于np.histogram(x+y, bins=10)

m = merge(a, b)
#  m[0] = [1, 0, 2, 0, 1, 0, 1, 0, 2, 1]
#  m[1] = [ 1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5,  5. ,  5.5,  6. ] 

【问题讨论】:

  • 为什么不能使用更简单的组合列表并创建直方图的解决方案而不是合并两个直方图,即np.histogran(x+y, bins=10)
  • 我没有 x 和 y :(
  • 您能写下样本的预期输出(实际值)吗?
  • @Reti43 但我们知道 bins/ranges:a[1] 和 b[1] 将保存它(a[0] 和 b[0] 是直方图的值)。我们需要像重采样这样的东西......
  • 重要的是直方图是用必要数量的箱创建的,以达到这个宽度。例如,对于列表x = [4.38, 5.26, 5.27],您至少需要np.round((max(x) - min(x)) / 0.01) + 1 箱,即90 个箱,以确保您的列表可以重建。如果您可以控制直方图的生成,即使您无法保存xy 的值,您仍然可以检索它们。

标签: python numpy merge histogram


【解决方案1】:

合并两个不同直方图的问题没有唯一的解决方案。我在这里提出一个简单而快速的解决方案,该解决方案基于两个必要的设计假设来处理分箱序列固有的信息丢失:

  1. 恢复的值由它们所属的 bin 的开头表示。

  2. 合并应保持最高 bin 分辨率以避免进一步丢失信息,并应完全包含子直方图的间隔。

代码如下:

  import numpy as np

  def merge(a, b):

      def extract_vals(hist):
          # Recover values based on assumption 1.
          values = [[y]*x for x, y in zip(hist[0], hist[1])]
          # Return flattened list.
          return [z for s in values for z in s]

      def extract_bin_resolution(hist):
          return hist[1][1] - hist[1][0]

      def generate_num_bins(minval, maxval, bin_resolution):
          # Generate number of bins necessary to satisfy assumption 2
          return int(np.ceil((maxval - minval) / bin_resolution))

      vals = extract_vals(a) + extract_vals(b)
      bin_resolution = min(map(extract_bin_resolution, [a, b]))
      num_bins = generate_num_bins(min(vals), max(vals), bin_resolution)

      return np.histogram(vals, bins=num_bins)

示例代码如下:

import matplotlib.pyplot as plt

x = [1,2,2,3]
y = [4,5,5,6]

a = np.histogram(x, bins=10)  
#  a[0] = [1, 0, 0, 0, 0, 2, 0, 0, 0, 1]
#  a[1] = [ 1. ,  1.2,  1.4,  1.6,  1.8,  2. ,  2.2,  2.4,  2.6,  2.8,  3. ]

b = np.histogram(y, bins=5)
#  b[0] = [1, 0, 2, 0, 1]
#  b[1] = [ 4. ,  4.4,  4.8,  5.2,  5.6,  6. ]

# Merge and plot results
c = merge(a, b)

c_num_bins = c[1].size - 1

plt.hist(a[0], bins=5, label='a')
plt.hist(b[0], bins=10, label='b')
plt.hist(c[0], bins=c_num_bins, label='c')
plt.legend()

plt.show()

【讨论】:

    【解决方案2】:

    我实际上已经在 dandom 的答案中添加了评论,但我缺乏所需的声誉。 我对你的例子有点困惑。如果我没记错的话,你正在绘制直方图箱的直方图。应该是这个吧?

    plt.figure()
    plt.plot(a[1][:-1], a[0], marker='.', label='a')
    plt.plot(b[1][:-1], b[0], marker='.', label='b')
    plt.plot(c[1][:-1], c[0], marker='.', label='c')
    plt.legend()
    plt.show()
    

    另外请注意您对组合直方图的建议。您当然是对的,没有唯一的解决方案,因为您根本不知道样本在您用于组合的更精细的网格中的位置。当有两个直方图时,它们的 bin 宽度明显不同,建议的合并函数可能会导致直方图稀疏且看起来很人工。

    我尝试通过插值组合直方图(假设计数箱内的样本均匀分布在原始箱中 - 这当然也只是一个假设)。 然而,这会导致看起来更自然的结果,至少对于从我通常遇到的分布中采样的数据而言。

    import numpy as np
    def merge_hist(a, b):
    
        edgesa = a[1]
        edgesb = b[1]
        da = edgesa[1]-edgesa[0]
        db = edgesb[1]-edgesb[0]
        dint = np.min([da, db])
    
        min = np.min(np.hstack([edgesa, edgesb]))
        max = np.max(np.hstack([edgesa, edgesb]))
        edgesc = np.arange(min, max, dint)
    
        def interpolate_hist(edgesint, edges, hist):
            cumhist = np.hstack([0, np.cumsum(hist)])
            cumhistint = np.interp(edgesint, edges, cumhist)
            histint = np.diff(cumhistint)
            return histint
    
        histaint = interpolate_hist(edgesc, edgesa, a[0])
        histbint = interpolate_hist(edgesc, edgesb, b[0])
    
        c = histaint + histbint
        return c, edgesc
    

    两个高斯分布的例子:

    import numpy as np
    a = 5 + 1*np.random.randn(100)
    b = 10 + 2*np.random.randn(100)
    hista, edgesa = np.histogram(a, bins=10)
    histb, edgesb = np.histogram(b, bins=5)
    
    histc, edgesc = merge_hist([hista, edgesa], [histb, edgesb])
    
    plt.figure()
    width = edgesa[1]-edgesa[0]
    plt.bar(edgesa[:-1], hista, width=width)
    width = edgesb[1]-edgesb[0]
    plt.bar(edgesb[:-1], histb, width=width)
    plt.figure()
    width = edgesc[1]-edgesc[0]
    plt.bar(edgesc[:-1], histc, width=width)
    
    plt.show()
    

    不过,我不是统计学家,所以如果建议方法可行,请告诉我。

    【讨论】:

      猜你喜欢
      • 2019-04-11
      • 2014-10-02
      • 1970-01-01
      • 2020-12-20
      • 2016-07-09
      • 1970-01-01
      • 1970-01-01
      • 2015-05-27
      • 1970-01-01
      相关资源
      最近更新 更多