Numpy: Pedido: argmax2d

Criado em 21 jun. 2017  ·  13Comentários  ·  Fonte: numpy/numpy

Seria muito bom se houvesse uma função de conveniência para executar um argmax sobre uma matriz 2D e retornar os índices de linha e coluna do máximo.

Muitas vezes me vejo reutilizando o seguinte código

def argmax2d(X):
    n, m = X.shape
    x_ = np.ravel(X)
    k = np.argmax(x_)
    i, j = k // m, k % m
    return i, j

Embora seja simples, é opaco o suficiente para que eu tenha que procurar consistentemente essa função.
Seria bom se isso já estivesse disponível em numpy.

Haveria alguma objeção à abertura de um PR no numpy para ter essa funcionalidade prontamente disponível?

Comentários muito úteis

Por que não usar np.unravel_index ?

por exemplo

np.unravel_index(X.argmax(), X.shape)

Como bônus, também funciona quando X tem mais de 2 dimensões.

Todos 13 comentários

Por que não usar np.unravel_index ?

por exemplo

np.unravel_index(X.argmax(), X.shape)

Como bônus, também funciona quando X tem mais de 2 dimensões.

Não sou fã de argmax2d , mas poderia ser persuadido por i, j = a.argmax(axis=(0, 1))

@ eric-wieser, como você diria que isso deveria se comportar? Atualmente a.argmax() retorna um escalar e a.argmax(axis=0) retorna uma matriz com a forma das dimensões restantes. Eu esperaria que a.argmax(axis=(0,1)) para um array 3d retornasse um array de forma a.shape[2] .

Deixando esse problema de lado, seria um pouco estranho usar a.argmax(axis=range(n)) no caso geral para obter uma tupla de índice de n -length em vez do índice linear inteiro padrão. Talvez uma nova palavra-chave pudesse alternar entre as representações de saída?

Então, novamente, como essa opção de resultado ciente de forma funcionaria junto com a palavra-chave axis já resultando em um valor de retorno não escalar?

E se uma das linhas em sua matriz 2D não tiver um máximo (é simplesmente uma matriz constante) - como argmax pode relatar isso?
Por exemplo:

# 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]

Seria bom ter um argumento extra, por exemplo, para permitir que o usuário controle o que fazer no caso sem máximo disponível: np.argmax(x,axis,no_ind=0) (0 é o padrão para preservar a compatibilidade com versões anteriores).

@ thoth291 não é uma pergunta mais genérica em relação ao comportamento de argmax ? A mesma coisa acontece sem nenhum argumento de palavra-chave axis em uma matriz 1d:

>>> np.argmax([2,2,2])
0

E este é o comportamento habitual em caso de empate: escolha o primeiro entre os valores empatados. Da mesma forma como max daquele array não é nan : é 2. E se você tem um máximo, você tem que ter um índice correspondente. Ou eu perdi o seu ponto?

@adeak , agora você disse isso - estou começando a pensar que essa é realmente uma questão mais geral.
Posso concordar que max([2,2,2]) é igual a 2 .
Mas pense sobre argmax ([2,2,2]) - de acordo com a definição na documentação numpy, ele retorna the indices corresponding to the first occurrence . Isso parece certo do ponto de vista da implementação - mas na verdade é apenas um arcaísmo do algoritmo e não tem nada a ver com o que realmente deve acontecer. Efetivamente, QUALQUER índice pode ser retornado e deve ser tratado como correto. Ou pode-se dizer que argmax é ambíguo no caso de uma matriz de valor constante.

Dito isso, prefiro evitar tal decisão em primeiro lugar e retornar inf para sinalizar que cabe ao usuário decidir como tratar esse caso.

e não tem nada a ver com o que realmente acontece.

Você quer dizer "não tem nada a ver com a definição abstrata de argmax"? Espero que _ "o algoritmo" _ e _ "o que realmente acontece" _ não sejam apenas a mesma coisa, mas que correspondam aos documentos.

@adeak : Desculpe por nunca responder

Então, novamente, como essa opção de resultado ciente de forma funcionaria junto com a palavra-chave de eixo existente já resultando em um valor de retorno não escalar?

Acho que há uma maneira óbvia de lidar com isso. Como um exemplo:

>>> 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)

Com isso e manter as regras, você obteria arr[argmax(arr, axes, keepdims=True)] == max(arr, keepdims=True) por qualquer dimensionalidade, o que me parece muito desejável

Em pseudocódigo, eu esperaria:

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 - nice catch - Eu digitei mais rápido do que fui capaz de traduzir corretamente. Terá cuidado mais tarde. Atualizado o comentário para "deve acontecer" em vez de "acontece". Espero que ajude, caso contrário - aberto para sugestões de reformulação.
O ponto é que argmax ([2,2,2]) == 0 é tão bom quanto argmax ([2,2,2]) == 1 ou qualquer outra coisa e deve ser escolhido pelo usuário em vez da biblioteca. Caso contrário, um mecanismo de fallback deve ser fornecido na forma de (por exemplo) palavra-chave extra default= ou initial= que instruiria o que retornar neste caso.

@ thoth291 : já que o que você está discutindo não é específico para _2D_ argmax, sugiro que você crie um novo problema

@ eric-wieser não se preocupe, obrigado por entrar em contato comigo. Acho que entendo sua sugestão, e ela parece inequívoca e prática. O fato de que o tipo do argumento axis determina se o valor de retorno é uma matriz ou uma tupla de matrizes é um pouco surpreendente para mim (especialmente axes=0 vs axes=[0] ), mas tenho certeza de que há muitos exemplos existentes para o mesmo comportamento (além disso, a praticidade supera a pureza).

~ Apenas para ter certeza: em seu exemplo, se axes=(0,2) então as formas retornadas devem ser (B,D,E) , certo?

Só pra ter certeza

Ambos corrigidos, boa pegada

O fato de o tipo do argumento do eixo determinar se o valor de retorno é uma matriz ou uma tupla de matrizes é um pouco surpreendente para mim

Observe que a matriz real é a mesma. Acho que isso atinge tanto praticidade quanto pureza - a regra é que axis=(a,)res = (arr,) e axis=ares = arr . Apis, aquelas tuplas de casos especiais de tamanho 1 me parecem uma má ideia, já que esse caso especial tem que se tornar contagioso em toda a pilha de chamadas

Eu nunca teria pensado em casos especiais de tuplas de comprimento 1, isso parece errado. Eu queria saber sobre o caso escalar vs tupla. Vagamente me ocorreu que os casos de tupla escalar e de comprimento 1 correspondem um ao outro da maneira que você mencionou, mas agora que você soletrou é óbvio que os casos escalar -> 1 tupla -> tupla geral são generalizações diretas. Então, obrigado, apoio totalmente sua sugestão.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

keithbriggs picture keithbriggs  ·  3Comentários

ghost picture ghost  ·  4Comentários

marcocaccin picture marcocaccin  ·  4Comentários

dmvianna picture dmvianna  ·  4Comentários

astrofrog picture astrofrog  ·  4Comentários