Python 为什么random.sample比numpy';什么是随机选择?

Python 为什么random.sample比numpy';什么是随机选择?,python,numpy,random,Python,Numpy,Random,我需要一种无需更换特定数组的方法a。我尝试了两种方法(参见下面的MCVE),使用random.sample()和np.random.choice 我假设numpy函数会更快,但事实并非如此。在我的测试中,random.sample比np.random.choice快约15% 这是正确的,还是我在下面的例子中做错了什么?如果这是正确的,为什么 import numpy as np import random import time from contextlib import contextman

我需要一种无需更换特定数组的方法
a
。我尝试了两种方法(参见下面的MCVE),使用
random.sample()
np.random.choice

我假设
numpy
函数会更快,但事实并非如此。在我的测试中,
random.sample
np.random.choice
快约15%

这是正确的,还是我在下面的例子中做错了什么?如果这是正确的,为什么

import numpy as np
import random
import time
from contextlib import contextmanager


@contextmanager
def timeblock(label):
    start = time.clock()
    try:
        yield
    finally:
        end = time.clock()
        print ('{} elapsed: {}'.format(label, end - start))


def f1(a, n_sample):
    return random.sample(range(len(a)), n_sample)


def f2(a, n_sample):
    return np.random.choice(len(a), n_sample, replace=False)


# Generate random array
a = np.random.uniform(1., 100., 10000)
# Number of samples' indexes to randomly take from a
n_sample = 100
# Number of times to repeat functions f1 and f2
N = 100000

with timeblock("random.sample"):
    for _ in range(N):
        f1(a, n_sample)

with timeblock("np.random.choice"):
    for _ in range(N):
        f2(a, n_sample)

TL;DR自numpy v1.17.0以来,建议使用
numpy.random.default\rng()
对象,而不是
numpy.random
。供选择:

将numpy导入为np
rng=np.random.default_rng()#您可以传递种子
rng.choice(…)#接口相同
除了v1.17中引入的随机API的其他更改之外,这个新版本的choice现在更加智能,在大多数情况下应该是最快的。为了向后兼容,旧版本保持不变



正如评论中所提到的,numpy长期存在一个关于
np.random.choice
实现对
k无效的问题。我明白了,这是一个长期存在的问题。请@ayhan回复你的评论,这样我就可以把它标记为接受了。我知道这个问题,因为它在这里出现过几次,但我真的不知道为什么它会慢一些。我的答案可能是一个链接和一些引号,但最好是等待其他人,也许他们可以解释这个问题?我认为问题在于,
np.random.choice
通过生成数组中所有索引的排列,然后获取其中的第一个
n_样本
,进行随机采样而不进行替换(请参阅)。如果
n_sample
远小于数组
a
中的元素数,则这将变得非常低效。另一方面,
random.sample
仅绘制
n_samples
随机样本。它通过两种方式之一实现这一点——或者通过跟踪它已经选择的项目(如果
n_样本
random.sample elapsed: 0.8711776689742692
np.random.choice elapsed: 1.9704092079773545
np.random.default_rng().choice elapsed: 0.818919860990718
random.sample elapsed: 8.785315042012371
np.random.choice elapsed: 1.9777243090211414
np.random.default_rng().choice elapsed: 1.05490942299366
random.sample elapsed: 80.15063399000792
np.random.choice elapsed: 2.0218082449864596
np.random.default_rng().choice elapsed: 2.8596064270241186
import numpy as np
import random
from timeit import default_timer as timer
from contextlib import contextmanager


@contextmanager
def timeblock(label):
    start = timer()
    try:
        yield
    finally:
        end = timer()
        print ('{} elapsed: {}'.format(label, end - start))


def f1(a, n_sample):
    return random.sample(range(len(a)), n_sample)


def f2(a, n_sample):
    return np.random.choice(len(a), n_sample, replace=False)


def f3(a, n_sample):
    return np.random.default_rng().choice(len(a), n_sample, replace=False)


# Generate random array
a = np.random.uniform(1., 100., 10000)
# Number of samples' indexes to randomly take from a
n_sample = 100
# Number of times to repeat tested functions
N = 100000

print(f'{N} times {n_sample} samples')
with timeblock("random.sample"):
    for _ in range(N):
        f1(a, n_sample)

with timeblock("np.random.choice"):
    for _ in range(N):
        f2(a, n_sample)

with timeblock("np.random.default_rng().choice"):
    for _ in range(N):
        f3(a, n_sample)