使用Numpy进行独特的随机数采样

时间:2017-07-11 21:21:25

标签: python performance numpy

我需要创建一个10,000 x 50的数组,其中每行包含1到365之间的递增系列随机数,如下所示:

sample_dates = np.array([np.sort(np.random.choice(365, 50, replace=False)) for _ in range(10000)])

我想通过迭代器来解决这个问题的唯一方法:

{{1}}

哪个有效,但速度很慢(运行时间约为0.33秒),而且我将要做这几千次)。有没有更快的方法来实现这一目标?

编辑:据我所知,这个解决方案中最昂贵的部分是迭代和对np.random.choice的10k个别调用,而不是排序

4 个答案:

答案 0 :(得分:2)

考虑到数组的形状,我认为迭代列可能会提供一些改进。所以我的想法是产生10k数字 - 替换。然后,在循环中,生成另外10k个数字并检查行方式重复。如果有的话,消除那些并生成那么多随机数。如果我没记错的话,这也称为命中和未命中算法。

这是工作代码:

arr = np.random.choice(365, 10000)
for i in range(49):
    arr2 = np.random.choice(365, 10000)
    comp = (arr2 == arr)
    while comp.any():
        duplicate = comp if i==0 else comp.any(axis=0)
        arr2[duplicate] = np.random.choice(365, duplicate.sum())
        comp = (arr2 == arr)
    arr = np.vstack([arr, arr2])
arr = arr.T
arr.sort(axis=1)

这需要93.4毫秒才能完成。您的计算机在我的计算机上需要590毫秒,因此它提供了大约6倍的改进。

答案 1 :(得分:2)

这是np.argpartition/np.argsort的诀窍。

这个想法是 -

  • 获取一组形状为(10000,365)的浮动随机数组,并在每行上执行argsort。这将为我们提供唯一的索引,从而模拟与replace=False一起使用的np.random.choice条件。

  • 为每行切出第一个50列。

  • 最后,每行的排序都可以完成排序数据的工作。

现在,我们可以使用np.argpartition进一步提升效果,以k=50为每行进行分区。

因此,我们将有一个矢量化解决方案,如此 -

np.sort(np.random.rand(10000,365).argpartition(50,axis=1)[:,:50])

让我们验证输出数据的一致性

In [209]: out = np.sort(np.random.rand(10000,365).argpartition(50,axis=1)[:,:50])

In [210]: count = np.bincount(out.ravel(), minlength=365)

In [211]: print count.min(), count.max()
1277 1466

看起来很漂亮!让我们来看主要业务,即获得业绩数据。

运行时测试

方法 -

# Original approach
def org_app():
    return np.array([np.sort(np.random.choice(365, 50, replace=False)) for _ in range(10000)])

# @Nils Werner's soln
def sort_random_choice():
    return np.sort([np.random.choice(365, 50, replace=False) for _ in range(10000)], axis=1)

# @Miriam Farber's soln
def random_permute():
    l = np.array([True]*50 + [False]*315)
    total = np.arange(1,366)
    return np.array([total[np.random.permutation(l)] for _ in range(10000)])

# Proposed in this post
def argpartition_sort(nrows=10000, maxc=365, ncols=50):
    return np.sort(np.random.rand(nrows,maxc).argpartition(ncols,axis=1)[:,:ncols])

# @ayhan's soln
def while_loop():
    arr = np.random.choice(365, 10000)
    for i in range(49):
        arr2 = np.random.choice(365, 10000)
        comp = (arr2 == arr)
        while comp.any():
            duplicate = comp if i==0 else comp.any(axis=0)
            arr2[duplicate] = np.random.choice(365, duplicate.sum())
            comp = (arr2 == arr)
        arr = np.vstack([arr, arr2])
    arr = arr.T
    arr.sort(axis=1)
    return arr

计时 -

In [44]: %timeit org_app()
    ...: %timeit sort_random_choice()
    ...: %timeit random_permute()
    ...: %timeit argpartition_sort()
    ...: %timeit while_loop()
    ...: 
1 loops, best of 3: 258 ms per loop
1 loops, best of 3: 232 ms per loop
10 loops, best of 3: 166 ms per loop
10 loops, best of 3: 79.9 ms per loop
10 loops, best of 3: 58.6 ms per loop

答案 2 :(得分:1)

一种可能的优化是通过将sort置于循环之外来对其进行矢量化:

sample_dates = np.sort([np.random.choice(365, 50, replace=False) for _ in range(10000)], axis=1)

答案 3 :(得分:1)

以下解决方案不使用sort:

l = np.array([True]*50 + [False]*315)
total = np.arange(1,366)
sample_dates = np.array([total[np.random.permutation(l)] for _ in range(10000)])

因此它似乎比其他建议的解决方案更快(我的计算机需要0.44秒,而#34; Nils Werner"解决方案需要0.77秒.OP的解决方案需要0.81秒)。