Python:根据特定范围内的项数从列表创建分布

3 投票
3 回答
25597 浏览
提问于 2025-04-16 03:11

我给这个问题加了个标签 poisson,因为我不确定这在这个情况下是否有帮助。

我需要从一组数据中创建一个分布(最后可能会做成一张图片)。

比如说:

data = [1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 10, 10, 10, 22, 30, 30, 35, 46, 58, 59, 59]

这样数据就可以用来创建一个可视化的分布。我可能会说,范围是10,并且每个范围里至少需要有3个数据点才能算作有效的点。

根据这个例子数据,我希望得到的结果类似于:

ditribution = [1, 2, 4, 6]

因为在0-9、10-19、30-39和50-59这些范围内都有超过3个数据点。利用这个结果,我可以生成一张图像,图中会把我最终分布中存在的部分用更深的颜色标出来。下面是我想要创建的那种图像的例子,实际上会用更多的数据生成。现在先忽略蓝线。

我知道可以用一种比较“笨”的方法,就是逐个遍历列表中的每个数据点来进行计算。但是,我的数据集可能有成千上万,甚至几百万个数字。在实际情况下,我的范围(10)和所需的数据点数量(3)可能会更大。

distribution image

谢谢大家的帮助。

3 个回答

0

这听起来像是需要用到某种直方图的工作。为了实现这个目标,事先排序并不是必须的。我在这里讨论了使用一种变体的桶排序来将相近的元素分组,虽然你需要根据自己的需求调整这个算法。请注意,制作直方图时,你并不需要在桶里存储数字本身。

4

因为data可能会非常长,所以你可以考虑使用numpy。它提供了很多对数字计算很有用的功能,使用numpy数组来存储data所需的内存比用Python列表要少[*],而且由于很多numpy的功能在后台调用了C语言的函数,你可能会发现速度上有一些提升:

import numpy as np

data = np.array([1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 10, 10, 10, 22, 30, 30, 35, 46, 58, 59, 59])

hist,bins=np.histogram(data,bins=np.linspace(0,60,7))
print(hist)
# [11  3  1  3  1  3]

distribution=np.where(hist>=3)[0]+1
print(distribution)
# [1 2 4 6]

[*] -- 注意:在上面的代码中,定义data时实际上形成了一个Python列表。所以这里的最大内存需求实际上比你只用Python列表要大。不过,如果没有其他地方再引用这个Python列表,内存应该会被释放。或者,如果数据存储在磁盘上,可以使用numpy.loadtxt直接将其读取到numpy数组中。

4

如果你的 data 数据是一直排好序的,可以用一种比较简洁的方法:

import itertools as it

d = [k+1 for k, L in
         ((k, len(list(g))) for k, g in it.groupby(data,key=lambda x:x//10))
     if L>=3]

如果 data 不是排好序的,或者你不确定它是否排好序,可以在使用 itertools.groupby 时,把 sorted(data) 作为第一个参数,而不是直接用 data

如果你想要一种不那么紧凑的方法,当然可以把它展开,比如:

def divby10(x): return x//10

distribution = []
for k, g in it.groupby(data, key=divby10):
    L = len(list(g))
    if L < 3: continue
    distribution.append(k+1)

无论哪种情况,groupby 的工作原理是,它首先对传入的可迭代对象的每个项目应用作为 key= 传入的函数,以获取每个项目的“键”;对于每一组连续的、具有相同“键”的项目,groupby 会返回一个包含两个项目的元组:键的值,以及该组中所有项目的可迭代对象。

在这里,键是通过将一个项目除以 10(并取整)得到的;len(list(g)) 是具有该“键”的连续项目的数量。由于这些项目必须是连续的,所以你需要确保数据是排好序的(而且,直接排序要比按“值除以 10 取整”来排序简单得多;-)。

撰写回答