Numpy: Запрос: argmax2d

Созданный на 21 июн. 2017  ·  13Комментарии  ·  Источник: numpy/numpy

Было бы очень хорошо, если бы была удобная функция для выполнения argmax над 2D-массивом и возврата максимальных индексов строк и столбцов.

Я часто замечаю, что повторно использую следующий код

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.

Будут ли какие-либо возражения против открытия PR в numpy, чтобы эта функция была легко доступна?

Самый полезный комментарий

Почему бы не использовать np.unravel_index ?

например

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

В качестве бонуса это работает, когда X имеет более двух измерений.

Все 13 Комментарий

Почему бы не использовать np.unravel_index ?

например

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

В качестве бонуса это работает, когда X имеет более двух измерений.

Не фанат argmax2d , но меня может убедить i, j = a.argmax(axis=(0, 1))

@ eric-wieser, как бы вы сказали, что он должен себя вести? В настоящее время a.argmax() возвращает скаляр, а a.argmax(axis=0) возвращает массив с формой остальных измерений. Я ожидал, что a.argmax(axis=(0,1)) для трехмерного массива вернет массив формы a.shape[2] .

Помимо этой проблемы, было бы немного странно использовать a.argmax(axis=range(n)) в общем случае для получения кортежа индекса длины n вместо целочисленного линейного индекса по умолчанию. Возможно, новое ключевое слово может переключаться между выходными представлениями?

Опять же, как этот вариант результата с учетом формы будет работать вместе с существующим ключевым словом axis уже приводящим к нескалярному возвращаемому значению?

Что делать, если одна из строк в вашем 2D-массиве не имеет максимума (это просто постоянный массив) - как 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 в 1d-массиве:

>>> 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 vs axes=[0] ), но я уверен, что существует множество примеров такого же поведения (к тому же практичность важнее чистоты).

~ На всякий случай: если в вашем примере axes=(0,2) то возвращаемые формы должны быть (B,D,E) , верно?

Просто чтобы убедиться

Оба исправлены, хороший улов

Тот факт, что тип аргумента оси определяет, является ли возвращаемое значение массивом или кортежем массивов, немного удивляет меня.

Обратите внимание, что фактический массив такой же. Я думаю, что это поражает как практичностью, так и чистотой - правило таково: axis=(a,)res = (arr,) и axis=ares = arr . Apis, что особые кортежи размера 1 кажутся мне плохой идеей, поскольку этот специальный корпус должен стать заразительным для всего стека вызовов.

Я бы никогда не подумал о кортежах длины 1 в частном случае, это звучит неправильно. Мне было интересно узнать о случае скаляра и кортежа. Мне смутно пришло в голову, что случаи скалярного кортежа и кортежа длины 1 соответствуют друг другу так, как вы упомянули, но теперь, когда вы это объяснили, очевидно, что скалярные -> 1-кортеж -> общие случаи кортежей являются простыми обобщениями. Так что спасибо, я полностью поддерживаю ваше предложение.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги