Numpy: ОШИБКА: вывод numpy.percentile не отсортирован

Созданный на 12 окт. 2019  ·  16Комментарии  ·  Источник: numpy/numpy

Вывод numpy.percentile не всегда сортируется

Воспроизведение примера кода:

import numpy as np
q = np.arange(0, 1, 0.01) * 100
percentile = np.percentile(np.array([0, 1, 1, 2, 2, 3, 3 , 4, 5, 5, 1, 1, 9, 9 ,9, 8, 8, 7]) * 0.1, q)
equals_sorted = np.sort(percentile) == percentile
print(equals_sorted)
assert equals_sorted.all()

Сообщение об ошибке:

[Истинно Истинно Истинно Истинно Истинно Истинно Истинно Истинно Истинно Истинно
Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда
Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда
Истинно Истинно Истинно Истинно Истинно Истинно Истинно Истинно Истинно Истинно
Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда
Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда
Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда Правда
Истинно Истинно Истинно Истинно Истинно Ложно Ложно Истинно Истинно Истинно Истинно Ложно
Истина Истина Истина Ложь]
AssertionError Traceback (последний вызов последним)
в
1 q = np.percentile (np.array ([0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 1, 1, 9, 9, 9, 8, 8, 7]) * 0,1, np.arange (0, 1, 0,01) * 100)
2 равно_сортировано = np.sort (q) == q
----> 3 assert equals_sorted.all ()

AssertionError:

Информация о версии Numpy / Python:

1.17.2 3.6.8 (v3.6.8: 3c6b436a57, 24 декабря 2018 г., 02:04:31)
[GCC 4.2.1, совместимый с Apple LLVM 6.0 (clang-600.0.57)]

00 - Bug numpy.lib good first issue

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

Эй, похоже, было обновление одного из ответов stackexchange, предоставленных @ eric-wieser, с хорошей альтернативной интерполяцией.
Цепочка включает доказательство монотонности, и предлагаемое исправление, похоже, решает все упомянутые проблемы.
Если бы это имело смысл для проблемы, я был бы готов реализовать это как первую фиксацию, или кто-то другой мог бы попробовать.
20191209_020250

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

Почему вы ожидали, что это будет отсортировано? Процентиль поэлементный - выходы в порядке входов.

Привет !
В самом деле, процентиль является точным - с учетом q , что в нашем случае
np.arange(0, 1, 0.01) * 100 .
Я ожидаю, что вывод будет отсортирован, потому что отсортировано q .

В одном ULP есть некоторые числовые ошибки, которые различаются для разных входов с одинаковым выходным значением. Я сомневаюсь, что с этим что-то можно сделать.

Немного сокращенный случай отказа:

In [40]: np.percentile(np.array([0, 1, 1, 2, 2, 3, 3 , 4, 5, 5, 1, 1, 9, 9 ,9, 8, 8, 7]) * 0.1, [89, 90, 95, 96, 98, 99])
Out[40]: array([0.9, 0.9, 0.9, 0.9, 0.9, 0.9])

In [41]: np.diff(_)
Out[41]:
array([-1.11022302e-16,  2.22044605e-16, -1.11022302e-16,  1.11022302e-16,
       -1.11022302e-16])

здесь показывает несортированность через diff.

Я думаю, что мы можем что-то с этим поделать. Я думаю, это сводится к стабильности этих строк, которые выполняют операцию lerp (по сути, add(v_below*weights_below, v_above*weights_above) ):

https://github.com/numpy/numpy/blob/b9fa88eec62e34e906689408096beb2450830d9a/numpy/lib/function_base.py#L3907 -L3908

https://github.com/numpy/numpy/blob/b9fa88eec62e34e906689408096beb2450830d9a/numpy/lib/function_base.py#L3928 -L3929

https://github.com/numpy/numpy/blob/b9fa88eec62e34e906689408096beb2450830d9a/numpy/lib/function_base.py#L3939 -L3942

При линейной интерполяции значений с плавающей запятой приходится идти на ряд компромиссов, но я подозреваю, что здесь есть «правильный» выбор, а мы его просто не сделали.

Еще немного предыстории: https://math.stackexchange.com/questions/907327/accurate-floating-point-linear-interpolation

