В Numpy 2 есть отдельный float64(nan) – как от него избавиться?

Похоже, что в Numpy 2 появился свой отдельный float64(nan), отличный от np.nan. Теперь регрессионные тесты терпят неудачу, потому что пока

>>> np.nan is np.nan
True

у нас теперь есть

>>> np.nan is np.float64("nan")
False

(также json не может сериализовать эти новые NaN).

Почему они внесли это изменение?

Есть ли способ автоматически преобразовать числа с плавающей запятой в обычные числа с плавающей запятой?

Numpy выдает ошибку на моей машине, потому что np.float устарело: «np.float был устаревшим псевдонимом для встроенного float. Чтобы избежать этой ошибки в существующем коде, используйте float отдельно. Это не изменит никакого поведения и безопасно. Если вы специально если вам нужен скалярный тип numpy, используйте np.float64 здесь. Псевдонимы изначально устарели в NumPy 1.20. Более подробную информацию и рекомендации см. в исходном примечании к выпуску: numpy.org/devdocs/release/1.20.0-notes.html#deprecations;" . Использование np.float здесь особенно нецелесообразно, поскольку 32-битные NaN и 64-битные различаются.

Jérôme Richard 26.07.2024 15:59

@JérômeRichard- спасибо, я исправил опечатку

sds 26.07.2024 16:00

Какие изменения? Я тоже получаю False с NumPy 1.

no comment 26.07.2024 16:02
np.nan имеет тип float, что немного удивительно (не тип Numpy). Таким образом, это не будет то же самое, что np.float32("nan") или np.float64("nan"). Однако, как ни странно, np.nan is float("nan") тоже не верно... Может быть, NaN Python сигнализируют NaN, а Numpy молчат NaN? Обратите внимание, что сравнение NaN обычно является очень плохой идеей. Действительно, NaN никогда не равен самому себе, и существует много разных NaN (даже тихих NaN). Лучше использовать np.isnan. Кстати, я тоже получаю False с Numpy 1.24.3 (в Windows).
Jérôme Richard 26.07.2024 16:02

@JérômeRichard: У float("nan") нет причин возвращать конкретный объект numpy.nan. Вы бы увидели то же самое, если бы попробовали 2.0 is float("2.0")

user2357112 26.07.2024 16:09

@user2357112 user2357112 Да, действительно, можно создать новый объект. Спасибо. Оно было бы равно, если бы значение было каким-то образом кэшировано (AFAIK другие языки, такие как Java, делают это для небольших целых чисел и, конечно же, для NaN, что влияет на некоторые реализации Python, но, очевидно, не на CPython). Возможно, выгода довольно мала.

Jérôme Richard 26.07.2024 16:18
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Существует несколько NaN (см. ниже и NaN в качестве ссылки), и Python может повторно использовать объекты или нет. Использование X is Y в рамках модульного теста является здесь основной проблемой.

Например, np.inf - np.inf дает np.nan:

np.inf-np.inf
# nan

И все еще:

np.nan is (np.inf-np.inf)
# False

Если вы хотите узнать, является ли объект NaN, всегда используйте np.isnan (или эквивалент math или pandas), никогда не сравнивайте объекты или is:

np.isnan((np.inf-np.inf))
# True

подробнее о NaN и объектах Python

NaN не кодируются одним способом. Могут быть тихие NaN (неоднозначная операция) или сигнальные NaN (ошибочная операция). Кроме того, Python может повторно использовать объекты… или нет.

При запуске np.nan is np.nan Python использует один и тот же объект для обоих. Если вы запускаете np.log(-1) is np.log(-1) на моей машине, это создает два разных объекта, и результат False:

np.log(-1) is np.log(-1)
# False

Чтобы дать вам более сложный пример, на моей машине np.nan всегда выдается один и тот же объект. Тихие/сигнальные NaN всегда создают разные объекты. Последовательное создание одного типа NaN иногда приводит к повторному использованию объекта, а иногда нет:

(id(np.nan),        # 135051355677520
 id(np.inf-np.inf), # 135046756258928
 id(float('nan')),  # 135046756258928
 id(np.log(-1)),    # 135046756256624  # triggers warning
 id(np.sqrt(-1)),   # 135046756256624  # triggers warning # same as previous
 id(float('nan')),  # 135046756258928                     # same as before
 id(np.sqrt(-1)),   # 135046756261584  # triggers warning # different
 id(np.nan),        # 135051355677520                     # same as before
)

При повторном запуске все идентификаторы меняются, но по той же схеме. np.nan остается стабильным:

(id(np.nan),        # 135051355677520
 id(np.inf-np.inf), # 135046756261872
 id(float('nan')),  # 135046756261872
 id(np.log(-1)),    # 135046756262736  # triggers warning
 id(np.sqrt(-1)),   # 135046756262736  # triggers warning # same as previous
 id(float('nan')),  # 135046756261872                     # same as before
 id(np.sqrt(-1)),   # 135046756262640  # triggers warning # different
 id(np.nan),        # 135051355677520                     # same as before
)

Короче говоря, не делайте этого. Используйте np.isnan

Различия в выходных данных id, которые вы видите, не имеют ничего общего с тихими/сигнальными NaN или другими различиями в кодировании NaN. np.nan поддерживается ссылкой в ​​numpy.__dict__ (и другими ссылками), поэтому его идентификатор нельзя повторно использовать для других объектов. np.inf-np.inf и float('nan') оба создают новые числа с плавающей запятой, и первый из них умирает до того, как будет создан второй, поэтому они оказываются в одной и той же ячейке памяти.

user2357112 26.07.2024 16:23
np.log(-1) и np.sqrt(-1) оба создают новые экземпляры numpy.float64, а не float. Распределитель размещает их в том же месте, что и друг друга, но в другом месте, чем обычное место с плавающей запятой, созданное np.inf-np.inf и float('nan').
user2357112 26.07.2024 16:24

@user2357112 user2357112 я хотел сказать, что и то, и другое: NaN могут быть закодированы по-разному, а также могут быть повторно использованы как один и тот же объект… или нет (как в классическом Python).

mozway 26.07.2024 16:27

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

user2357112 26.07.2024 16:28

Понятно, я уточню

mozway 26.07.2024 16:29

@user2357112 user2357112 Надеюсь, теперь стало лучше. Как вы можете видеть, тихие или сигнальные NaN можно использовать повторно, если они создаются последовательно, но тихий NaN никогда не будет тем же объектом, что и сигнальный NaN.

mozway 26.07.2024 16:35

Вы не показываете повторное использование объектов, вы просто показываете повторное использование памяти.

no comment 26.07.2024 16:47

... но здесь нет сигнальных NaN. Предупреждения не поступают из-за сигнализации NaN — NumPy обнаруживает исключения IEEE 754 с плавающей запятой для выдачи этих предупреждений. Аналогичное предупреждение можно увидеть с помощью np.exp(10000), хотя такого понятия, как «сигнал бесконечности», не существует.

user2357112 26.07.2024 16:50

@user2357112 user2357112 Плохо, я проверил двоичное представление, и оба действительно являются тихими NaN (11111111110000000000000000000000). Я знаю, что предупреждение инициируется самим Python, но я действительно думал, что были возвращены разные NaN. Попробую найти где я это прочитал. Следует отметить, что тип NaN не тот же самый: np.inf-np.inf — это float, np.sqrt(-1) — это np.float64. Важным сообщением остается то, что не следует использовать == или is для сравнения NaN.

mozway 26.07.2024 19:24

Другие вопросы по теме