Numpy: редуктор угловой (Trac # 236)

Созданный на 19 окт. 2012  ·  49Комментарии  ·  Источник: numpy/numpy

_Оригинальный билет http://projects.scipy.org/numpy/ticket/236 от 07.08.2006 пользователем trac martin_wiechert, назначен unknown._

.reduceat неправильно обрабатывает повторяющиеся индексы. Когда индекс повторяется, должен быть возвращен нейтральный элемент операции. В приведенном ниже примере ожидается [0, 10], а не [1, 10].

In [1]:import numpy

In [2]:numpy.version.version
Out[2]:'1.0b1'

In [3]:a = numpy.arange (5)

In [4]:numpy.add.reduceat (a, (1,1))
Out[4]:array([ 1, 10])
01 - Enhancement 23 - Wish List numpy.core

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

Основная мотивация для reduceat - избежать зацикливания на reduce для максимальной скорости. Поэтому я не совсем уверен, что обертка цикла for над reduce была бы очень полезным дополнением к Numpy. Это будет противоречить основной цели reduceat .

Более того, логика существования reduceat и API в качестве быстрой векторизованной замены цикла над reduce чиста и полезна. Я бы не стал его осуждать, а скорее исправлю.

Что касается скорости reduceat , давайте рассмотрим простой пример, похожий на некоторые реальные случаи, которые у меня есть в моем собственном коде, где я использую reduceat :

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

Это разница во времени более чем в 100 раз и иллюстрирует важность сохранения эффективности reduceat .

Таким образом, я бы предпочел исправление reduceat введению новых функций.

Наличие start_indices и end_indices , хотя и полезных в некоторых случаях, часто является избыточным, и я бы рассматривал это как возможное дополнение, но не как исправление для текущего несогласованного reduceat поведение.

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

_ @ teoliphant написал (а)

К сожалению, возможно, метод reduceat NumPy следует поведению метода reduceat Numeric для этого углового случая.

В случаях равенства индексов нет возможности вернуть элемент "идентичности" операции. Определенное поведение заключается в возврате элемента, заданного первым индексом, если срез возвращает пустую последовательность. Следовательно, документированное и фактическое поведение reduceat в этом случае состоит в построении

[a [1], add.reduce (a [1:])]

Это запрос функции.

Пользователь _trac martin_wiechert написал 2008-08-08_

также см. билет № 835

Milestone изменен на 1.1 пользователем @alberts 12

Milestone изменен на Unscheduled пользователем @cournape 2

Я думаю, это тесно связано с # 835: если один из индексов len(a) , reduceat не может вывести элемент по этому индексу, что необходимо, если появляется индекс len(a) или повторяется в конце индексов.

Некоторые решения:

  • опция reduceat чтобы не устанавливать никакого значения в выводе, где end - start == 0
  • возможность установить вывод на заданное фиксированное значение, где end - start == 0
  • параметр where , как в ufunc() , который маскирует, какие выходы должны вообще вычисляться.

Были ли еще мысли по этому поводу? Мне было бы интересно иметь возможность установить для вывода значение идентификатора (если оно существует), где end - start == 0.

Я решительно поддерживаю изменение поведения reduceat предложенное в этой давней открытой проблеме. Это похоже на явную ошибку или очевидную ошибку дизайна, которая мешает полезности этой замечательной конструкции Numpy.

reduceat должен вести себя одинаково для всех индексов. А именно, для каждого индекса i ufunc.reduceat(a, indices) должен возвращать ufunc.reduce(a[indices[i]:indices[i+1]]) .

Это также должно быть верно для случая indices[i] == indices[i+1] . Я не вижу разумной причины, почему в этом случае reduceat должно возвращать a[indices[i]] вместо ufunc.reduce(a[indices[i]:indices[i+1]]) .

Смотрите также ЗДЕСЬ аналогичный комментарий создателя Pandas Уэса МакКинни .

Вау, это действительно ужасно и сломано.
.
Нам понадобится обсуждение в списке рассылки, но я, по крайней мере,
полностью за то, чтобы сделать это предупреждение FutureWarning в следующем выпуске
и исправление поведения несколькими выпусками позже. Нам нужен кто-то, чтобы взять
начать обсуждение и написать патч. Может, это ты?

Спасибо за поддерживающий ответ. Я могу начать обсуждение, если это поможет, но, к сожалению, я не до исправления кода C.

Что вы собираетесь делать для ufuncs без личности, например np.maximum?

Для таких функций пустая редукция должна быть ошибкой, так как она уже есть
когда вы используете .reduce () вместо .reduceat ().

В самом деле, поведение должно определяться согласованностью с ufunc.reduce(a[indices[i]:indices[i+1]]) , чего и ожидал бы каждый пользователь. Так что для этого не требуется новых дизайнерских решений. Для меня это действительно похоже на давно существующее исправление ошибки. Если никто не сможет оправдать текущее непоследовательное поведение.

@njsmith Я не могу зарегистрироваться в списке Numpy. Я отправил свой адрес сюда https://mail.scipy.org/mailman/listinfo/numpy-discussion, но я никогда не получаю «письмо с запросом подтверждения». Не уверен, нужны ли особые требования для подписки ...

@divenex : вы проверяли папку со спамом? (Я всегда забываю об этом ...) Иначе я не уверен, что может пойти не так. Определенно не должно быть никаких особых требований для подписки, кроме «иметь адрес электронной почты». Если вы все еще не можете заставить его работать, сообщите об этом, и мы попытаемся отследить соответствующего системного администратора ... Мы определенно хотим знать, не работает ли он.

Версия reduceat , соответствующая ufunc.reduce(a[indices[i]:indices[i+1]]) была бы действительно хороша. Было бы намного полезнее! Либо аргумент для выбора поведения, либо новая функция ( reduce_intervals ? reduce_segments ? ...?) Позволит избежать нарушения обратной несовместимости.

У меня, возможно, возникнет соблазн полностью отказаться от np.ufunc.reduceat - кажется более полезным иметь возможность указать набор начальных и конечных индексов, чтобы избежать случаев, когда indices[i] > indices[i+1] . Кроме того, имя at предполагает гораздо большее сходство с at чем существует на самом деле.

В качестве замены я бы предложил np.piecewise_reduce np.reducebins , возможно, чистый питон, который в основном делает:

def reducebins(func, arr, start=None, stop=None, axis=-1, out=None):
    """
    Compute (in the 1d case) `out[i] = func.reduce(arr[start[i]:stop[i]])`

    If only `start` is specified, this computes the same reduce at `reduceat` did:

        `out[i]  = func.reduce(arr[start[i]:start[i+1]])`
        `out[-1] = func.reduce(arr[start[-1]:])`

    If only `stop` is specified, this computes:

        `out[0] = func.reduce(arr[:stop[0]])`
        `out[i] = func.reduce(arr[stop[i-1]:stop[i]])`

    """
    # convert to 1d arrays
    if start is not None:
        start = np.array(start, copy=False, ndmin=1, dtype=np.intp)
        assert start.ndim == 1
    if stop is not None:
        stop = np.array(stop, copy=False, ndmin=1, dtype=np.intp)
        assert stop.ndim == 1

    # default arguments that do useful things
    if start is None and stop is None:
        raise ValueError('At least one of start and stop must be specified')
    elif stop is None:
        # start only means reduce from one index to the next, and the last to the end
        stop = np.empty_like(start)
        stop[:-1] = start[1:]
        stop[-1] = arr.shape[axis]
    elif start is None:
        # stop only means reduce from the start to the first index, and one index to the next
        start = np.empty_like(stop)
        start[1:] = stop[:-1]
        start[0] = 0
    else:
        # TODO: possibly confusing?
        start, stop = np.broadcast_arrays(start, stop)

    # allocate output - not clear how to do this safely for subclasses
    if not out:
        sh = list(arr.shape)
        sh[axis] = len(stop)
        sh = tuple(sh)
        out = np.empty(shape=sh)

    # below assumes axis=0 for brevity here
    for i, (si, ei) in enumerate(zip(start, stop)):
        func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
    return out

Который имеет приятные свойства:

  • np.add.reduce(arr) совпадает с np.piecewise_reduce(np.add, arr, 0, len(arr))
  • np.add.reduceat(arr, inds) совпадает с np.piecewise_reduce(np.add, arr, inds)
  • np.add.accumulate(arr) совпадает с np.piecewise_reduce(np.add, arr, 0, np.arange(len(arr)))

Теперь, нужно ли это пройти через механизм __array_ufunc__ ? Большая часть того, что необходимо обработать, уже должна быть покрыта func.reduce - единственная проблема - это строка np.empty , которая является проблемой, которую разделяет np.concatenate .

Для меня это хорошее решение с точки зрения API. Достаточно даже просто указать два набора индексов для reduceat . С точки зрения реализации? Что ж, не очень сложно изменить текущий PyUFunc_Reduceat для поддержки наличия двух наборов индексов, если это дает преимущество. Если мы действительно видим преимущество в эффективной поддержке варианта использования, подобного накоплению, это тоже не составит труда.

Нечто подобное Мартен предложил в аналогичном обсуждении из ~ 1
год назад, но он также упомянул возможность добавления опции «шаг»:

http://numpy-discussion.10968.n7.nabble.com/Behavior-of-reduceat-td42667.html

Что мне нравится (так что +1, если кто считает) из вашего предложения:

  • Создание новой функции вместо попытки спасти существующую
    один.
  • Делаем аргументы начального и конечного индексов конкретными, а не
    волшебным образом вычисляя их из многомерного массива.
  • Значения по умолчанию для индексов None очень удобны.

Я думаю, что для этой новой функции важно хорошенько подумать:

  • Должны ли мы сделать «шаг» вариантом? (Я бы сказал да)
  • Имеет ли смысл транслировать массивы индексов, или они должны
    быть 1D?
  • Это должна быть функция np или метод ufunc? (Я думаю, что предпочитаю это
    как метод)

А из отдела литья велосипедов мне больше нравятся:

  • Дайте ему более запоминающееся имя, но у меня нет предложений.
  • Используйте 'start' и 'stop' (и 'step', если мы решим пойти на это) для
    согласованность с np.arange и фрагментом Python.
  • Удаление _indices из имен kwarg.

Хайме

13 апреля 2017 г., в 13:47, Эрик Визер [email protected]
написал:

Возможно, у меня возникнет соблазн вообще отказаться от np.ufunc.reduceat - это
кажется более полезным иметь возможность указать набор начальных и конечных индексов, чтобы
избегайте случаев, когда индексы [i]> индексы [i + 1]. Кроме того, название предполагает
гораздо большее сходство, чем существует на самом деле

В качестве замены я бы предложил np.piecewise_reduce, который в основном
делает:

def piecewise_reduce (func, arr, start_indices = None, end_indices = None, axis = -1, out = None):
если start_indices равно None, а end_indices равно None:
start_indices = np.array ([0], dtype = np.intp)
end_indices = np.array (arr.shape [ось], dtype = np.intp)
elif end_indices равно None:
end_indices = np.empty_like (начальные_индексы)
end_indices [: - 1] = start_indices [1:]
end_indices [-1] = arr.shape [ось]
elif start_indices равно None:
start_indices = np.empty_like (конечные_индексы)
start_indices [1:] = end_indices
end_indices [0] = 0
еще:
assert len ​​(start_indices) == len (end_indices)

if not out:
    sh = list(arr.shape)
    sh[axis] = len(end_indices)
    out = np.empty(shape=sh)

# below assumes axis=0 for brevity here
for i, (si, ei) in enumerate(zip(start_indices, end_indices)):
    func.reduce(arr[si:ei,...], out=alloc[i, ...], axis=axis)
return out

Который имеет приятные свойства:

  • np.ufunc.reduce совпадает с np.piecewise_reduce (func, arr, 0,
    len (прибл))
  • np.ufunc.accumulate - это то же самое, что и `np.piecewise_reduce (func, arr,
    np.zeros (len (arr)), np.arange (len (arr)))

Теперь, нужно ли это пройти через механизм __array_ufunc__? Большинство
то, что необходимо обработать, уже должно быть охвачено func.reduce -
единственная проблема - это пустая строка np.empty, которая является проблемой np.concatenate
акции.

-
Вы получаете это, потому что подписаны на эту ветку.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/834#issuecomment-293867746 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/ADMGdtjSCodONyu6gCpwofdBaJMCIKa-ks5rvgtrgaJpZM4ANcqc
.

-
(__ /)
(Оо)
(> <) Este es Conejo. Copia a Conejo en tu firma y ayúdale en sus самолеты
de dominación mundial.

Используйте "старт" и "стоп"

Готово

Должны ли мы сделать `` шаг '' вариантом

Похоже на довольно узкий вариант использования

Имеет ли смысл транслировать массивы индексов, или они должны быть 1D

Обновлено. > 1d - это явно плохо, но я думаю, что мы должны разрешить 0d и трансляцию для таких случаев, как накопление.

Это должна быть функция np или метод ufunc? (Я думаю, что предпочитаю это
как метод)

Каждый метод ufunc - это еще одна вещь, которую нужно обработать __array_ufunc__ .

Основная мотивация для reduceat - избежать зацикливания на reduce для максимальной скорости. Поэтому я не совсем уверен, что обертка цикла for над reduce была бы очень полезным дополнением к Numpy. Это будет противоречить основной цели reduceat .

Более того, логика существования reduceat и API в качестве быстрой векторизованной замены цикла над reduce чиста и полезна. Я бы не стал его осуждать, а скорее исправлю.

Что касается скорости reduceat , давайте рассмотрим простой пример, похожий на некоторые реальные случаи, которые у меня есть в моем собственном коде, где я использую reduceat :

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

Это разница во времени более чем в 100 раз и иллюстрирует важность сохранения эффективности reduceat .

Таким образом, я бы предпочел исправление reduceat введению новых функций.

Наличие start_indices и end_indices , хотя и полезных в некоторых случаях, часто является избыточным, и я бы рассматривал это как возможное дополнение, но не как исправление для текущего несогласованного reduceat поведение.

Я не думаю, что индексы запуска и остановки могут поступать из разных массивов
будет иметь большое значение для эффективности, если будет реализовано в C.

13 апреля 2017 года в 23:40 divenex [email protected] написал:

Основная мотивация reduceat - избежать зацикливания.
максимальная скорость. Поэтому я не совсем уверен, что оболочка цикла for
reduce было бы очень полезным дополнением к Numpy. Это пошло бы против
уменьшить основное назначение.

Кроме того, логика для сокращения существования и API, как быстрая векторизация
замена на loop over reduce, чистая и полезная. я не стал бы
отказаться от этого, а лучше исправить.

Что касается уменьшения скорости, давайте рассмотрим простой пример, но похожий на
несколько реальных случаев, которые у меня есть в моем собственном коде, где я использую reduceat:

п = 10000
arr = np.random.random (n)
inds = np.random.randint (0, n, n // 10)
inds.sort ()
% timeit out = np.add.reduceat (arr, inds) 10000 циклов, лучшее из 3: 42,1 мкс на цикл
% timeit out = piecewise_reduce (np.add, arr, inds) 100 циклов, лучшее из 3: 6,03 мс на цикл

Это разница во времени более чем в 100 раз и демонстрирует важность
сохранения снижения эффективности.

Подводя итог, я бы предпочел исправление reduceat внедрению новых
функции.

Наличие start_indices и end_indices, хотя и полезно в некоторых случаях,
часто излишне, и я бы рассматривал это как возможное дополнение, но не как исправление
для текущего снижения непоследовательного поведения.

-
Вы получили это, потому что оставили комментарий.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/834#issuecomment-293898215 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/AAEz6xPex0fo2y_MqVHbNP5YNkJ0CBJrks5rviW-gaJpZM4ANcqc
.

Это разница во времени более чем в 100 раз и иллюстрирует важность сохранения пониженной эффективности.

Спасибо за это. Думаю, я недооценил накладные расходы, связанные с первым этапом вызова reduce (это происходит только один раз для reduceat ).

Не аргумент против бесплатной функции, но, безусловно, аргумент против ее реализации на чистом питоне

но не как исправление текущего непоследовательного поведения reduceat.

Проблема в том, что сложно изменить поведение кода, который существует так долго.


Другое возможное расширение: когда indices[i] > indices[j] , вычислить обратное:

    for i, (si, ei) in enumerate(zip(start, stop)):
        if si >= ei:
            func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
        else:
            func.reduce(arr[ei:si,...], out=out[i, ...], axis=axis)
            func.inverse(func.identity, out[i, ...], out=out[i, ...])

Где np.add.inverse = np.subtract , np.multiply.inverse = np.true_divide . Это приводит к прекрасному свойству, которое

func.reduce(func.reduceat(x, inds_from_0)) == func.reduce(x))

Например

a = [1, 2, 3, 4]
inds = [0, 3, 1]
result = np.add.reduceat(a, inds) # [6, -5, 9] == [(1 + 2 + 3), -(3 + 2), (2 + 3 + 4)]

Проблема в том, что сложно изменить поведение кода, который существует так долго.

Отчасти поэтому в потоке электронной почты я предложил придать особое значение двумерному массиву индексов, в котором дополнительное измерение равно 2 или 3: тогда он (эффективно) интерпретируется как стек срезов. Но я понимаю, что это тоже несколько беспорядочно, и, конечно, можно использовать метод reduce_by_slice , slicereduce или reduceslice .

ps Я действительно думаю, что все, что работает на многих ufunc, должно быть методом, чтобы его можно было передать через __array_ufunc__ и переопределить.

На самом деле, другое предложение, которое, на мой взгляд, намного лучше: вместо того, чтобы спасать reduceat , почему бы не добавить аргумент slice (или start , stop , step ) на ufunc.reduce !? Как заметил @ eric-wieser, любая такая реализация означает, что мы можем просто отказаться от reduceat целом, поскольку это будет просто

add.reduce(array, slice=slice(indices[:-1], indices[1:])

(где теперь мы можем сделать так, чтобы поведение соответствовало ожидаемому для пустого среза)

Здесь можно было бы транслировать срез, если бы он был 0-d, и можно было бы даже рассмотреть возможность передачи кортежей срезов, если использовался кортеж осей.

РЕДАКТИРОВАТЬ: сделал указанное выше slice(indices[:-1], indices[1:]) чтобы разрешить расширение до кортежа срезов ( slice может содержать произвольные данные, поэтому это будет работать нормально).

Я бы все равно нашел исправление для reduceat , сделав его правильной 100% векторизованной версией reduce , наиболее логичного дизайнерского решения. В качестве альтернативы, чтобы избежать нарушения кода (но см. Ниже), можно создать эквивалентный метод с именем reducebins , который является просто исправленной версией reduceat . Фактически, я согласен с @ eric-wieser в том, что имя reduceat передает больше связи с функцией at чем есть.

Я понимаю, что код нельзя взламывать. Но я должен сказать, что мне трудно представить, что большая часть кода зависела от старого поведения, учитывая, что оно просто не имело логического смысла, и я бы просто назвал это давней ошибкой. Я ожидал, что этот код, использующий reduceat просто удостоверился, что indices не дублировался, чтобы избежать бессмысленного результата от reduceat , или исправил вывод, как я сделал, используя out[:-1] *= np.diff(indices) > 0 . Конечно, меня будет интересовать случай пользователя, в котором старое поведение / ошибка использовались по назначению.

Я не совсем уверен в решении @mhvk slice потому что оно вводит нестандартное использование конструкции slice . Более того, это было бы несовместимо с нынешней идеей дизайна reduce , которая заключается в _ "уменьшении размера a на единицу, применяя ufunc вдоль одной оси" _.

Я также не вижу убедительных примеров использования обоих индексов start и end . Фактически, я вижу красивую логику дизайна текущего метода reduceat концептуально похожего на np.histogram , где bins , который _ "определяет края бункера", _ заменяется на indices , которые также представляют края бункеров, но в индексном пространстве, а не в значении. И reduceat применяет функцию к элементам, содержащимся внутри каждой пары ребер бункера. Гистограмма - чрезвычайно популярная конструкция, но она не требует и в Numpy не включает возможность передачи двух векторов левого и правого краев. По той же причине я сомневаюсь, что существует острая необходимость в обоих ребрах в reduceat или его замене.

Основная мотивация для reduceat - избежать зацикливания на сокращении для максимальной скорости. Поэтому я не совсем уверен, что обертка цикла for с уменьшением будет очень полезным дополнением к Numpy. Это будет противоречить основной цели.

Я согласен с @divenex здесь. Тот факт, что reduceat требует сортировки индексов и перекрытия, является разумным ограничением для обеспечения того, чтобы цикл можно было эффективно вычислить с помощью кэша за один проход по данным. Если вам нужны перекрывающиеся интервалы, почти наверняка есть лучшие способы вычислить желаемую операцию (например, агрегирование скользящего окна).

Я также согласен с тем, что самым чистым решением является определение нового метода, такого как reducebins с фиксированным API (и не рекомендуется использовать reduceat ), и не пытаться втиснуть его в reduce который уже делает что-то другое.

Всем привет,

Я хочу пресечь обсуждение того, что это ошибка. Это задокументированное поведение из строки документации :

For i in ``range(len(indices))``, `reduceat` computes
``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th
generalized "row" parallel to `axis` in the final result (i.e., in a
2-D array, for example, if `axis = 0`, it becomes the i-th row, but if
`axis = 1`, it becomes the i-th column).  There are three exceptions to this:

* when ``i = len(indices) - 1`` (so for the last index),
  ``indices[i+1] = a.shape[axis]``.
* if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is
  simply ``a[indices[i]]``.
* if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised.

Поэтому я против любых попыток изменить поведение reduceat .

Быстрый поиск в github показывает множество вариантов использования этой функции. Все ли здесь уверены, что все используют только строго возрастающие индексы?

Что касается поведения новой функции, я бы сказал, что без отдельных массивов запуска / остановки функциональность сильно затруднена. Есть много ситуаций, когда нужно измерять значения в перекрывающихся окнах, которые не имеют регулярного массива (поэтому скользящие окна не будут работать). Например, интересующие регионы определяются каким-то независимым методом. И @divenex показал, что разница в производительности по сравнению с итерацией Python может быть огромной.

Есть много ситуаций, когда нужно измерять значения в перекрывающихся окнах, которые не имеют регулярного массива (поэтому скользящие окна не будут работать).

Да, но вы бы не хотели использовать наивный цикл, такой как реализованный reduceat . Вы хотели бы реализовать собственное вычисление скользящего окна с сохранением промежуточных результатов каким-либо образом, чтобы это можно было сделать за один линейный проход по данным. Но сейчас мы говорим об алгоритме, который намного сложнее, чем reduceat .

@shoyer Я могу представить себе случаи, когда перекрываются лишь некоторые из np.reduceat , означают, что было бы трудно или невозможно получить лучшее решение с чистым кодом Python - чаще всего это единственный код, который пользователи готовы писать.

@jni Конечно, может быть полезно reduceat (который мы определенно хотим отказаться от него, даже если мы никогда не удалим его).

разделение на группы с произвольными запусками и остановками может быть полезным. Но мне кажется, что это значительное расширение возможностей.

Мне это кажется очень тривиальным. Прямо сейчас у нас есть код, который, по сути, выполняет ind1 = indices[i], ind2 = indices[i + 1] . Чтобы изменить это, чтобы использовать два разных массива вместо одного, нужно очень мало усилий.

И однопроходное поведение при передаче смежных диапазонов должно быть почти таким же быстрым, как и сейчас - единственные накладные расходы - это еще один аргумент для nditer.

Мне это кажется очень тривиальным.

Точно. Более того, это функциональность, которую пользователи имеют с reduceat (при использовании любого другого индекса), но будут потеряны с новой функцией, которая не поддерживает перекрытие.

Кроме того, двухиндексная форма может имитировать старое (странное) поведение:

def reduceat(func, arr, inds):
    deprecation_warning()
    start = inds
    stops = zeros(inds.shape)
    stops[:-1] = start[1:]
    stops[-1] = len(arr)
    np.add(stops, 1, where=ends == starts, out=stops)  # reintroduce the "bug" that we would have to keep
    return reducebins(func, arr, starts, stops)

Это означает, что нам не нужно поддерживать две очень похожие реализации.

Я не категорически против индексов starts и stops для новых reducebins , хотя я до сих пор не вижу очевидного примера, где они оба необходимы. Это похоже на обобщение np.histogram путем добавления начальных и конечных bins ребер ...

В конечном счете, это нормально, если не затрагивается основное использование, и можно также вызвать reducebins(arr, indices) с одним массивом индексов и без потери скорости.

Конечно, есть много ситуаций, когда нужно работать с неперекрывающимися ячейками, но в этом случае я обычно ожидал бы, что ячейки не будут определяться только парами ребер. Доступными функциями для такого сценария являются ndimage.labeled_comprehension Scipy и связанные с ним функции, такие как ndimage.sum и т. Д.

Но это кажется совершенно отличным от объема reducebins .

Итак, что было бы естественным вариантом использования для starts и stops в reducebins ?

Итак, что было бы естественным вариантом использования для запусков и остановок в redubins?

Достижимо другими способами, но скользящее среднее длиной k будет reducebins(np,add, arr, arange(n-k), k + arange(n-k)) . Я подозреваю, что без учета затрат на размещение индексов производительность будет сопоставима с подходом as_strided .

Уникально, reducebins допускает скользящую среднюю различной продолжительности, что невозможно с as_strided

Другой вариант использования - устранение неоднозначности между включением конца или начала в форму с одним аргументом.

Например:

a = np.arange(10)
reducebins(np.add, start=[2, 4, 6]) == [2 + 3, 4 + 5, 6 + 7 + 8 + 9]  # what `reduceat` does
reducebins(np.add, stop=[2, 4, 6])  == [0 + 1, 2 + 3, 4 + 5]          # also useful

Другой вариант использования - устранение неоднозначности между включением конца или начала в форму с одним аргументом.

Я не совсем понимаю это. Можете ли вы включить сюда входной тензор? Также: какими будут значения по умолчанию для start / stop ?

В любом случае, я не категорически против отдельных аргументов, но это не так чисто от замены. Я бы хотел сказать: «Не используйте reduceat, а используйте reducebins», но это (немного) сложнее, когда интерфейс выглядит иначе.

На самом деле, я только что понял, что даже опция запуска / остановки не покрывает вариант использования пустых срезов, который был полезен мне в прошлом: когда мои свойства / метки соответствуют строкам в разреженной матрице CSR, и я использую значения indptr для уменьшения. С reduceat я могу игнорировать пустые строки. Любая замена потребует дополнительной бухгалтерии. Итак, какую бы замену вы ни придумали, оставьте reduceat .

In [2]: A = np.random.random((4000, 4000))
In [3]: B = sparse.csr_matrix((A > 0.8) * A)
In [9]: %timeit np.add.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 1)
1000 loops, best of 3: 1.81 ms per loop
In [12]: %timeit B.sum(axis=1).A
100 loops, best of 3: 1.95 ms per loop
In [16]: %timeit np.maximum.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 0)
1000 loops, best of 3: 1.8 ms per loop
In [20]: %timeit B.max(axis=1).A
100 loops, best of 3: 2.12 ms per loop

Между прочим, загадку с пустой последовательностью можно решить так же, как это делает Python : путем предоставления начального значения. Это может быть скаляр или массив той же формы, что и indices .

да, я согласен, что в первую очередь нужно сосредоточиться на решении пустых срезов
дело. В случае start = end у нас может быть способ установить вывод
элемент к идентичности, или не изменять выходной элемент с помощью
указанный массив. Проблема с током в том, что он перезаписывается
с неактуальными данными

Я полностью согласен с

Давайте просто определим out=ufunc.reducebins(a, inds) как out[i]=ufunc.reduce(a[inds[i]:inds[i+1]]) для всех i кроме последнего, и исключим reduceat .

Текущие варианты использования индексов starts и ends кажутся более естественными и, вероятно, более эффективными, реализованными с помощью альтернативных функций, таких как as_strided или свертки.

@shoyer :

Я не совсем понимаю это. Можете ли вы включить сюда входной тензор? Также: каковы будут значения по умолчанию для запуска / остановки?

Обновлено с вводом. См. Реализацию reduce_bins в комментарии, с которого это началось, для значений по умолчанию. Я тоже добавил туда строку документации. Эта реализация полнофункциональная, но медленная (из-за Python).

но это (немного) сложнее, когда интерфейс выглядит иначе.

Когда передается только один аргумент start , интерфейс идентичен (игнорируя случай идентичности, который мы решили исправить в первую очередь). Эти три строки означают одно и то же:

np.add.reduce_at(arr, inds)
reduce_bins(np.add, arr, inds)
reduce_bins(np.add, arr, start=inds)

(различие метода / функции - это не то, о чем я слишком беспокоюсь, и я не могу определить новый метод ufunc в качестве прототипа в python!)


@jni :

На самом деле, я только что понял, что даже опция запуска / остановки не покрывает вариант использования пустых фрагментов, который был полезен мне в прошлом.

Вы ошибаетесь, это так - точно так же, как уже делает ufunc.reduceat . Это также возможно, просто передав start[i] == end[i] .

Загадка пустой последовательности может быть решена ... путем предоставления начального значения.

Да, мы уже рассмотрели это, и ufunc.reduce уже делает это, заполнив ufunc.identity . Это несложно добавить к существующему ufunc.reduecat , особенно если объединить # 8952. Но, как вы сами сказали, текущее поведение _документировано_, поэтому нам, вероятно, не следует его менять.


@divenex

Давайте просто определим out = ufunc.reducebins (a, inds) как out [i] = ufunc.reduce (a [inds [i]: inds [i + 1]]) для всех i, кроме последнего

Итак, len(out) == len(inds) - 1 ? Это отличается от текущего поведения reduceat , поэтому аргумент @shoyer о переключении здесь сильнее.


Все: я просмотрел предыдущие комментарии и удалил цитируемые электронные письма, так как они затрудняли чтение этого обсуждения

@ Эрик-Визер хороший замечание. В приведенном выше предложении я имел в виду, что для последнего индекса поведение reducebins будет отличаться от текущего reduceat . Однако в этом случае я не уверен, каким должно быть значение, поскольку последнее значение формально не имеет смысла.

Игнорируя проблемы совместимости, выходные данные reducebins (в 1D) должны иметь размер inds.size-1 по той же причине, по которой np.diff(a) имеет размер a.size-1 и np.histogram(a, bins) имеет размер bins.size-1 . Однако это противоречило бы желанию иметь замену для reduceat .

Я не думаю, что есть убедительный аргумент в пользу того, что a.size-1 - правильный ответ - включение index 0 и / или index n кажется довольно разумным поведением. Все они кажутся удобными в некоторых обстоятельствах, но я думаю, что очень важно иметь запасную замену.

Есть еще один аргумент в пользу того, что здесь прячется stop / start - он позволяет вам построить поведение, подобное diff если вы этого хотите, с очень небольшими затратами, при этом сохраняя reduceat поведение:

a = np.arange(10)
inds = [2, 4, 6]
reduce_bins(a, start=inds[:-1], stop=inds[1:])  #  [2 + 3, 4 + 5]

# or less efficiently:
reduce_at(a, inds)[:-1}
reduce_bins(a, start=inds)[:-1]
reduce_bins(a, stop=inds)[1:]

@ eric-wieser Я бы согласился с обязательными аргументами start и stop , но мне не нравится делать один из них необязательным. Не очевидно, что предоставление только start означает out[i] = func.reduce(arr[start[i]:start[i+1]]) а не out[i] = func.reduce(arr[start[i]:]) , о чем я мог догадаться.

Мой предпочтительный API для reducebins похож на reduceat но без запутанных "исключений", указанных в строке документации . А именно:

Для i in range(len(indices)) , reduceat вычисляет ufunc.reduce(a[indices[i]:indices[i+1]]) , который становится i-й обобщенной «строкой», параллельной оси в конечном результате (т. Е. В 2-мерном массиве, например, если axis = 0 становится i-й строкой, но если axis = 1, она становится i-м столбцом).

Я мог бы пойти любым путем в отношении третьего «исключения», которое требует неотрицательных индексов ( 0 <= indices[i] <= a.shape[axis] ), что я рассматриваю скорее как проверку работоспособности, чем как исключение. Но, возможно, и это можно было бы сделать - я вижу, насколько отрицательные индексы могут быть кому-то полезны, и нетрудно провести математические вычисления, чтобы нормализовать такие индексы.

Отсутствие автоматического добавления индекса в конце не означает, что результат должен иметь длину len(a)-1 , как результат np.histogram .

@jni Не могли бы вы привести пример того, что вы действительно хотите вычислить из массивов, найденных в разреженных матрицах? Желательно с конкретным примером с неслучайными числами и самодостаточным (вне зависимости от scipy.sparse).

Не очевидно, что предоставление только start означает out [i] = func.reduce (arr [start [i]: start [i + 1]]), а не out [i] = func.reduce (arr [start [i] :]), о чем я и догадывался.

Я стремился к чтению, что «Каждая ячейка начинается с этих позиций», что подразумевает, что все ячейки являются смежными, если явно не указано иное. Возможно, мне стоит попробовать составить более полную строку документации. Я думаю, что вижу веский аргумент в пользу запрета передачи ни одного аргумента, поэтому я удалю его из своей функции предложения.

который требует неотрицательных индексов (0 <= index [i] <a.shape [axis])

Обратите внимание, что здесь также есть ошибка (# 835) - верхняя граница должна быть включающей, поскольку это срезы.

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

Исправлено, спасибо.

Не в самой функции reduceat , у вас нет;)

Оказывается, :\doc\neps\groupby_additions.rst содержит (меньшее IMO) предложение для функции reduceby .

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