Numpy索引将1设置为最大值,将零设置为0

时间:2017-05-23 19:26:33

标签: python numpy multidimensional-array

我认为我在numpy中误解了索引。

我有一个形状为(dim_x, dim_y, dim_z)的3D-numpy数组,我希望沿第三轴(dim_z)找到最大值,并将其值设置为1,将其他所有值设置为零。

问题在于,即使值不同,我最终会在同一行中使用多个1。

以下是代码:

>>> test = np.random.rand(2,3,2)
>>> test
array([[[ 0.13110146,  0.07138861],
        [ 0.84444158,  0.35296986],
        [ 0.97414498,  0.63728852]],

       [[ 0.61301975,  0.02313646],
        [ 0.14251848,  0.91090492],
        [ 0.14217992,  0.41549218]]])

>>> result = np.zeros_like(test)
>>> result[:test.shape[0], np.arange(test.shape[1]), np.argmax(test, axis=2)]=1
>>> result
array([[[ 1.,  0.],
        [ 1.,  1.],
        [ 1.,  1.]],

       [[ 1.,  0.],
        [ 1.,  1.],
        [ 1.,  1.]]])

我期待以:

结束
array([[[ 1., 0.],
        [ 1., 0.],
        [ 1., 0.]],

       [[ 1., 0.],
        [ 0., 1.],
        [ 0., 1.]]])

可能我在这里遗漏了一些东西。根据我的理解,0:dim_x, np.arange(dim_y)会返回dim_xdim_y元组,np.argmax(test, axis=dim_z)的形状为(dim_x, dim_y),因此如果索引的形式为[x, y, z] {1}}一对[x, y]不应该出现两次。

有人能解释我哪里错了吗?提前谢谢。

3 个答案:

答案 0 :(得分:1)

我们在寻找什么

我们沿最后一轴获得argmax指数 -

idx = np.argmax(test, axis=2)

对于给定的样本数据,我们有idx

array([[0, 0, 0],
       [0, 1, 1]])

现在,idx涵盖第一和第二轴,同时获得argmax个索引。

要在输出中指定相应的那些,我们需要为前两个轴创建范围数组,覆盖长度和根据idx 的形状对齐。现在,idx是一个2D形状(m,n)数组,其中m = test.shape[0]n = test.shape[1]

因此,用于分配到前两个输出轴的范围数组必须是 -

X = np.arange(test.shape[0])[:,None]
Y = np.arange(test.shape[1])

注意,需要将第一个范围数组扩展到2D,以使其与idx的行对齐,而Y将与idx的列对齐 -

In [239]: X
Out[239]: 
array([[0],
       [1]])

In [240]: Y
Out[240]: array([0, 1, 2])

示意放 -

idx :
    Y array
    --------->
    x x x | X array
    x x x |
          v

原始代码中的错误

您的代码是 -

