繁体   English   中英

将 numpy 数组与矩阵的每一行进行比较以计算相似项(矢量化)

[英]Compare numpy array to every row of matrix to count similar items (vectorized)

我在玩宾果游戏,我从 50 个可能的球中选择了 15 个球(没有替换)。

然后抽取 30 个球(不更换),如果我的 15 个球在这组抽取的 30 个球中,我将获得奖品。

我想通过多次模拟来计算中奖的概率,最好是矢量化。

所以到目前为止,这是我的代码:

import numpy as np

my_chosen_balls = np.random.choice(range(1,51), 15)
samples_30_balls = np.random.choice(range(1,51), (1_000_000, 30))

我如何将我选择的 15 个球与这 30 个球样本中的任何一个进行比较,看看是否我的所有球都被选中了?
因此,将我的 15 个球与每个样品分别进行比较。

这是一个较小的示例,用于可视化:

my_chosen_balls = np.array([7, 4, 3])
sample_5_balls = np.array([[5, 5, 5, 6, 6, 4, 1, 1, 1, 8, 2, 3, 2, 8, 8],
                           [1, 9, 1, 3, 4, 8, 5, 4, 7, 2, 8, 6, 5, 6, 4],
                           [7, 3, 6, 9, 8, 3, 6, 9, 3, 1, 6, 5, 3, 1, 7],
                           [8, 4, 3, 2, 9, 5, 3, 8, 4, 6, 9, 2, 6, 5, 9],
                           [3, 2, 8, 5, 1, 9, 2, 5, 8, 4, 5, 1, 7, 4, 6]])

有几种方法可以做到这一点。 由于您只有 15 个选项,因此您可以使用np.isin

mask = np.isin(sample_5_balls, my_chosen_balls).sum(0) == my_chosen_balls.size

如果你想要成功的百分比:

np.count_nonzero(mask) / sample_5_balls.shape[1]

问题是您无法使用np.random.choicenp.random.Generator.choice等工具轻松生成samples_30_ballssample_5_balls等数组。 有一些可用的解决方案,例如Numpy random choice, replacement only along axis ,但它们仅适用于少量项目。

相反,您可以使用排序和切片来获得您想要的内容,如此处和此处所示

sample_30_balls = np.random.rand(50, 100000).argsort(0)[:30, :]

您需要将数字加 1 以显示,但对于答案的其余部分,从零开始的 go 会容易得多。

如果您的人口规模保持在 64 人或以下,您可以使用 bit twiddling 使一切运行得更快。 首先将数据转换为单个数字数组:

sample_30_bits = (1 << sample_30_balls).sum(axis=0)

这两个操作等同于

sample_30_bits = np.bitwise_or.reduce((2**sample_30_balls), axis=0)

单个样本是单个 integer 具有此方案:

my_chosen_bits = (1 << np.random.rand(50).argsort()[:15]).sum()

np.isin现在非常简单:它只是按位 AND ( & )。 您可以使用我在这里写的快速bit_count function(逐字复制):

def bit_count(arr):
     # Make the values type-agnostic (as long as it's integers)
     t = arr.dtype.type
     mask = t(-1)
     s55 = t(0x5555555555555555 & mask)  # Add more digits for 128bit support
     s33 = t(0x3333333333333333 & mask)
     s0F = t(0x0F0F0F0F0F0F0F0F & mask)
     s01 = t(0x0101010101010101 & mask)

     arr = arr - ((arr >> 1) & s55)
     arr = (arr & s33) + ((arr >> 2) & s33)
     arr = (arr + (arr >> 4)) & s0F
     return (arr * s01) >> (8 * (arr.itemsize - 1))

pct = (bit_count(my_chosen_bits & sample_30_bits) == 15).sum() / sample_30_bits.size

但还有更多:现在您不仅可以为 30 个球生成大量样本,还可以为 15 个球生成大量样本。 一种替代方法是生成相同数量的样本,并将它们进行一对一比较:

