如果有一个方便的函数来对 2D 数组执行 argmax 并返回最大值的行和列索引,那就太好了。
我经常发现自己重复使用以下代码
def argmax2d(X):
n, m = X.shape
x_ = np.ravel(X)
k = np.argmax(x_)
i, j = k // m, k % m
return i, j
虽然它很简单,但它足够不透明,我必须不断地查找这个函数。
如果这已经在 numpy 中可用,那就太好了。
是否会反对在 numpy 中打开 PR 以使此功能随时可用?
为什么不使用np.unravel_index
?
例如
np.unravel_index(X.argmax(), X.shape)
作为奖励,它也适用于X
具有超过 2 个维度的情况。
不是argmax2d
的粉丝,但我可以被i, j = a.argmax(axis=(0, 1))
说服
@eric-wieser 你会怎么说这应该表现? 目前a.argmax()
返回一个标量,而a.argmax(axis=0)
返回一个具有剩余维度形状的数组。 我希望a.argmax(axis=(0,1))
用于 3d 数组返回形状a.shape[2]
的数组。
撇开这个问题不谈,在一般情况下使用a.argmax(axis=range(n))
来获取n
-length 索引元组而不是默认的整数线性索引会有点奇怪。 也许一个新的关键字可以在输出表示之间切换?
再说一次,这个形状感知结果选项如何与已经产生非标量返回值的现有axis
关键字一起工作?
如果二维数组中的行之一没有最大值(它只是常量数组)怎么办 - argmax 如何报告?
例如:
# setup the problem
import numpy as np
x=np.arange(10).reshape([2,5])
x[1,3]=2
# x is this:
# [[0, 1, 2, 3, 4],
# [5, 6, 7, 2, 9]]
# this will behave normally
np.argmax(x==2, axis=1)
# Out: [2,3]
# but this one returns 0 instead of NaN
np.argmax(x==3, axis=1)
# Out: [3,0]
# Expected: [3, nan]
最好有一个额外的参数,例如让用户控制在没有可用最大值的情况下做什么: np.argmax(x,axis,no_ind=0)
(默认为 0 以保持向后兼容性)。
@thoth291这不是关于argmax
行为的更通用的问题吗? 在一维数组上没有axis
关键字参数会发生同样的事情:
>>> np.argmax([2,2,2])
0
这是联系情况下的习惯行为:选择联系值中的第一个。 同样,该数组的max
不是nan
:它是 2。如果你有一个最大值,你必须有一个相应的索引。 还是我错过了你的观点?
@adeak ,现在你这么说 - 我开始认为这确实是一个更普遍的问题。
我同意max([2,2,2])
等于2
。
但是想想 argmax([2,2,2]) - 根据 numpy 文档中的定义,它返回the indices corresponding to the first occurrence
。 从实现的角度来看,这似乎没问题——但这实际上只是算法的陈词滥调,与实际应该发生的事情无关。 实际上,任何索引都可以返回并且应该被视为正确的。 或者可以说 argmax 在常量值数组的情况下是不明确的。
话虽如此,我宁愿首先避免这样的决定并返回inf
以表示由用户决定如何处理这种情况。
并且与实际发生的事情无关。
您的意思是“与 argmax 的抽象定义无关”? 我当然希望 _“算法”_ 和 _“实际发生的事情”_ 不仅是一回事,而且它们与文档相匹配。
@adeak :抱歉一直没有回复
再说一次,这个形状感知结果选项如何与已经产生非标量返回值的现有轴关键字一起工作?
我认为有一种显而易见的方法可以解决这个问题。 举个例子:
>>> ret = argmax(np.empty((A, B, C, D, E)), axes=(0, 2))
>>> type(ret)
tuple
>>> len(ret) # == len(axes)
2
>>> ret[0].shape
(B, D, E)
>>> ret[1].shape
(B, D, e)
有了它和 keepdims,你会得到arr[argmax(arr, axes, keepdims=True)] == max(arr, keepdims=True)
的任何维度,这对我来说似乎是非常可取的
在伪代码中,我希望:
def argmax(arr, axes, keepdims)
was_tuple = isinstance(axes, tuple):
if not was_tuple:
axes = (axes,)
shape = np.array(arr.shape)
axis_mask = np.array([i in axes for i in range(arr.ndim)])
shape[axis_mask] = 1
ret = tuple(np.empty(shape) for _ in axes)
# do the actual work
if not keepdims:
ret = tuple(r.reshape(shape[~axis_mask]) for r in ret)
if not was_tuple:
return ret[0]
@eric-wieser - 不错的收获 - 我输入的速度比正确翻译的速度要快。 以后要小心了。 将评论更新为“应该发生”而不是“发生”。 希望有所帮助,否则 - 对重新制定的建议开放。
关键是 argmax([2,2,2])==0 与 argmax([2,2,2])==1 或其他任何东西一样好,它应该由用户而不是库选择。 否则,应以(例如)额外关键字default=
或initial=
的形式提供回退机制,这将指示在这种情况下返回的内容。
@thoth291 :由于您讨论的内容并非特定于 _2D_ argmax,我建议您创建一个新问题
@eric-wieser 不用担心,感谢您回复我。 我想我理解你的建议,而且它看起来确实明确且实用。 axis
参数的类型决定返回值是数组还是数组元组这一事实让我有点惊讶(尤其是axes=0
与axes=[0]
),但我相信有很多相同行为的现有示例(而且,实用性胜过纯度)。
~只是为了确定:在你的例子中,如果 axes=(0,2)
那么返回的形状应该是(B,D,E)
,对吧?
只是要确定
两者都纠正了,很好的捕获
轴参数的类型决定返回值是数组还是数组元组这一事实让我有点惊讶
请注意,实际数组是相同的。 我认为这既符合实用性又符合纯度 - 规则是axis=(a,)
→ res = (arr,)
和axis=a
→ res = arr
。 API 大小为 1 的特殊情况元组让我觉得是个坏主意,因为这种特殊的大小写必须在整个调用堆栈中传染
我从没想过要对长度为 1 的元组进行特殊处理,这听起来是错误的。 我想知道标量与元组的情况。 我确实模糊地想到,标量和长度为 1 的元组情况以您提到的方式相互对应,但是现在您将其拼写出来,很明显标量 -> 1-元组 -> 一般元组情况是简单的概括。 所以谢谢,我完全支持你的建议。
最有用的评论
为什么不使用
np.unravel_index
?例如
作为奖励,它也适用于
X
具有超过 2 个维度的情况。