[英]In Python, how can I compare a numpy array to each row of a matrix to chose the row that is most similar to the vector?
[英]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.choice
或np.random.Generator.choice
等工具轻松生成samples_30_balls
或sample_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.rand
和argsort
。 因此,您可以在不创建临时 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.