Да, согласен, +1 за реорганизацию операций так, чтобы она была строго монотонной (численно). Было бы хорошо, если бы он тоже был не хуже или хотя бы почти такой же по точности. Я уверен, что нам действительно не нужно беспокоиться о некоторых дополнительных операциях / скорости здесь.

РЕДАКТИРОВАТЬ: отмечен как хороший первый выпуск. Но после этого, вероятно, это будет довольно простая реорганизация в коде Python.

Мне было бы интересно заняться этим вопросом. Я смотрел на некоторые из неудачных случаев и заметил, что все они связаны с линейной интерполяцией между одним и тем же числом. то есть в примере Эрика все перечисленные им процентили расположены между двумя девятками. Поэтому я думаю, что линейная интерполяция между ними должна быть точной 9? устранение проблемы линейной интерполяции между двумя одинаковыми числами, похоже, решило бы проблемы, представленные в этой ошибке, и не привело бы к заметному снижению производительности. Однако, если мы хотим гарантировать, что линейная интерполяция всегда будет монотонной, мы можем это сделать, но для этого потребуется кусочная функция, которая, как мне кажется, снизит производительность.

@ ngonzo95 должен быть способ записать арифметику интерполяции по-другому, чтобы достичь этого, т.е. изменить / перестроить формулу, которая используется для вычисления (так, чтобы она была математически идентична, но численно гарантировала монотонность). Никакого кусочного расчета не требуется.

Никакого кусочного расчета не требуется.

Это зависит от ваших требований к lerp . Некоторые из них, которые могут нас волновать, а могут и не волновать:

  • монотонный ( (lerp(a, b, t1) - lerp(a, b, t0)) * (b - a) * (t1 - t0) >= 0 )
  • ограниченный ( a <= lerp(a, b, t) <= b )
  • симметричный ( lerp(a, b, t) == lerp(b, a, 1-t) )

( 0 <= t <= 1 )

О, ладно, я не ожидал, что кусочки будут необходимы, но я думаю, что не знаю достаточно хорошо этого.

изучив его подробнее, я обнаружил, что функция a + (ba) * t имеет свойство быть как монотонным (определение, указанное выше), так и согласованным (lerp (a, a, t) = a). Я считаю, что этого должно быть достаточно для требований функций. Кажется, одним из основных недостатков этой функции является то, что lerp (a, b, 1)! = B. Однако я думаю, что способ вычисления весов гарантирует, что 0 <= t <1.

Кажется, одним из основных недостатков этой функции является то, что lerp (a, b, 1)! = B. Однако я думаю, что способ вычисления весов гарантирует, что 0 <= t <1.

Обратите внимание, что, к сожалению, при такой формулировке возможно использование lerp(a, b. 1-eps) > b) .

Новое в открытом исходном коде.
Хотел решить эту проблему как свою первую проблему. Как я могу внести свой вклад? Есть ли предпосылки?

Я просматривал некоторые из неудачных случаев и заметил, что все они связаны с линейной интерполяцией между одним и тем же числом

В scikit-learn мы недавно наткнулись на эту проблему: https://github.com/scikit-learn/scikit-learn/issues/15733

Поскольку мы ожидаем, что q будет строго возрастать, мы можем применить np.maximum.accumulate переупорядочив массив. Однако, если бы мы могли решить проблему напрямую в NumPy, это было бы здорово. Есть ли что-нибудь, что можно исправить?

@glemaitre : все соответствующие строки в numpy связаны в моем комментарии выше, https://github.com/numpy/numpy/issues/14685#issuecomment -541467915

Эй, похоже, было обновление одного из ответов stackexchange, предоставленных @ eric-wieser, с хорошей альтернативной интерполяцией.
Цепочка включает доказательство монотонности, и предлагаемое исправление, похоже, решает все упомянутые проблемы.
Если бы это имело смысл для проблемы, я был бы готов реализовать это как первую фиксацию, или кто-то другой мог бы попробовать.
20191209_020250

Обратите внимание, что есть еще одна проблема с lerp в quantile() : значения inf не обрабатываются правильно, см. # 12282.

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