N = 100000
sample_15_bits = (1 << np.random.rand(50, N).argsort(0)[:15, :]).sum(0)
sample_30_bits = (1 << np.random.rand(50, N).argsort(0)[:30, :]).sum(0)
pct = (bit_count(sample_15_bits & sample_30_bits) == 15).sum() / N

另一种选择是为每个数量生成可能不同的 arrays 个样本,并将所有样本相互比较。 这将需要更多的结果空间,因此我将针对较小的输入显示它:

M = 100
N = 5000
sample_15_bits = (1 << np.random.rand(50, M).argsort(0)[:15, :]).sum(0)
sample_30_bits = (1 << np.random.rand(50, N).argsort(0)[:30, :]).sum(0)
pct = (bit_count(sample_15_bits[:, None] & sample_30_bits) == 15).sum() / (M * N)

如果您需要针对空间进行优化(例如,使用真正大的样本量),请记住这里的所有操作都使用 ufuncs,除了np.random.randargsort 因此,您可以在不创建临时 arrays 的情况下就地完成大部分工作。这将作为练习留给读者。

另外,我建议您绘制bit_count(sample_15_bits & sample_30_bits)的直方图来调整您的期望值。 这是上面最后一个示例的计数直方图:

y = np.bincount(bit_count(sample_15_bits[:, None] & sample_30_bits).ravel())
x = np.arange(y.size)
plt.bar(x, y)

在此处输入图像描述

请注意 15 处的条形有多小。 我在写这个答案时看到pct的值在 7e-5 左右,但我懒得计算理论值。

使用isin计算相交值并与 15 进行比较。我将数据生成更改为无替换样本。

import numpy as np
np.random.seed(10)

my_chosen_balls = np.random.choice(range(0,50), 15, replace=False)
samples_30_balls = np.random.rand(1_000_000,50).argsort(1)[:,:30]

(np.isin(samples_30_balls, my_chosen_balls).sum(1) == 15).sum()

Output

74

所以大约有0.007%的机会。


如何在没有替换的情况下生成数据样本

在形状samples, range中生成[0,1)中的随机值。 这里有 10 个来自[0,1,2,3,4]的样本

np.random.rand(10,5)

出去

array([[0.37216438, 0.16884495, 0.05393551, 0.68189535, 0.30378455],
       [0.63428637, 0.6566772 , 0.16162259, 0.16176099, 0.74568611],
       [0.81452942, 0.10470267, 0.89547322, 0.60099124, 0.22604322],
       [0.16562083, 0.89936513, 0.89291548, 0.95578207, 0.90790727],
       [0.11326867, 0.18230934, 0.44912596, 0.65437732, 0.78308136],
       [0.72693801, 0.22425798, 0.78157525, 0.93485338, 0.84097546],
       [0.96751432, 0.57735756, 0.48147214, 0.22441829, 0.53388467],
       [0.95415338, 0.07746658, 0.93875458, 0.21384035, 0.26350969],
       [0.39937711, 0.35182801, 0.74707871, 0.07335893, 0.27553172],
       [0.80749372, 0.40559599, 0.33654045, 0.14802479, 0.71198915]]

使用argsort将“转换”为整数

np.random.rand(10,5).argsort(1)

出去

array([[4, 2, 1, 0, 3],
       [0, 1, 3, 2, 4],
       [1, 3, 2, 4, 0],
       [4, 0, 2, 3, 1],
       [2, 3, 0, 1, 4],
       [1, 4, 3, 2, 0],
       [4, 3, 2, 0, 1],
       [1, 0, 2, 3, 4],
       [4, 1, 2, 3, 0],
       [1, 4, 0, 2, 3]])

切片到所需的样本大小

np.random.rand(10,5).argsort(1)[:,:3]

出去

array([[2, 3, 4],
       [0, 4, 3],
       [3, 0, 4],
       [2, 0, 3],
       [2, 3, 4],
       [3, 4, 2],
       [2, 0, 1],
       [0, 4, 3],
       [0, 2, 3],
       [2, 3, 4]])

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM