Numpy 数组:row/column 具有随机关系的明智 argmax
Numpy arrays: row/column wise argmax with random ties
这是我在 Python 2.7 中尝试用 Numpy 做的事情。假设我有一个数组 a
定义如下:
a = np.array([[1,3,3],[4,5,6],[7,8,1]])
我可以做 a.argmax(0)
或 a.argmax(1)
来获得 row/column 明智的 argmax:
a.argmax(0)
Out[329]: array([2, 2, 1], dtype=int64)
a.argmax(1)
Out[330]: array([1, 2, 1], dtype=int64)
但是,当 a
的第一行出现平局时,我希望在平局之间随机决定 argmax(默认情况下,Numpy return 是第一个元素每当 argmax 或 argmin 出现平局时)。
去年,有人提出了一个关于随机解决 Numpy argmax/argmin 关系的问题:Select One Element in Each Row of a Numpy Array by Column Indices
但是,题目针对的是一维数组。在那里,投票最多的答案对此很有效。还有第二个答案也试图解决多维数组的问题但不起作用 - 即它不 return,对于每个 row/column 随机解决的最大值索引。
因为我使用的是大数组,所以执行此操作的最有效方法是什么?
一种简单的方法是在开始时将一个小的随机数添加到所有值中,因此您的数据将如下所示:
a = np.array([[1.1827,3.1734,3.9187],[4.8172,5.7101,6.9182],[7.1834,8.5012,1.9818]])
这可以通过 a = a + np.random.random(a.shape)
来完成。
如果以后需要取回原始值,可以a.astype(int)
删除小数部分。
通用案例解决方案,每组选一个
为了解决从指定选择范围的 list/array 数字中选择随机数的一般情况,我们将使用创建统一随机数组的技巧,添加由间隔指定的偏移量长度,然后执行 argsort
。实现看起来像这样 -
def random_num_per_grp(L):
# For each element in L pick a random number within range specified by it
r1 = np.random.rand(np.sum(L)) + np.repeat(np.arange(len(L)),L)
offset = np.r_[0,np.cumsum(L[:-1])]
return r1.argsort()[offset] - offset
示例案例-
In [217]: L = [5,4,2]
In [218]: random_num_per_grp(L) # i.e. select one per [0-5,0-4,0-2]
Out[218]: array([2, 0, 1])
因此,输出将具有与输入 L
中相同数量的元素,并且第一个输出元素将在 [0,5)
中,第二个在 [0,4)
中,依此类推。
在这里解决我们的问题
为了解决我们这里的情况,我们会使用一个修改版本(特别是删除 func 末尾的偏移量删除部分,就像这样 -
def random_num_per_grp_cumsumed(L):
# For each element in L pick a random number within range specified by it
# The final output would be a cumsumed one for use with indexing, etc.
r1 = np.random.rand(np.sum(L)) + np.repeat(np.arange(len(L)),L)
offset = np.r_[0,np.cumsum(L[:-1])]
return r1.argsort()[offset]
方法 #1
一个解决方案可以像这样使用它-
def argmax_per_row_randtie(a):
max_mask = a==a.max(1,keepdims=1)
m,n = a.shape
all_argmax_idx = np.flatnonzero(max_mask)
offset = np.arange(m)*n
return all_argmax_idx[random_num_per_grp_cumsumed(max_mask.sum(1))] - offset
验证
让我们对给定样本进行大量运行并计算每行每个索引的出现次数
In [235]: a
Out[235]:
array([[1, 3, 3],
[4, 5, 6],
[7, 8, 1]])
In [225]: all_out = np.array([argmax_per_row_randtie(a) for i in range(10000)])
# The first element (row=0) should have similar probabilities for 1 and 2
In [236]: (all_out[:,0]==1).mean()
Out[236]: 0.504
In [237]: (all_out[:,0]==2).mean()
Out[237]: 0.496
# The second element (row=1) should only have 2
In [238]: (all_out[:,1]==2).mean()
Out[238]: 1.0
# The third element (row=2) should only have 1
In [239]: (all_out[:,2]==1).mean()
Out[239]: 1.0
方法 #2:使用 masking
提高性能
我们可以使用 masking
并因此避免 flatnonzero
以提高性能,因为通常使用布尔数组。此外,我们将概括为同时覆盖行 (axis=1) 和列 (axis=0) 以给我们自己一个修改过的,就像这样 -
def argmax_randtie_masking_generic(a, axis=1):
max_mask = a==a.max(axis=axis,keepdims=True)
m,n = a.shape
L = max_mask.sum(axis=axis)
set_mask = np.zeros(L.sum(), dtype=bool)
select_idx = random_num_per_grp_cumsumed(L)
set_mask[select_idx] = True
if axis==0:
max_mask.T[max_mask.T] = set_mask
else:
max_mask[max_mask] = set_mask
return max_mask.argmax(axis=axis)
样本在 axis=0
和 axis=1
-
上运行
In [423]: a
Out[423]:
array([[1, 3, 3],
[4, 5, 6],
[7, 8, 1]])
In [424]: argmax_randtie_masking_generic(a, axis=1)
Out[424]: array([1, 2, 1])
In [425]: argmax_randtie_masking_generic(a, axis=1)
Out[425]: array([2, 2, 1])
In [426]: a[1,1] = 8
In [427]: a
Out[427]:
array([[1, 3, 3],
[4, 8, 6],
[7, 8, 1]])
In [428]: argmax_randtie_masking_generic(a, axis=0)
Out[428]: array([2, 1, 1])
In [429]: argmax_randtie_masking_generic(a, axis=0)
Out[429]: array([2, 1, 1])
In [430]: argmax_randtie_masking_generic(a, axis=0)
Out[430]: array([2, 2, 1])
您可以使用一个随机数数组,其形状与您的输入相同,但屏蔽该数组以仅留下候选者供选择。
import numpy as np
def rndArgMax(a, axis):
a_max = a.max(axis, keepdims=True)
tmp = np.random.random(a.shape) * (a == a_max)
return tmp.argmax(axis)
a = np.random.randint(0, 3, size=(2, 3, 4))
print(rndArgMax(a, 1))
# array([[1, 1, 2, 1],
# [0, 1, 1, 1]])
这是我在 Python 2.7 中尝试用 Numpy 做的事情。假设我有一个数组 a
定义如下:
a = np.array([[1,3,3],[4,5,6],[7,8,1]])
我可以做 a.argmax(0)
或 a.argmax(1)
来获得 row/column 明智的 argmax:
a.argmax(0)
Out[329]: array([2, 2, 1], dtype=int64)
a.argmax(1)
Out[330]: array([1, 2, 1], dtype=int64)
但是,当 a
的第一行出现平局时,我希望在平局之间随机决定 argmax(默认情况下,Numpy return 是第一个元素每当 argmax 或 argmin 出现平局时)。
去年,有人提出了一个关于随机解决 Numpy argmax/argmin 关系的问题:Select One Element in Each Row of a Numpy Array by Column Indices
但是,题目针对的是一维数组。在那里,投票最多的答案对此很有效。还有第二个答案也试图解决多维数组的问题但不起作用 - 即它不 return,对于每个 row/column 随机解决的最大值索引。
因为我使用的是大数组,所以执行此操作的最有效方法是什么?
一种简单的方法是在开始时将一个小的随机数添加到所有值中,因此您的数据将如下所示:
a = np.array([[1.1827,3.1734,3.9187],[4.8172,5.7101,6.9182],[7.1834,8.5012,1.9818]])
这可以通过 a = a + np.random.random(a.shape)
来完成。
如果以后需要取回原始值,可以a.astype(int)
删除小数部分。
通用案例解决方案,每组选一个
为了解决从指定选择范围的 list/array 数字中选择随机数的一般情况,我们将使用创建统一随机数组的技巧,添加由间隔指定的偏移量长度,然后执行 argsort
。实现看起来像这样 -
def random_num_per_grp(L):
# For each element in L pick a random number within range specified by it
r1 = np.random.rand(np.sum(L)) + np.repeat(np.arange(len(L)),L)
offset = np.r_[0,np.cumsum(L[:-1])]
return r1.argsort()[offset] - offset
示例案例-
In [217]: L = [5,4,2]
In [218]: random_num_per_grp(L) # i.e. select one per [0-5,0-4,0-2]
Out[218]: array([2, 0, 1])
因此,输出将具有与输入 L
中相同数量的元素,并且第一个输出元素将在 [0,5)
中,第二个在 [0,4)
中,依此类推。
在这里解决我们的问题
为了解决我们这里的情况,我们会使用一个修改版本(特别是删除 func 末尾的偏移量删除部分,就像这样 -
def random_num_per_grp_cumsumed(L):
# For each element in L pick a random number within range specified by it
# The final output would be a cumsumed one for use with indexing, etc.
r1 = np.random.rand(np.sum(L)) + np.repeat(np.arange(len(L)),L)
offset = np.r_[0,np.cumsum(L[:-1])]
return r1.argsort()[offset]
方法 #1
一个解决方案可以像这样使用它-
def argmax_per_row_randtie(a):
max_mask = a==a.max(1,keepdims=1)
m,n = a.shape
all_argmax_idx = np.flatnonzero(max_mask)
offset = np.arange(m)*n
return all_argmax_idx[random_num_per_grp_cumsumed(max_mask.sum(1))] - offset
验证
让我们对给定样本进行大量运行并计算每行每个索引的出现次数
In [235]: a
Out[235]:
array([[1, 3, 3],
[4, 5, 6],
[7, 8, 1]])
In [225]: all_out = np.array([argmax_per_row_randtie(a) for i in range(10000)])
# The first element (row=0) should have similar probabilities for 1 and 2
In [236]: (all_out[:,0]==1).mean()
Out[236]: 0.504
In [237]: (all_out[:,0]==2).mean()
Out[237]: 0.496
# The second element (row=1) should only have 2
In [238]: (all_out[:,1]==2).mean()
Out[238]: 1.0
# The third element (row=2) should only have 1
In [239]: (all_out[:,2]==1).mean()
Out[239]: 1.0
方法 #2:使用 masking
提高性能
我们可以使用 masking
并因此避免 flatnonzero
以提高性能,因为通常使用布尔数组。此外,我们将概括为同时覆盖行 (axis=1) 和列 (axis=0) 以给我们自己一个修改过的,就像这样 -
def argmax_randtie_masking_generic(a, axis=1):
max_mask = a==a.max(axis=axis,keepdims=True)
m,n = a.shape
L = max_mask.sum(axis=axis)
set_mask = np.zeros(L.sum(), dtype=bool)
select_idx = random_num_per_grp_cumsumed(L)
set_mask[select_idx] = True
if axis==0:
max_mask.T[max_mask.T] = set_mask
else:
max_mask[max_mask] = set_mask
return max_mask.argmax(axis=axis)
样本在 axis=0
和 axis=1
-
In [423]: a
Out[423]:
array([[1, 3, 3],
[4, 5, 6],
[7, 8, 1]])
In [424]: argmax_randtie_masking_generic(a, axis=1)
Out[424]: array([1, 2, 1])
In [425]: argmax_randtie_masking_generic(a, axis=1)
Out[425]: array([2, 2, 1])
In [426]: a[1,1] = 8
In [427]: a
Out[427]:
array([[1, 3, 3],
[4, 8, 6],
[7, 8, 1]])
In [428]: argmax_randtie_masking_generic(a, axis=0)
Out[428]: array([2, 1, 1])
In [429]: argmax_randtie_masking_generic(a, axis=0)
Out[429]: array([2, 1, 1])
In [430]: argmax_randtie_masking_generic(a, axis=0)
Out[430]: array([2, 2, 1])
您可以使用一个随机数数组,其形状与您的输入相同,但屏蔽该数组以仅留下候选者供选择。
import numpy as np
def rndArgMax(a, axis):
a_max = a.max(axis, keepdims=True)
tmp = np.random.random(a.shape) * (a == a_max)
return tmp.argmax(axis)
a = np.random.randint(0, 3, size=(2, 3, 4))
print(rndArgMax(a, 1))
# array([[1, 1, 2, 1],
# [0, 1, 1, 1]])