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分布来选择么?请大神不吝赐教。。。

参考技术A dict对象只有keys函数。

无替换样本的 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.choicenumpy.random.shuffle 的实现决策与其他 numpy.random 函数一样,在不影响向后兼容性的情况下无法更改(请参阅最近的 RNG policy for NumPy)。另请参阅以下问题:

Why is random.sample faster than numpy's random.choice? Why does numpy.random.choice not use arithmetic coding? Does numpy.random.seed() always give the same random number every time?

比较 numpy.random.choicenumpy.random.Generator.choice,这是在 NumPy 1.17 及更高版本中对项目进行采样的新方法。优点是numpy.random.Generator.choice 不受与numpy.random.choicenumpy.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.choicenumpy.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。这虽然不可忽略,但可能不足以让我改变我的实现。 感谢使用 argminargmax 实现大小为 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()的问题的主要内容,如果未能解决你的问题,请参考以下文章

未选择的numpy random.choice 元素

[ python知识 ] 关于随机抽样函数random

Pandas 随机加权选择

Pandas 随机加权选择

没有替换概率的抽样

Pytorch 的随机选择?