_Оригинальный билет http://projects.scipy.org/numpy/ticket/236 от 07.08.2006 пользователем trac martin_wiechert, назначен unknown._
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])
_ @ 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, если кто считает) из вашего предложения:
Я думаю, что для этой новой функции важно хорошенько подумать:
А из отдела литья велосипедов мне больше нравятся:
Хайме
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
.
Самый полезный комментарий
Основная мотивация для
reduceat
- избежать зацикливания наreduce
для максимальной скорости. Поэтому я не совсем уверен, что обертка цикла for надreduce
была бы очень полезным дополнением к Numpy. Это будет противоречить основной целиreduceat
.Более того, логика существования
reduceat
и API в качестве быстрой векторизованной замены цикла надreduce
чиста и полезна. Я бы не стал его осуждать, а скорее исправлю.Что касается скорости
reduceat
, давайте рассмотрим простой пример, похожий на некоторые реальные случаи, которые у меня есть в моем собственном коде, где я используюreduceat
:Это разница во времени более чем в 100 раз и иллюстрирует важность сохранения эффективности
reduceat
.Таким образом, я бы предпочел исправление
reduceat
введению новых функций.Наличие
start_indices
иend_indices
, хотя и полезных в некоторых случаях, часто является избыточным, и я бы рассматривал это как возможное дополнение, но не как исправление для текущего несогласованногоreduceat
поведение.