【问题标题】:Histogram for discrete values with matplotlibmatplotlib 离散值的直方图
【发布时间】:2015-05-07 22:01:11
【问题描述】:

我有时必须使用 matplotlib 对离散值进行直方图。在这种情况下,分箱的选择可能至关重要:如果您使用 10 个分箱对 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 进行直方图,则其中一个分箱将有两次和其他人一样重要。换句话说,binsize 通常应该是离散化大小的倍数。

虽然这个简单的案例我自己比较容易处理,但有没有人有一个指向库/函数的指针,可以自动处理这个问题,包括离散化大小可能略有变化的浮点数据的情况由于 FP 舍入?

谢谢。

【问题讨论】:

    标签: numpy matplotlib


    【解决方案1】:

    鉴于您的问题的标题,我将假设离散化大小是恒定的。

    您可以找到这个离散化大小(或者至少,严格地说,是该大小的 n 倍,因为您的数据中可能没有两个相邻的样本)

    np.diff(np.unique(data)).min()
    

    这会找到数据中的唯一值 (np.unique),然后找到它们之间的差异 (np.diff)。需要唯一性,这样您就不会得到零值。然后你会找到最小的差异。在离散化常数非常小的情况下,这可能会出现问题 - 我会回到那个问题。

    接下来 - 您希望您的值位于 bin 的中间 - 您当前的问题是因为 9 和 10 都位于 matplotlib 自动提供的最后一个 bin 的边缘,因此您在一个 bin 中获得了两个样本。

    所以 - 试试这个:

    import matplotlib.pyplot as plt
    import numpy as np
    
    data = range(11)
    data = np.array(data)
    
    d = np.diff(np.unique(data)).min()
    left_of_first_bin = data.min() - float(d)/2
    right_of_last_bin = data.max() + float(d)/2
    plt.hist(data, np.arange(left_of_first_bin, right_of_last_bin + d, d))
    plt.show()
    

    这给出了:


    小非整数离散化

    我们可以制作更多的测试数据集,例如

    import random 
    
    data = []
    for _ in range(1000):
        data.append(random.randint(1,100))
    data = np.array(data)
    nasty_d = 1.0 / 597 #Arbitrary smallish discretization
    data = data * nasty_d
    

    如果您随后通过上面的数组运行它并查看代码吐出的d,您将看到

    >>> print(nasty_d)
    0.0016750418760469012
    >>> print(d)
    0.00167504187605
    

    所以 - d 的检测值不是创建数据时使用的 nasty_d 的“真实”值。但是 - 通过将 bin 移动一半 d 以获得中间值的技巧 - 这无关紧要除非您的离散化非常小,所以你在浮点精度的限制你有1000个箱子,检测到的d和“真实”离散化之间的差异可以达到这样的程度其中一个垃圾箱“错过”了数据点。这是需要注意的事情,但可能不会打击你。

    上面的示例图是


    非均匀离散化/最合适的 bin...

    对于更复杂的情况,您可能希望查看this blog post I found。本文着眼于从(连续/准连续)数据中自动“学习”最佳 bin 宽度的方法,在开发自己的贝叶斯动态规划方法之前参考多种标准技术,例如 Sturges' rule and Freedman and Diaconis' rule

    如果这是您的用例 - 问题要广泛得多,可能不适合 Stack Overflow 上的明确答案,但希望这些链接会有所帮助。

    【讨论】:

    • 很好,我没有意识到移动半个 bin 可以解决我担心的大多数问题(事实上,我在谈论统一离散化,而不是关于 Freedman-Diaconis 类型规则,我我知道)。不过,当您说“所以 - d 的检测值不是 d 的“真实”值”时,我认为您的代码中有错字。
    • 谢谢。错字的好地方-我现在已经清理了。
    • 自我注意:有用的直方图选项:plt.hist(x, bins = bins, density = True, color = "green", ec = "black" 其中ec 代表“边缘颜色”。
    【解决方案2】:

    也许答案不如 J Richard Snape's 完整,但这是我最近学到的一个,我发现它既直观又简单。

    import numpy as np
    import matplotlib.pyplot as plt
    
    # great seed
    np.random.seed(1337)
    
    # how many times will a fair die land on the same number out of 100 trials.
    data = np.random.binomial(n=100, p=1/6, size=1000)
    
    # the trick is to set up the bins centered on the integers, i.e.
    # -0.5, 0.5, 1,5, 2.5, ... up to max(data) + 1.5. Then you substract -0.5 to
    # eliminate the extra bin at the end.
    bins = np.arange(0, data.max() + 1.5) - 0.5
    
    # then you plot away
    fig, ax = plt.subplots()
    _ = ax.hist(data, bins)
    ax.set_xticks(bins + 0.5)
    

    事实证明,大约 16/100 次投掷将是相同的数字!

    【讨论】:

    • 正是我想要的,谢谢!这应该是直方图的内置功能
    【解决方案3】:

    不完全是 OP 要求的,但如果所有值都是整数,则不需要计算 bin。

    np.unique(d, return_counts=True) 返回一个唯一值列表的元组作为第一个元素,并将它们的计数作为第二个元素。这可以使用星号运算符直接插入plt.bar(x, height)

    import numpy as np
    import matplotlib.pyplot as plt
    
    d = [1,1,2,4,4,4,5,6]
    plt.bar(*np.unique(d, return_counts=True))
    

    这将导致以下情节:

    请注意,这在技术上也适用于浮点数,但结果可能出乎意料,因为每个数字都会创建一个条形图。

    【讨论】:

    • 简洁优雅,正是我想要的
    【解决方案4】:

    另一个只用少量代码处理简单案例的版本!这次使用numpy.uniquematplotlib.vlines

    import numpy as np
    import matplotlib.pyplot as plt
    
    # same seed/data as Manuel Martinez to make plot easy to compare
    np.random.seed(1337)
    data = np.random.binomial(100, 1/6, 1000)
    
    values, counts = np.unique(data, return_counts=True)
    
    plt.vlines(values, 0, counts, color='C0', lw=4)
    
    # optionally set y-axis up nicely
    plt.ylim(0, max(counts) * 1.06)
    

    给我:

    看起来非常可读

    【讨论】:

      猜你喜欢
      • 2013-08-29
      • 2016-11-01
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 2021-09-01
      • 2017-12-08
      • 2013-07-31
      • 2017-04-12
      相关资源
      最近更新 更多