Numpy: Demande : argmax2d

Créé le 21 juin 2017  ·  13Commentaires  ·  Source: numpy/numpy

Ce serait vraiment bien s'il y avait une fonction pratique pour effectuer un argmax sur un tableau 2D et renvoyer les index de ligne et de colonne du maximum.

Je me retrouve souvent à réutiliser le code suivant

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

Bien qu'il soit simple, il est suffisamment opaque pour que je doive constamment rechercher cette fonction.
Ce serait bien si cela était déjà disponible dans numpy.

Y aurait-il des objections à ouvrir un PR dans numpy pour que cette fonctionnalité soit facilement disponible ?

Commentaire le plus utile

Pourquoi ne pas utiliser np.unravel_index ?

par exemple

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

En prime, cela fonctionne également lorsque X a plus de 2 dimensions.

Tous les 13 commentaires

Pourquoi ne pas utiliser np.unravel_index ?

par exemple

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

En prime, cela fonctionne également lorsque X a plus de 2 dimensions.

Pas fan de argmax2d , mais je pourrais me laisser convaincre par i, j = a.argmax(axis=(0, 1))

@eric-wieser comment diriez-vous que cela devrait se comporter? Actuellement, a.argmax() renvoie un scalaire et a.argmax(axis=0) renvoie un tableau avec la forme des dimensions restantes. Je m'attendrais à ce que a.argmax(axis=(0,1)) pour un tableau 3D renvoie un tableau de forme a.shape[2] .

Mis à part ce problème, il serait un peu étrange d'utiliser a.argmax(axis=range(n)) dans le cas général pour obtenir un tuple d'index de longueur n au lieu de l'index linéaire entier par défaut. Peut-être qu'un nouveau mot-clé pourrait basculer entre les représentations de sortie ?

Encore une fois, comment cette option de résultat tenant compte de la forme fonctionnerait-elle avec le mot-clé axis existant, entraînant déjà une valeur de retour non scalaire ?

Que se passe-t-il si l'une des lignes de votre tableau 2D n'a pas de maximum (c'est simplement un tableau constant) - comment argmax peut-il le signaler ?
Par exemple:

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

Ce serait bien d'avoir un argument supplémentaire par exemple pour laisser l'utilisateur contrôler ce qu'il faut faire dans le cas où il n'y a pas de max disponible : np.argmax(x,axis,no_ind=0) (0 est la valeur par défaut pour préserver la compatibilité descendante).

@thoth291 n'est-ce pas une question plus générique concernant le comportement de argmax ? La même chose se produit sans argument de mot-clé axis sur un tableau 1d :

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

Et c'est le comportement habituel en cas d'égalité : choisir la première parmi les valeurs liées. De même comment le max de ce tableau n'est pas nan : c'est 2. Et si vous avez un max, vous devez avoir un index correspondant. Ou ai-je raté votre point?

@adeak , maintenant vous avez dit que - je commence à penser qu'il s'agit bien d'une question plus générale.
Je peux convenir que max([2,2,2]) est égal à 2 .
Mais pensez à argmax([2,2,2]) - selon la définition de la documentation numpy, il renvoie the indices corresponding to the first occurrence . Cela semble être OK du point de vue de l'implémentation - mais ce n'est en réalité qu'un archaïsme de l'algorithme et n'a rien à voir avec ce qui devrait réellement se passer. En fait, N'IMPORTE QUEL index peut être renvoyé et doit être traité comme correct. Ou on peut dire que argmax est ambigu dans le cas d'un tableau à valeur constante.

Cela étant dit, je préfère éviter une telle décision en premier lieu et retourner inf pour signaler qu'il appartient à l'utilisateur de décider comment traiter un tel cas.

et n'a rien à voir avec ce qui se passe réellement.

Voulez-vous dire « n'a rien à voir avec la définition abstraite d'argmax » ? J'espère bien que _"l'algorithme"_ et _"ce qui se passe réellement"_ ne sont pas seulement la même chose, mais qu'ils correspondent aux documents.

@adeak : Désolé de ne jamais répondre

Encore une fois, comment cette option de résultat tenant compte de la forme fonctionnerait-elle avec le mot-clé d'axe existant résultant déjà en une valeur de retour non scalaire ?

Je pense qu'il y a une façon évidente de traiter cela. Par exemple:

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

Avec ça et keepdims, vous obtiendriez arr[argmax(arr, axes, keepdims=True)] == max(arr, keepdims=True) pour n'importe quelle dimensionnalité, ce qui me semble super-souhaitable

En pseudocode, je m'attendrais à :

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 - belle prise - je l'ai tapé plus vite que je n'ai pu traduire correctement. Sera prudent plus tard. Mise à jour du commentaire en « devrait arriver » au lieu de « arrive ». J'espère que cela aide, sinon - ouvert aux suggestions de reformulation.
Le fait est que argmax([2,2,2])==0 est aussi bon que argmax([2,2,2])==1 ou autre chose et il doit être choisi par l'utilisateur plutôt que par la bibliothèque. Sinon, un mécanisme de secours devrait être fourni sous la forme (par exemple) d'un mot-clé supplémentaire default= ou initial= qui indiquerait ce qu'il faut retourner dans ce cas.

@thoth291 : puisque ce dont vous parlez n'est pas spécifique à _2D_ argmax, je vous suggère de créer un nouveau problème

@eric-wieser pas de soucis, merci de me répondre. Je pense que je comprends votre suggestion, et elle semble sans ambiguïté et pratique. Le fait que le type de l'argument axis détermine si la valeur de retour est un tableau ou un tuple de tableaux me surprend un peu (surtout axes=0 vs axes=[0] ), mais je suis sûr qu'il existe de nombreux exemples existants pour le même comportement (de plus, l'aspect pratique bat la pureté).

~Juste pour être sûr : dans votre exemple, si axes=(0,2) alors les formes renvoyées devraient être (B,D,E) , n'est-ce pas ?

Juste pour être sûr

Les deux corrigés, bonne prise

Le fait que le type de l'argument d'axe détermine si la valeur de retour est un tableau ou un tuple de tableaux est un peu surprenant pour moi

Notez que le tableau réel est le même. Je pense que cela touche à la fois l'aspect pratique et la pureté - la règle est que axis=(a,)res = (arr,) et axis=ares = arr . Apis que les tuples de cas spéciaux de taille 1 me semblent être une mauvaise idée, car ce cas spécial doit devenir contagieux dans toute la pile d'appels

Je n'aurais jamais pensé à des tuples de longueur 1, ça sonne faux. Je m'interrogeais sur le cas scalaire vs tuple. Il m'est vaguement venu à l'esprit que les cas de tuple scalaire et de longueur-1 se correspondent de la manière que vous avez mentionnée, mais maintenant que vous l'avez expliqué, il est évident que les cas de tuple scalaire -> 1-tuple -> général sont des généralisations simples. Alors merci, je soutiens pleinement votre suggestion.

Cette page vous a été utile?
0 / 5 - 0 notes