python下关于np.random.choice()的问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python下关于np.random.choice()的问题相关的知识,希望对你有一定的参考价值。
代码如下:
for seed in np.arange(10):
np.random.seed(seed)
print(np.random.choice(5,p=[0.1, 0, 0.3, 0.6, 0]))
运行结果:
3
3
3
3
3
2
3
0
3
0
对于运行结果相当不解,本来就指定了choice方法的概率分布参数p了,为什么选择结果仍受seed的影响,不说好了设定p后,就只按p分布来选择么?请大神不吝赐教。。。
无替换样本的 np.random.choice 与 np.random.shuffle 的比较
【中文标题】无替换样本的 np.random.choice 与 np.random.shuffle 的比较【英文标题】:Comparison of np.random.choice vs np.random.shuffle for samples without replacement 【发布时间】:2021-03-20 22:35:52 【问题描述】:我的用例有点具体。 我想从列表/数组中抽取 2 个项目而不进行替换(包含 50 个或 100 个元素)。所以我不必担心大小为 10^4 或 10^5 的数组或多维数据。
我想知道
numpy.random.choice()
或 numpy.random.shuffle()
哪个更快,为什么?
如果他们都产生“质量好”的随机样本?也就是说,两者都是为了我的目的生成好的随机样本,还是产生较少的随机样本? (只是为了确保我没有忽略有关这些函数的源代码的完整性检查)。
对于问题 1,我尝试对这两个函数进行计时(代码如下),并且 shuffle 方法似乎快了大约 5-6 倍。非常欢迎您对此提供任何见解。如果有更快的方法来实现我的目的,我会很高兴听到它们(我查看了 python random
模块的选项,但我测试中最快的方法是使用 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
执行就地操作,但由于我只需要两个随机元素的索引,因此我基本上不必在原始数组上执行它。还有其他questions 比较python random
模块中的两个函数。但我需要 2 个无需更换的样品。
【问题讨论】:
@DaniMesejo 是的。随后的样本是独立的。 【参考方案1】:回答您的问题:
shuffle
似乎是最快的实现方式
它应该给出相同的答案(事实上,它似乎是同一件事)
让我们开始@SvenMarnach 的回答here。这不是该问题的欺骗,但答案很有用。不幸的是,这个答案与shuffler
timewise 不一致:
%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
正在后台进行随机和拆分,因此这些方法应该是等效的。不过,为什么这里的速度如此之快让我感到莫名其妙。
编辑:sample_indices
基于@DaniMesejo 的回答(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]
【讨论】:
感谢您的建议。不过,我现在还有另一个问题。rng = np.random.default_rng(); def chooser2(size, num_samples): return rng.choice(size, num_samples, replace=False)
似乎在 #> 16.9 µs ± 1.1 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
上做得更好。知道为什么吗?我应该将 rng 声明放在循环内部还是外部?以下哪一项是“正确”/“更好”的做事方式?我不习惯 numpy.random 中的新生成器
尽量避免在 SO 上使用“正确”或“更好”之类的词,这是一个很好的方式来结束您的问题作为征求意见。 sampler
更快,并且应该和chooser
一样随机,因为它的代码相同。 为什么奇怪的是,当相同的代码作为底层代码时会更快。
对不起,我不想用这些词。但无论如何,你知道我应该将rng=np.random.default_rng()
选项留在函数内部还是外部?好像声明只需要做一次?
据我所知,生成器只需要实例化一次,是的,所以你可以把它放在函数之外。【参考方案2】:
查看numpy.random.choice
的源代码;使用replace=False
,它会创建一个包含 50 项的临时列表,对该列表进行打乱,并从该列表中取出两项。
从 1.17 版开始,numpy.random.choice
和 numpy.random.shuffle
的实现决策与其他 numpy.random
函数一样,在不影响向后兼容性的情况下无法更改(请参阅最近的 RNG policy for NumPy)。另请参阅以下问题:
比较 numpy.random.choice
和 numpy.random.Generator.choice
,这是在 NumPy 1.17 及更高版本中对项目进行采样的新方法。优点是numpy.random.Generator.choice
不受与numpy.random.choice
或numpy.random.shuffle
相同的兼容性保证。如果您关心numpy.random.Generator
的性能,您可以在 NumPy 的 GitHub 存储库中提出问题。
【讨论】:
rng = np.random.default_rng(); rng.choice(size, num_samples, replace=False)
仍然比 shuffle
慢,即 . . .有点莫名其妙
优点是numpy.random.Generator.choice
不受numpy.random.choice
或numpy.random.shuffle
相同的兼容性保证。如果您关心numpy.random.Generator
的性能,您可以在 NumPy 的 GitHub 存储库中提出问题。
谢谢@PeterO。我将尝试使用新的生成器方法并进行比较。让我也看看你分享的资源。【参考方案3】:
您可以使用另一种解决方案,其想法是生成一个随机数组,然后找到最小值和最大值的位置:
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]]
【讨论】:
感谢您的有趣建议。虽然有些争论。您在sample_indices_shuffler()
中添加的np.array()
的类型转换似乎是不必要的,因为这些只是我正在处理的索引。对于一次生成 1 个样本 (ran=1
),即使在删除 np.hstack()
之后,sample_indices()
函数似乎比 sample_indices(1,50) #>5.69 µs ± 349 ns per loop
慢于 sample_indices_shuffler(1, 50) #>2.69 µs ± 236 ns per loop
。
@Rithwik 但是你想要 1 还是 50000? sample_indices
背后的想法是生成 50000 对,而不仅仅是 1
您建议的性能提升似乎来自一起生成大量样本。但是,如果我将我的算法更改为这个实现而不是一次一个样本,我得到的增益是我的代码总运行时间大约 7 秒。 %timeit sample_indices(5000000,50) #> 2.97 s ± 30.9 ms per loop
与 %timeit sample_indices_shuffler(5000000, 50) #>10.4 s ± 133 ms per loop
。这虽然不可忽略,但可能不足以让我改变我的实现。
感谢使用 argmin
和 argmax
实现大小为 2 的随机选择器的方法。这真有趣。我会为其他用例保留它。【参考方案4】:
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
相同的速度。
不确定这有多大用处,但认为值得发布。
【讨论】:
感谢@Sam 这个方法。我不需要输出在一个 numpy 数组中,所以这已经足够了。我曾尝试过类似的功能实现,但我使用了np.random.randint
,这似乎要糟糕得多。然而randrange
似乎是一个不错的选择。虽然,在我的笔记本电脑中,增益比你得到的要小(对于 50 的大小)。 %timeit randrange_two(50) #> 1.49 µs ± 89.1 ns per loop
%timeit shuffler(50, 2) #> 1.85 µs ± 39.9 ns per loop
但是,对于更大的尺寸,randrange
的扩展性似乎要好得多! shuffler
似乎与大小成线性关系,但 randrange
花费的时间几乎相同(甚至可能随着大小而改进)。 %timeit randrange_two(500) #> 1.42 µs ± 69.9 ns per loop
%timeit shuffler(500, 2) #> 16.5 µs ± 240 ns per loop
。所以这对我来说可能是一个更好的解决方案,因为我可能必须使用 50-500 的大小。
@Rithwik 以防万一,我使用的是 Python 3.8.6 和 numpy 版本 1.19.4以上是关于python下关于np.random.choice()的问题的主要内容,如果未能解决你的问题,请参考以下文章