Автоматическое создание массивов объектов недавно устарело в numpy. Я согласен с изменением, но кажется немного сложным написать определенные виды универсального кода, который определяет, можно ли преобразовать предоставленный пользователем аргумент в массив, не являющийся объектом.
Matplotlib содержит следующий фрагмент:
# <named ("string") colors are handled earlier>
# tuple color.
c = np.array(c)
if not np.can_cast(c.dtype, float, "same_kind") or c.ndim != 1:
# Test the dtype explicitly as `map(float, ...)`, `np.array(...,
# float)` and `np.array(...).astype(float)` all convert "0.5" to 0.5.
# Test dimensionality to reject single floats.
raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
но иногда функция вызывается с массивом цветов в различных форматах (например, ["red", (0.5, 0.5, 0.5), "blue"]
) - мы перехватываем ValueError и вместо этого конвертируем каждый элемент по одному.
Теперь вызов np.array (c) выдаст DeprecationWarning. Как мы можем это обойти? Даже что-то вроде np.min_scalar_type(c)
излучает предупреждение (которое, я полагаю, не должно?), Поэтому мне не очевидно, как проверить, "если бы мы преобразовали эту вещь в массив, каким был бы dtype?"
1.19.0.dev0 + bd1adc3 3.8.0 (по умолчанию, 6 ноября 2019 г., 21:49:08)
[GCC 7.3.0]
Один из вариантов был бы
`` питон
пытаться:
# опередить игру и продвинуть устаревание до ошибки, которая заменит его
с warnings.catch_warnings ():
warnings.filterwarnings ('поднять', DeprecationWarning, message = "...")
c_arr = np.asarray (с)
кроме (DeprecationWarning, ValueError):
# все, что вы сейчас делаете для ValueError
Я предполагаю, что это, и неудачный тест, упомянутый в gh-15045, - это случаи, когда выдача DeprecationWarning
в течение нескольких лет вместо прямого испускания ValueError
вызывает больше оттока кода, чем необходимо.
Обратите внимание, что warnings.catch_warnings
не является потокобезопасным. Это делает обходной путь немного подверженным последующим проблемам в будущем.
Я думаю, что отток кода стоит периода устаревания.
Matplotlib запускает свой набор тестов с предупреждениями о сбоях, чтобы своевременно уловить именно такие изменения, так что мне кажется, что система работает :).
Но AFAICT нет даже достаточно простого исправления (как указано выше, предлагаемое исправление не является потокобезопасным) для него: /
Думаю, я понимаю, о чем говорит @anntzer . Мы находимся в беспорядке, когда нижележащие библиотеки хотят быстро выйти из строя, чтобы они могли попробовать что-то еще, в то время как пользователям нужно показывать более мягкое сообщение.
Проблема в том, что на сегодняшний день у автора библиотеки нет возможности спросить: «Будет ли это выдавать предупреждение?» Без фактического ... выдачи предупреждения, а его подавление не является потокобезопасным.
Что касается предупреждений о безопасности потоков: https://bugs.python.org/issue37604
AFAIK, устаревание находится в ветке выпуска. Мы хотим вернуть его обратно? В противном случае для исправлений потребуются резервные копии. Я все еще не понимаю, почему предупреждения не появлялись в колесах ветки выпуска и не отображались в ночных сборках до последних двух сборок. Я ничего не менял после ветки, и с тех пор в основной ветке ничего не выглядит подозрительно, кроме, возможно, # 15040.
ИМХО (и в соответствии с точкой @mattip выше) это такие изменения, которые было бы намного проще обрабатывать ниже по течению, если бы переход на повышение произошел без периода устаревания. Не уверен, что это вариант: /
Или, возможно, multibuild обрабатывает ветки иначе, чем master.
FWIW Я всегда был как минимум -1 при этом изменении, особенно как заядлый пользователь рваных структур данных, но в любом случае теперь мне нужно выяснить, что делать с сотнями неудачных тестов для подготовки SciPy 1.4.0rc2
в https://github.com/scipy/scipy/pull/11161
теперь мне нужно выяснить, что делать с сотнями неудачных тестов
Простым вариантом будет:
Вся суть в том, что мы использовали DeprecationWarning
вместо ValueError
заключалась в том, чтобы дать нижележащим проектам и пользователям льготный период, чтобы сделать именно это.
AFAIK, устаревание находится в ветке выпуска. Мы хотим вернуть его обратно?
Думаю, да, идет дождь. Теперь у нас есть список того, что ломается в Pandas, Matplotlib, SciPy, внутри numpy.testing
и NumPy ufuncs, ==
и т. Д. Я думаю, нам следует отменить изменения сейчас и оценить / исправить все эти вещи, а затем снова ввести устаревание.
Можем ли мы пойти на компромисс в отношении ожидающего предупреждения об устаревании?
Таким образом, последующие проекты могут добавить его в свои списки игнорирования, и когда мы вернемся к DeprecationWarning, они снова примут решение.
Похоже, что мы отклонились от исходной проблемы, которая, как представляется, «с учетом последовательности значений, как matplotlib может определить, являются ли они одним цветом или списком цветов». Я думаю, должно быть решение, которое не требует преобразования значений в ndarray и проверки dtype этого массива. Лучшим решением может быть какая-то рекурсивная функция is_a_color()
.
Я отменил изменение для 1.18.x в # 15053.
Считается, что взлом scipy и pandas CI достаточно раздражает, чтобы временно отменить его и в мастере. Я бы хотел, чтобы он вернулся в основном по расписанию (скажем, в течение месяца). Тем не менее, нам может потребоваться найти решение. Также меня немного беспокоят исправления, которые делают панды, поскольку они используют catch_warnings
.
Если действительно нет способа, и нам нужно поточно-безопасное подавление предупреждений. np.seterr
может содержать слот для него: /.
Похоже, что мы отклонились от исходной проблемы, которая, как представляется, «с учетом последовательности значений, как matplotlib может определить, являются ли они одним цветом или списком цветов».
Я думаю, что проблема, которую поднимает
ndarray(flexible_input)
use_the_array
что делать, если к такому коду нельзя добавить dtype=object
?
Также меня немного беспокоят исправления, которые делают панды, поскольку они используют
catch_warnings
.
@seberg не лучше ли для этого suppress_warnings
?
@rgommers нет, suppress_warnings
решил проблему постоянного подавления предупреждений, хотя этого быть не должно. Это было исправлено в более новых версиях python, поэтому он нам больше не нужен (он имеет лучшие свойства, так как поддерживает вложение, но не поддерживает потокобезопасность. Я не уверен, что это возможно за пределами python, и даже если бы было то наверное не желательно)
Не совсем уверен, что проблемные случаи не совпадают с первоначальным намерением (https://numpy.org/neps/nep-0034.html), чего мы просто не ожидаем.
Как бы то ни было, выходом было бы явно включить старое поведение по принципу «принимая во внимание вашу озабоченность, но мы явно хотим контекстно-зависимый объект dtype и сами будем обрабатывать проблемный ввод». Что-то вроде одного из
~~~
np.array (данные, dtype = 'allow_object')
np.array (данные, allow_object_dtype = True)
с np.array_create_allow_object_dtype ():
np.array (данные)
~~~
все не очень красиво и название обязательно будет улучшено. Но это дает чистый выход для библиотек, которые полагались на поведение и хотят его сохранить (по крайней мере, на данный момент).
На самом деле это не случай matplotlib:
with np.forbid_ragged_arrays_immediately():
np.array(data)
действительно ли вы хотите поймать ошибку, а не получить объект dtype?
Отменить устаревание, ожидающее в настоящее время для master, нельзя. Я не думаю, что его следует полностью отменить, как это было в 1.18, потому что это также удалило исправления, которые, я думаю, мы хотим сохранить. @mattip Было бы
FWIW Я думаю, что большинство мест в mpl, которые поражают это, можно исправить (с более или менее реструктуризацией - в одном случае код оказывается намного быстрее после ...).
Я думаю, что предлагаемый @timhoffm API был бы лучше, чем with np.forbid_ragged_arrays_immediately:
потому что последнее можно легко записать в терминах первого (поднять, если np.array(..., allow_object=True).dtype == object
), тогда как противоположное ( try: with np.forbid: ... except ValueError: ...
) будет менее эффективным, если мы все же хотим создать массив объектов. Но CM (просто «локально продвигающийся после периода устаревания») лучше, чем ничего.
(Опять же, я думаю, что изменение хорошее, это просто вопрос того, как оно выполняется.)
Да, нам просто нужно выяснить, как должно выглядеть API. Как отмечают многие, в настоящее время есть две основные проблемы:
object
и "allow ragged"
. Если объекты имеют разумный тип (скажем, Decimal
), вы действительно хотите получить предупреждение / ошибку, но также может потребоваться передать dtype=object
Наконец, нам нужно придумать, как втиснуть это в наш код :). ndmin
может быть еще одной целью втиснуть флаги, по крайней мере, контролирующие рваное поведение.
Отменить устаревание, ожидающее в настоящее время для master, нельзя. Я не думаю, что его следует полностью отменить, как это было в 1.18, потому что это также удалило исправления, которые, я думаю, мы хотим сохранить. @mattip Было бы
Я не вижу проблемы с полным откатом, а затем с повторным введением тех частей, которые сейчас имеют смысл. Опять же, возврат чего-либо - это не оценочное суждение о том, что хорошо или плохо, это просто прагматичный способ раскрыть кучу вещей, которые мы только что сломали, нажав кнопку слияния. Очевидно, что есть влияние и нерешенные проблемы, которые не были предусмотрены в нэпе, поэтому сначала нужно вернуться назад.
Аргумент в пользу того, чтобы еще не возвращаться - пока изменение находится в главном, мы можем использовать последующие CI-прогоны, чтобы попытаться выяснить, как будут выглядеть их обходные пути.
Нисходящий CI красный, это _ очень_ бесполезно. Теперь у нас есть их список неудач, и нам не нужно оставлять красный цвет их КИ, чтобы нам было немного легче здесь.
И, по крайней мере, CI Matplotlib работает против pip install --pre
not master ветки
И, по крайней мере, CI Matplotlib работает против
pip install --pre
not master ветки
Похоже, это тянет из-за ночных колес. Изменение уже было отменено для 1.18.0rc1, поэтому вы не должны его видеть, если будете устанавливать с помощью --pre
из PyPI.
Некоторые из приведенных выше комментариев сводятся к переосмыслению предлагаемых изменений в NEP 34. Я не уверен, что эта ветка является подходящим местом для продолжения этого обсуждения, но продолжаю. (Ничего страшного, если это следует обсудить в другом месте - копировать и вставлять комментарии очень просто.: Smile: Кроме того, некоторые из вас видели вариацию этих комментариев при обсуждении Slack.)
Подумав об этом недавно, я @timhoffm (и эта идея, вероятно, предлагалась в другое время в последние несколько месяцев): определить конкретную строку или одноэлементный объект, который, если задан как аргумент dtype
для array
позволяет функции обрабатывать ввод неровной формы, создавая массив одномерных объектов. Фактически, это обеспечивает поведение dtype=None
до NEP-34, при котором входные данные рваной формы автоматически преобразуются в массив объектов. Если задано любое другое значение для dtype
(включая None
или object
), выдается предупреждение об устаревании, если ввод имеет рваную форму. В будущей версии NumPy это предупреждение будет преобразовано в ошибку.
Я думаю, теперь ясно, что использование dtype=object
для обработки ввода неровной формы не является хорошим решением проблемы. В идеале мы бы отделили понятия «массив объектов» от «рваного массива». Но мы не можем полностью разделить их, потому что, когда мы хотим обработать рваный массив, единственный выбор, который у нас есть, - это создать массив объектов. С другой стороны, иногда нам нужен массив объектов, но мы не хотим автоматического преобразования входных данных неровной формы в массив объектов последовательностей.
Например (см. Пункт 1 в последнем комментарии @seberg ), предположим, что f1
, f2
, f3
и f4
равны Fraction
objects, и я работаю с массивами объектов Fraction
s. Меня не интересует создание рваного массива. Если я случайно напишу a = np.array([f1, f2, [f3, f4]], dtype=object)
, я _ хочу_, чтобы это сгенерировало ошибку по всем причинам, по которым у нас есть NEP 34. Однако с NEP 34 это создаст 1-мерный массив длиной 3.
Альтернативы, которые добавляют новый аргумент ключевого слова, такие как второе предложение @timhoffm , кажутся более сложными, чем необходимо. Проблема, которую мы пытаемся решить, - это «ножное ружье», когда неровный ввод автоматически преобразуется в массив одномерных объектов. Проблема возникает только тогда, когда dtype=None
передается в array
. Требовать от пользователей заменить dtype=None
на dtype=<special-value-that-enables-ragged-handling>
для сохранения прежнего проблемного поведения - это простое изменение API, которое легко объяснить. Неужели нам действительно нужно что-то большее, чем это?
Я думаю, теперь ясно, что использование
dtype=object
для обработки ввода неровной формы не является хорошим решением проблемы. В идеале мы бы отделили понятия «массив объектов» от «рваного массива».
Возможно, звучит разумно. Также хорошо отметить, что в NumPy нет настоящей концепции «рваного массива» . Это то, что мы в основном не поддерживаем (ищите "рваный" в документации, в системе отслеживания проблем или в списке рассылки, чтобы подтвердить, хотите ли вы), это то, что поддерживают DyND и XND, и мы только начали говорить о том, чтобы иметь краткое фраза для обсуждения «мы хотим удалить поведение np.array([1, [2, 3]])
которое сбивает пользователей с толку». Следовательно, запекание «рваных массивов» как нового API должно выполняться с особой осторожностью, это абсолютно не то, что мы хотим продвигать. Так что было бы хорошо прояснить это при именовании всего dtype=some_workaround
мы можем добавить.
Похоже, что общее мнение объединяется вокруг решения о продлении срока устаревания (возможно, на неопределенный срок) путем разрешения np.array(vals, dtype=special)
которое будет вести себя как до NEP 34. Я предпочитаю синглтон, а не строку, поскольку это означает, что использование библиотеки может делать special = getattr(np.special, None)
и их код будут работать во всех версиях.
Теперь нам нужно определиться с именем и местом его отображения. Возможно, never_fail
или guess_dimensions
? Что касается того, где его выставлять, я бы предпочел не вешать его на np
а на какой-то другой внутренний модуль, возможно, с _
чтобы указать, что это действительно частный интерфейс.
Я думаю, что путь вперед состоит в том, чтобы внести поправки в NEP 34, а затем выставить обсуждение в списке рассылки.
Обратите внимание, что также было несколько сообщений о проблемах с использованием операторов (как минимум ==
и operator.mod
). Вы предлагаете игнорировать это или как-то сохранить это состояние в массиве?
Почти во всех случаях, вероятно, известно, что один из операндов является массивом numpy. Таким образом, вероятно, должно быть возможно получить четко определенное поведение путем ручного преобразования в массив numpy.
Может ли кто-нибудь указать на пример operator.mod
?
Что касается оператора ==
, тот, который я видел, делал что-то вроде np.array(vals, dtype=object) == vals
where vals=[1, [2, 3]]
(перефразируя код), поэтому решение состоит в том, чтобы заранее создать массив справа боковая сторона.
Многие из scipy сбоев, похоже, имеют форму np.array([0.25, np.array([0.3])])
, где смешивание скаляров и ndarray с shape==(1,)
нарушит обнаружение измерений и создаст массив объектов. xref gh-15075
Может ли кто-нибудь указать на пример
operator.mod
?
Видел это в PR Pandas от @jbrockmendel , но я думаю, что с тех пор он изменился (больше не вижу явного operator.mod
в комментариях).
Что касается оператора
==
, тот, который я видел, делал что-то вродеnp.array(vals, dtype=object) == vals
wherevals=[1, [2, 3]]
(перефразируя код), поэтому решение состоит в том, чтобы заранее создать массив справа боковая сторона.
В этот момент он становится np.array(vals, dtype=object) == np.array(vals, dtype=object)
, так что лучше просто удалите тест :)
@mattip написал:
Я предпочитаю синглтон, а не строку, поскольку это означает, что использование библиотек может выполнять special = getattr (np.special, None), и их код будет работать в разных версиях.
Для меня это звучит нормально.
Теперь нам нужно определиться с именем и местом его отображения. Возможно,
never_fail
илиguess_dimensions
? Что касается того, где его выставлять, я бы предпочел не вешать его на np, а на какой-то другой внутренний модуль, возможно, с _, чтобы указать, что это действительно частный интерфейс.
Мое текущее рабочее имя для этого - legacy_auto_dtype
, но, вероятно, есть много других имен, к которым у меня не было бы претензий.
Я не уверен, что это имя должно быть личным. Согласно любому практическому определению _private_ и _public_, это будет _public_ объект. Он предоставляет пользователям возможность сохранить прежнее поведение, например, array(data)
, переписав это как array(data, dtype=legacy_auto_dtype)
. Я предполагаю, что обновленный NEP объяснит, что именно так следует модифицировать код, чтобы сохранить прежнее поведение (для тех, кто должен это сделать). В этом случае объект определенно не является частным. Фактически, похоже, что это общедоступный объект, который останется в NumPy на неопределенный срок. Но, возможно, мое понимание того, как будет разворачиваться модифицированный NEP 34, неверно.
Согласен с описанием публичного / приватного, которое дает @WarrenWeckesser ; либо он общедоступен, либо не должен использоваться кем-либо за пределами NumPy.
Re name: выберите имя, которое описывает функциональность. Такие вещи, как «наследие», почти никогда не бывают хорошей идеей.
выберите имя, которое описывает функциональность.
auto_object
, auto_dtype
, auto
?
Подумать немного вслух ...
Что делает этот объект?
В настоящее время, когда NumPy предоставляется объект Python, содержащий подпоследовательности, длина которых не согласуется с обычным массивом nd, NumPy создаст массив с типом данных object
, с объектами на первом уровне, где возникает несогласованность формы осталось как объекты Python. Например, array([[1, 2], [1, 2, 3]])
имеет форму (2,)
, np.array([[1, 2], [3, [99]]])
имеет форму (2, 2)
и т. Д. В NEP 34 мы отказываемся от такого поведения, поэтому пытаемся создать массив с «рваным» вводом в конечном итоге приведет к ошибке, если он явно не включен. Особая ценность, о которой мы говорим, позволяет старому поведению.
Какое хорошее имя для этого ? ragged_as_object
? inconsistent_shapes_as_object
?
В этот момент он становится
np.array(vals, dtype=object) == np.array(vals, dtype=object)
, так что лучше просто удалите тест :)
Ну, я перефразировал. Фактический тест больше похож на my_func(vals) == vals
должен стать my_func(vals) == np.array(vals, dtype=object)
Я предложу расширение NEP 34, чтобы разрешить специальное значение для dtype.
Обратите внимание, что похоже, что scipy не нужен этот часовой для прохождения тестов с scipy / scipy # 11310 и scipy / scipy # 11308
gh-15119 был объединен, что повторно реализовало нэп. Если он не будет отменен, мы можем закрыть эту проблему.
Я собираюсь закрыть это, так как мы не занимались этим до выпуска 1.19. И я, по крайней мере, надеюсь, что причина этого была в том, что обсуждение утихло, поскольку все крупные проекты смогли найти разумные решения созданных им проблем.
Пожалуйста, поправьте меня, если я ошибаюсь, особенно если это по-прежнему подвержено проблемам с pandas, matplotlib и т. Д. Но я предполагаю, что мы слышали об этом во время цикла выпуска кандидатов на выпуск 1.19.x.
Самый полезный комментарий
Может ли кто-нибудь указать на пример
operator.mod
?Что касается оператора
==
, тот, который я видел, делал что-то вродеnp.array(vals, dtype=object) == vals
wherevals=[1, [2, 3]]
(перефразируя код), поэтому решение состоит в том, чтобы заранее создать массив справа боковая сторона.Многие из scipy сбоев, похоже, имеют форму
np.array([0.25, np.array([0.3])])
, где смешивание скаляров и ndarray сshape==(1,)
нарушит обнаружение измерений и создаст массив объектов. xref gh-15075