np.random.choice与np.rando.shuffle的比较,用于不替换的样本



我的用例有点特定我想从列表/数组(50或100个元素(中采样2个不替换的项。所以我不必担心大小为10^4或10^5的数组或多维数据。

我想知道

  1. numpy.random.choice()numpy.random.shuffle()中哪一个更快,为什么
  2. 如果它们都产生了"0"的随机样本;质量好";?也就是说,两者都是为了我的目的生成良好的随机样本,还是生成较少的随机样本?(只是为了确保我没有忽略这些函数的源代码(

对于问题1,我尝试了对这两个函数进行计时(下面的代码(,shuffle方法似乎快了5-6倍。欢迎您对此提供任何见解。如果有更快的方法来实现我的目的,我会很高兴听到它们(我已经看过pythonrandom模块的选项,但我测试中最快的方法是使用np.random.shuffle()(。

def shuffler(size, num_samples):
items = list(range(size))
np.random.shuffle(items)
return items[:num_samples]

def chooser(size, num_samples):
return np.random.choice(size, num_samples, replace=False)
%timeit shuffler(50, 2)
#> 1.84 µs ± 17.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit chooser(50, 2)
#> 13 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

你可能认为它已经优化了,我在浪费时间试图节省开支。但是np.random.choice()在我的代码中被调用了5000000次,占用了我运行时的8%。它被用于循环中,以从群体中获得每次迭代的2个随机样本。伪码:

for t in range(5000000):
# Random sample of 2 from the population without replacement.

如果有更智能的实现来满足我的需求,我愿意接受建议。

PS:我知道shuffle执行就地操作,但由于我只需要两个随机元素的索引,所以基本上不必在我的原始数组上执行。还有其他问题可以比较pythonrandom模块中的两个函数。但我需要2个样品,不需要更换。

回答您的问题:

  1. shuffle似乎是最快的实现
  2. 它应该给出相同的答案(事实上,它似乎是同一件事(

让我们从这里开始@SvenManach的回答。这不是对那个问题的欺骗,但答案是有用的。不幸的是,这个答案并没有使其与shuffler时间一致:

%timeit shuffler(50, 2)
2.47 µs ± 180 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit chooser(50, 2)
52.5 µs ± 3.58 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

rng = np.random.default_rng()
def chooser2(size, num_samples):
return rng.choice(size, num_samples, replace=False)
%timeit chooser2(50, 2)
15.9 µs ± 1.41 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)

random.sample的答案更好:

import random 
def sampler(size, num_samples):
return np.array(random.sample(range(size), num_samples))
%timeit sampler(50, 2)
4.6 µs ± 140 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)    

不过速度还是比较慢。

由于我无法解析c代码,我将接受sven的说法,即random.choice在后台进行shuffle和split,因此这些方法应该是等效的。为什么这里的速度如此之快让我感到困惑。

编辑:基于@DaniMesejo的答案的sample_indices(num_samples = 2稍慢(:

def sample_indices(pop, size, num_samples):
arr = np.random.rand(pop, size)
return np.argpartition(arr, num_samples, axis = 1)[:, :num_samples] 

请参阅numpy.random.choice的源代码;使用replace=False,它创建一个50项的临时列表,打乱该列表,并从该列表中获取两项。

自1.17版本以来,numpy.random.choicenumpy.random.shuffle的实现决策与其他numpy.random函数一样,不能在不影响向后兼容性的情况下进行更改(请参阅最近针对NumPy的RNG策略(。另请参阅以下问题:

  • 为什么random.sample比numpy';随机选择
  • 为什么numpy.random.choice不使用算术编码
  • numpy.srandom.seed((每次都给出相同的随机数吗

numpy.random.choicenumpy.random.Generator.choice进行比较,这是NumPy 1.17及更高版本中对项目进行采样的较新方法。优点是numpy.random.Generator.choice不受与numpy.random.choicenumpy.random.shuffle相同的兼容性保证的约束。如果您关心numpy.random.Generator的性能,您可以在NumPy的GitHub存储库中提交问题。

您可以使用另一种解决方案,其想法是生成一个随机数组,然后找到最小值和最大值的位置:

import numpy as np

def sample_indices(ran, size):
arr = np.random.rand(ran, size)
mi = np.argmin(arr, axis=1).reshape((-1, 1))
ma = np.argmax(arr, axis=1).reshape((-1, 1))
return np.hstack((mi, ma))

def shuffler(size, num_samples):
items = list(range(size))
np.random.shuffle(items)
return items[:num_samples]

def chooser(size, num_samples):
return np.random.choice(size, num_samples, replace=False)

def sample_indices_shuffler(ran, size):
return np.array([shuffler(size, 2) for _ in range(ran)])

def sample_indices_chooser(ran, size):
return np.array([chooser(size, 2) for _ in range(ran)])

以下是时间安排:

%timeit sample_indices_chooser(1000, 50)
17.3 ms ± 1.74 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit sample_indices_shuffler(1000, 50)
2.69 ms ± 215 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit sample_indices(1000, 50)
553 µs ± 22.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

用于:

res = sample_indices(10, 50)
print(res)

输出

[[ 9  6]
[31 42]
[17 42]
[24 45]
[ 2 49]
[27 31]
[21 19]
[ 7 16]
[20 28]
[32 36]]

numpy针对大型数组进行了更好的优化。通过标准库中的random模块进行拒绝采样,使其速度大约是最佳OP的两倍。

硬编码num_choices=2的一个例子可能是:

from random import randrange
def randrange_two(size):
v1 = randrange(size)
v2 = randrange(size)
while v1 == v2:
v2 = randrange(size)
return v1, v2

这在我的笔记本电脑上运行约0.7µs,而shuffler运行1.7µs。请注意,将结果放入numpy数组会将速度减慢到与shuffler相同的速度。

不确定这有多有用,但认为它值得发布。

最新更新