result[:test.shape[0], np.arange(test.shape[1]), ..

这基本上是:

result[:, np.arange(test.shape[1]), ...

因此,您选择沿第一轴的所有元素,而不是仅选择对应于idx索引的相应元素。在这个过程中,您选择的元素多于分配所需的元素,因此您在1s数组中看到的不仅仅是result所需的内容。

更正

因此,唯一需要的修正是使用范围数组索引到第一个轴,并且工作解决方案将是 -

result[np.arange(test.shape[0])[:,None], np.arange(test.shape[1]), ...

备选方案

或者,使用之前使用XY -

创建的范围数组
result[X,Y,idx] = 1

获取X,Y的另一种方法是使用np.mgrid -

m,n = test.shape[:2]
X,Y = np.ogrid[:m,:n]

答案 1 :(得分:0)

这是一种更简单的方法:

>>>  test == test.max(axis=2, keepdims=1)
array([[[ True, False],
        [ True, False],
        [ True, False]],

       [[ True, False],
        [False,  True],
        [False,  True]]], dtype=bool)

...如果你真的想要它作为浮点1.0和0.0,那么转换它:

>>> (test==test.max(axis=2, keepdims=1)).astype(float)
array([[[ 1.,  0.],
        [ 1.,  0.],
        [ 1.,  0.]],

       [[ 1.,  0.],
        [ 0.,  1.],
        [ 0.,  1.]]])

这是一种方法,每行只有一个获胜者 - 列组合(即没有关系,如评论中所述):

rowmesh, colmesh = np.meshgrid(range(test.shape[0]), range(test.shape[1]), indexing='ij')
maxloc = np.argmax(test, axis=2)
flatind = np.ravel_multi_index( [rowmesh, colmesh, maxloc ], test.shape )
result = np.zeros_like(test)
result.flat[flatind] = 1

阅读hpaulj的答案后更新:

rowmesh, colmesh = np.ix_(range(test.shape[0]), range(test.shape[1]))

是我meshgrid调用的一种更高效,更简洁的替代方案(其余代码保持不变)

为什么你的方法失败的问题很难解释,但这里有一个直觉可以开始的地方:你的切片方法说“所有行,所有列的次数,时间是一定层次的” 。这个切片总共有多少个元素?相比之下,您实际想要将多少个元素设置为1?查看您尝试分配给的切片的相应test值时,可以看到所获得的值,这是有益的:

>>> test[:, :, maxloc].shape
(2, 3, 2, 3)   # oops!  it's because maxloc itself is 2x3

>>> test[:, :, maxloc]
array([[[[ 0.13110146,  0.13110146,  0.13110146],
         [ 0.13110146,  0.07138861,  0.07138861]],

        [[ 0.84444158,  0.84444158,  0.84444158],
         [ 0.84444158,  0.35296986,  0.35296986]],

        [[ 0.97414498,  0.97414498,  0.97414498],
         [ 0.97414498,  0.63728852,  0.63728852]]],


       [[[ 0.61301975,  0.61301975,  0.61301975],
         [ 0.61301975,  0.02313646,  0.02313646]],

        [[ 0.14251848,  0.14251848,  0.14251848],
         [ 0.14251848,  0.91090492,  0.91090492]],

        [[ 0.14217992,  0.14217992,  0.14217992],
         [ 0.14217992,  0.41549218,  0.41549218]]]])  # note the repetition, because in maxloc you're repeatedly asking for layer 0 sometimes, and sometimes repeatedly for layer 1

答案 2 :(得分:0)

我认为混合基本(切片)和高级索引存在问题。从数组中选择值比使用此赋值更容易看到;但它可能导致转置轴。对于这样的问题,最好使用ix_

提供的高级索引
In [24]: test = np.random.rand(2,3,2)
In [25]: idx=np.argmax(test,axis=2)
In [26]: idx
Out[26]: 
array([[1, 0, 1],
       [0, 1, 1]], dtype=int32)

基础和高级:

In [31]: res1 = np.zeros_like(test)
In [32]: res1[:, np.arange(test.shape[1]), idx]=1
In [33]: res1
Out[33]: 
array([[[ 1.,  1.],
        [ 1.,  1.],
        [ 0.,  1.]],

       [[ 1.,  1.],
        [ 1.,  1.],
        [ 0.,  1.]]])

高级:

In [35]: I,J = np.ix_(range(test.shape[0]), range(test.shape[1]))
In [36]: I
Out[36]: 
array([[0],
       [1]])
In [37]: J
Out[37]: array([[0, 1, 2]])
In [38]: res2 = np.zeros_like(test)
In [40]: res2[I, J , idx]=1
In [41]: res2
Out[41]: 
array([[[ 0.,  1.],
        [ 1.,  0.],
        [ 0.,  1.]],

       [[ 1.,  0.],
        [ 0.,  1.],
        [ 0.,  1.]]])

进一步考虑,如果目标是设置或找到6个argmax值,则使用第一维的切片是错误的

In [54]: test
Out[54]: 
array([[[ 0.15288242,  0.36013289],
        [ 0.90794601,  0.15265616],
        [ 0.34014976,  0.53804266]],

       [[ 0.97979479,  0.15898605],
        [ 0.04933804,  0.89804999],
        [ 0.10199319,  0.76170911]]])
In [55]: test[I, J, idx]
Out[55]: 
array([[ 0.36013289,  0.90794601,  0.53804266],
       [ 0.97979479,  0.89804999,  0.76170911]])

In [56]: test[:, J, idx]
Out[56]: 
array([[[ 0.36013289,  0.90794601,  0.53804266],
        [ 0.15288242,  0.15265616,  0.53804266]],

       [[ 0.15898605,  0.04933804,  0.76170911],
        [ 0.97979479,  0.89804999,  0.76170911]]])

使用切片,它从test(或res)中选择(2,3,2)一组值,而不是预期的(2,3)。还有2行。