从列表创建分布并生成随机数,这些随机数遵循 Python 中的分布

create distribution from a list and generate random numbers which follow that distribution in Python

假设我有一个数字列表(在这个特定示例中,所有数字都在 0.5 到 1.5 之间,当然它是一个离散集)。

my_list=  [0.564, 1.058, 0.779, 1.281, 0.656, 0.863, 0.958, 1.146, 0.742, 1.139, 0.957, 0.548, 0.572, 1.204, 0.868, 0.57, 1.456, 0.586, 0.718, 0.966, 0.625, 0.951, 0.766, 1.458, 0.83, 1.25, 0.7, 1.334, 1.015, 1.43, 1.376, 0.942, 1.252, 1.441, 0.795, 1.25, 0.851, 1.383, 0.969, 0.629, 1.008, 0.729, 0.841, 0.619, 0.63, 1.189, 0.514, 0.899, 0.807, 0.63, 1.101, 0.528, 1.385, 0.838, 0.538, 1.364, 0.702, 1.129, 0.639, 0.557, 1.28, 0.664, 1.021, 1.43, 0.792, 1.229, 0.837, 1.183, 0.54, 0.831, 1.279, 1.385, 1.377, 0.827, 1.32, 0.537, 1.19, 1.446, 1.222, 0.762, 1.302, 0.626, 1.352, 1.316, 1.286, 1.239, 1.027, 1.198, 0.961, 0.515, 0.989, 0.979, 1.123, 0.889, 1.484, 0.734, 0.718, 0.758, 0.782, 1.163, 0.579, 0.744, 0.711, 1.13, 0.598, 0.913, 1.305, 0.684, 1.108, 1.373, 0.945, 0.837, 1.129, 1.005, 1.447, 1.393, 1.493, 1.262, 0.73, 1.232, 0.838, 1.319, 0.971, 1.234, 0.738, 1.418, 1.397, 0.927, 1.309, 0.784, 1.232, 1.454, 1.387, 0.851, 1.132, 0.958, 1.467, 1.41, 1.359, 0.529, 1.139, 1.438, 0.672, 0.756, 1.356, 0.736, 1.436, 1.414, 0.921, 0.669, 1.21, 1.041, 0.597, 0.541, 1.162, 1.292, 0.538, 1.011, 0.828, 1.356, 0.897, 0.831, 1.018, 1.412, 1.363, 1.371, 1.231, 1.278, 0.564, 1.134, 1.324, 0.593, 1.307, 0.66, 1.376, 1.469, 1.315, 0.959, 1.099, 1.313, 1.032, 1.128, 1.175, 0.64, 0.581, 1.09, 0.934, 0.698, 1.272]

我可以根据它制作直方图分布图

hist(my_list, bins=20, range=[0.5,1.5])
show()

产生

现在,我想创建另一个随机数列表(假设这个新列表由 100 个数字组成)将遵循相同的分布(不确定如何 link 将离散集转换为连续集distribution !!! ) 作为旧列表 ( my_list ) ,所以如果我从新列表绘制直方图分布,它基本上会产生相同的直方图分布。

在 Python 2.7 中有什么方法可以这样做吗?我提前感谢任何帮助。

你首先需要"bucket up"感兴趣的范围,当然你可以使用scipy&c的工具来完成,但是为了了解一下是怎么回事Python 版本可能有帮助 - 没有优化,为了便于理解:

import collections

def buckets(discrete_set, amin=None, amax=None, bucket_size=None):
    if amin is None: amin=min(discrete_set)
    if amax is None: amax=min(discrete_set)
    if bucket_size is None: bucket_size = (amax-amin)/20
    def to_bucket(sample):
        if not (amin <= sample <= amax): return None  # no bucket fits
        return int((sample - amin) // bucket_size)
    b = collections.Counter(to_bucket(s)
            for s in discrete_set if to_bucket(s) is not None)
    return amin, amax, bucket_size, b

所以,现在你有一个 Counter(本质上是一个 dict)映射每个桶从 0 到它在离散集中观察到的计数。

接下来,您需要生成一个与通过调用 buckets(discrete_set) 测得的桶分布相匹配的随机样本。计数器的 elements 方法可以提供帮助,但您需要一个 random.sample...:[=​​24=] 的列表

mi, ma, bs, bks = buckets(discrete_set) 
buckelems = list(bks.elements())

(这可能会浪费很多space,但你可以稍后优化它,与这个以理解为重点的概述分开:-)。

现在很容易得到一个N尺寸的样本,例如:

def makesample(N, buckelems, mi, ma, bs):
    s = []
    for _ in range(N):
        buck = random.choice(buckelems)
        x = random.uniform(mi+buck*bs, mi+(buck+1)*bs)
        s.append(x)
    return s

这里我假设桶足够细粒度,可以在每个桶内使用均匀分布。

现在,优化它当然很有趣——buckelems 将拥有与 discrete_set 中最初一样多的项目,如果这对内存造成过大的负载,则可以构建累积分布并改为使用。

或者,可以完全绕过 Counter,只是 "round" 离散集中的每个项目到其存储桶的下限,如果内存还可以但需要更快的速度。或者,可以在 "perturbing" 所选值之前单独保留 discrete_set 并在其中保留 random.choice(根据具体问题的约束以不同的方式)。乐趣无穷...!-)

当您尝试进行分布拟合时,不要过多解读样本量较小的直方图的波谷和波峰。

我对您的数据进行了 Kolmogorov-Smirnov 检验以检验它们来自均匀 (0.5,1.5) 分布的假设,但未能拒绝。因此,您可以生成您想要的任何大小的 Uniform(0.5,1.5) 样本。

鉴于您所说的基础分布是连续的,我认为分布拟合方法优于 histogram/bucket-based 方法。