numpy 如何在Python中从“一个非常大的数组”快速生成随机数?

kt06eoxx  于 2023-05-07  发布在  Python
关注(0)|答案(3)|浏览(199)

最近,我一直在寻找一种在Python中生成随机数的快速方法。这里像下面的代码一样,我从一个拥有超过1000万个点的区域收集点。我想从区域点中随机抽取8192个点。然而,np.random.choice(np.arange(...))使程序变得如此缓慢,因为索引数组很大。
那么,如何让这个过程更快呢?使用任何有用的Python包或函数?感谢您的关注!

import numpy as np

area_idx = self.area_idxs[idx]
points = self.area_points[area_idx]  # 14350962 * 3
labels = self.area_labels[area_idx]  # 14350962
npoints = 8192
# np.random.choice(np.arange(...)) makes the program so slow.
selected_point_idxs = np.random.choice(np.arange(len(points)), npoints, replace=False)

从Python中的“一个非常大的数组”快速生成随机数。

1wnzp6jl

1wnzp6jl1#

Numpy在文档中提到,新代码应该使用新的随机生成器来生成随机数(参考numpy.random.choice中的注解),新的随机生成器在choice中会有更好的性能:

In [_]: n, k = 14350962, 8192

In [_]: %timeit np.random.choice(np.arange(n), k, replace=False)
772 ms ± 10.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [_]: %timeit np.random.choice(n, k, replace=False)
742 ms ± 8.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [_]: rng = np.random.default_rng()

In [_]: %timeit rng.choice(np.arange(n), k, replace=False)
19.1 ms ± 98.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [_]: %timeit rng.choice(n, k, replace=False)
147 µs ± 687 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

当索引直接由np.arange生成时,只需传递长度即可获得相同的结果,避免了索引的构建。

部分补充

在新的Generator.choice中,对于k很小的情况,numpy团队使用时间复杂度为O(k)的弗洛伊德采样算法,而旧的RandomState.choice总是使用时间复杂度为O(n)的随机排列。

6bc51xsx

6bc51xsx2#

如果你想在Python中从一个大数组中生成随机数,而不使用np.random.choice(因为它很慢),你可以使用一种称为reservoir sampling的技术。
水库采样涉及到初始化一个“样本”列表与数组的第一个k元素,然后迭代数组中的其余元素。对于剩余的每个元素,生成一个介于0和当前元素索引之间的随机数,如果该随机数小于k,则用当前元素替换样本列表中的元素。
示例实施方式:

import random
npoints = 8192
sample = points[:npoints].tolist()
for i in range(npoints, len(points)):
    j = random.randint(0, i)
    if j < npoints:
        sample[j] = points[i].tolist()

这个实现假设points是一个形状为(n, 3)的numpy数组(即,n点的维度为3)。如果您的points数组具有不同的形状,则可能需要相应地修改此实现。

8yparm6h

8yparm6h3#

您可以使用np.take函数。

import numpy as np
a = np.array([
  [1, 2, 3],
  [1, 2, 2],
  [1, 1, 1],
  [2, 2, 2],
  [1, 2, 3]
])
npoints = 3
point_ids = np.random.choice(a.shape[0], npoints)
points = np.take(a, point_ids, axis=0)

# print(points)
# [[1 2 3]
# [1 2 3]
# [2 2 2]]

相关问题