Почему Numpy преобразует тип «объект»-«int» в тип «объект»-«float»?

Это может быть ошибка или что-то, чего я не понимаю, когда numpy решает преобразовать типы объектов в массив «объектов».

X = np.array([5888275684537373439, 1945629710750298993],dtype=object) + [1158941147679947299,0]
Y = np.array([5888275684537373439, 1945629710750298993],dtype=object) + [11589411476799472995,0]
Z = np.array([5888275684537373439, 1945629710750298993],dtype=object) + [115894114767994729956,0]
print(type(X[0]),X[0]) # <class 'int'> 7047216832217320738
print(type(Y[0]),Y[0]) # <class 'float'> 1.7477687161336848e+19
print(type(Z[0]),Z[0]) # <class 'int'> 121782390452532103395

Сами массивы остаются типа «объект» (как и ожидалось). Неожиданно объекты массива Y были преобразованы в «плавающие». Почему это происходит? В результате я сразу же теряю точность в своей комбинаторике. Чтобы сделать ситуацию еще более странной, удаление 0 исправляет ситуацию:

X = np.array([5888275684537373439, 1945629710750298993],dtype=object) + [1158941147679947299]
Y = np.array([5888275684537373439, 1945629710750298993],dtype=object) + [11589411476799472995]
Z = np.array([5888275684537373439, 1945629710750298993],dtype=object) + [115894114767994729956]
print(type(X[0]),X[0]) # <class 'int'> 7047216832217320738
print(type(Y[0]),Y[0]) # <class 'int'> 17477687161336846434
print(type(Z[0]),Z[0]) # <class 'int'> 121782390452532103395

Я пробовал другие вещи, например, использовать большие/меньшие числа, но редко (если вообще когда-либо) получал «плавающие числа». Это что-то очень специфическое в размере этих конкретных значений «int».


Лучший код, показывающий проблему.

import numpy as np
A = np.array([1,1],dtype=object) + [2**62,0]
B = np.array([1,1],dtype=object) + [2**63,0]
C = np.array([1,1],dtype=object) + [2**64,0]
D = np.array([1,1],dtype=object) + [2**63]
E = np.array([1,1],dtype=object) + [2**63,2**63]
print(type(A[0]),A[0]) # <class 'int'> 4611686018427387905
print(type(B[0]),B[0]) # <class 'float'> 9.223372036854776e+18
print(type(C[0]),C[0]) # <class 'int'> 18446744073709551617
print(type(D[0]),D[0]) # <class 'int'> 9223372036854775809
print(type(E[0]),E[0]) # <class 'int'> 9223372036854775809

Скорее всего, [1158941147679947299,0] преобразуется в массив int dtype и контролирует добавление. [1158941147679947299] может оставаться скаляром Python. Математические операции с массивами dtype объектов являются случайными, не так четко определены и последовательны, как обычные числовые массивы.

hpaulj 01.07.2024 00:18

@hpaulj Но обычные числовые массивы нельзя использовать, если используются целые числа с несколькими сотнями цифр. Обновлено: добавлен лучший пример, который показывает, что он не выполняет преобразование, поскольку [2**63,0] не работает, но [2**63,2**63] работает.

Bobby Ocean 01.07.2024 00:38

Я предполагаю, что это ошибка, а не функция. Я пошел дальше и опубликовал это github.com/numpy/numpy/issues/26818

Bobby Ocean 01.07.2024 00:51

Не можете ли вы явно указать dtype объекта для второго списка/массива? Скажите ему именно то, что вы хотите.

hpaulj 01.07.2024 02:11

@hpaulj Боже мой, да!! Это работает, И мне не нужно менять весь свой код на циклы for. Гений. Если вы хотите опубликовать это, я приму это.

Bobby Ocean 01.07.2024 02:13
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
5
78
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если значение соответствует int64, это int. Но как только оно превышает это значение, NumPy переключается на float, пока не превысит это значение в два раза, а затем переключается обратно на int, поскольку Python int имеет произвольную точность.

import numpy as np
import sys

a = np.array([0, 0], dtype=object) + [sys.maxsize, 0]
b = np.array([0, 0], dtype=object) + [sys.maxsize + 1, 0]
c = np.array([0, 0], dtype=object) + [(2 * sys.maxsize) + 1, 0]
d = np.array([0, 0], dtype=object) + [2 * (sys.maxsize + 1), 0]

print(sys.maxsize)
for x in [a, b, c, d]:
    print(type(x[0]), ':', x[0])

Выход:

9223372036854775807
<class 'int'> : 9223372036854775807
<class 'float'> : 9.223372036854776e+18
<class 'float'> : 1.8446744073709552e+19
<class 'int'> : 18446744073709551616

Вы можете считать это ошибкой (или просто нежелательным), что numpy выбирает int вместо np.int64 в качестве типа для aX), но вполне может быть причина, по которой это происходит по выбору, поскольку операции над значением могут вызвать переполнение, и вы это сделали уточните object.

Учитывать:

a = np.array([0, 0], dtype=np.int64) + [sys.maxsize, 0]
b = np.array([0, 0], dtype=np.int64) + [sys.maxsize + 1, 0]
c = np.array([0, 0], dtype=np.int64) + [(2 * sys.maxsize) + 1, 0]
d = np.array([0, 0], dtype=np.int64) + [2 * (sys.maxsize + 1), 0]

for x in [a, b, c, d]:
    print(type(x[0]), ':', x[0])

Выход:

<class 'numpy.int64'> : 9223372036854775807
<class 'numpy.float64'> : 9.223372036854776e+18
<class 'numpy.float64'> : 1.8446744073709552e+19
<class 'int'> : 18446744073709551616

Что может быть ближе к тому, что вы ожидали.

Обратите внимание, что суммы в вашем примере выглядят следующим образом:

7047216832217320738    # your X[0] < sys.maxsize
9223372036854775807    # sys.maxsize
17477687161336846434   # your Y[0] > sys.maxsize
18446744073709551616   # 2 * (sys.maxsize + 1)
121782390452532103395  # your Z[0] > 2 * (sys.maxsize + 1)

Также обратите внимание, что значение, которое вы добавляете, и сумма имеют значение:

import numpy as np
import sys

a = np.array([0, 0], dtype=np.int64) + [(2 * sys.maxsize) + 1, 0]
b = np.array([0, 0], dtype=np.int64) + [2 * (sys.maxsize + 1), 0]
c = np.array([sys.maxsize, 0], dtype=np.int64) + [(2 * sys.maxsize) + 1, 0]
d = np.array([sys.maxsize, 0], dtype=np.int64) + [2 * (sys.maxsize + 1), 0]
try:
    # compare e to c
    e = np.array([sys.maxsize + 1, 0], dtype=np.int64) + [(2 * sys.maxsize) + 1, 0]
except OverflowError as e:
    print(e)

for x in [a, b, c, d]:
    print(type(x[0]), ':', x[0])

Выход:

int too big to convert
<class 'numpy.float64'> : 1.8446744073709552e+19
<class 'int'> : 18446744073709551616
<class 'numpy.float64'> : 2.7670116110564327e+19
<class 'int'> : 27670116110564327423

Добавление (2 * sys.maxsize) + 1 к sys.maxsize по-прежнему приводит к numpy.float64, но попытка добавить (2 * sys.maxsize) + 1 к sys.maxsize + 1 приводит к переполнению.

Я согласен, что это, по-видимому, пороговые значения. Однако ваше объяснение, похоже, предполагает, что A + [2**63,0] должно вести себя так же, как A + 2**63 или A + [2**63,2**63], но это не так. Таким образом, дело не только в том, что numpy имеет определенные пороговые значения, которые используются, но и в том, что numpy непоследователен, когда эти пороговые значения применяются (если это ожидаемая функция).

Bobby Ocean 01.07.2024 01:40

Еще одно замечание. Насколько я понимаю, dtype=object — это способ заполнить массив numpy указателями вместо целых чисел, чисел с плавающей запятой или чего-то еще. Эти указатели могут быть ЛЮБЫМ объектом Python, и, как правило, numpy будет зависеть от того, как ведет себя этот объект Python. В целом непоследовательно, чтобы numpy приступил к преобразованию типов «объектов» вместо того, чтобы делать то, что ему говорят объекты. Следовательно, A + 1 должен вести себя аналогично циклу for, если объекты в A являются объектами Python. Вот почему массив объектов может содержать 1000-значные целые числа и выполнять с ними модульную арифметику.

Bobby Ocean 01.07.2024 01:44

Вы правы, отмечая различное поведение, которое сбивает с толку и, возможно, в некоторых случаях нежелательно. Я хотел указать на конкретные границы и на то, как избежать, казалось бы, произвольного поведения. Идея «указателей» здесь мутит воду. Если вас волнует тип, не стоит уточнять object — то, что там делает NumPy, соответствует, хотя я согласен, что результат может быть нежелательным. Почему NumPy выбирает тот или иной вариант, скорее всего, связано с внутренней оптимизацией для конкретных случаев использования, и лучше всего исследовать его исходный код и, возможно, документацию — я не стал искать причину.

Grismar 01.07.2024 01:56

Ключевой момент, на который следует обратить внимание: передача object в качестве типа не должна рассматриваться как «сохранить тот тип, который я вам дал». Если вы специально хотите сохранить переданный тип, NumPy не позволит вам — вместо этого вы можете самостоятельно установить тип на основе какого-либо условия.

Grismar 01.07.2024 01:57

Я не уверен в вашем желании избегать типов object. Их суть в том, что такие вещи, как np.array(['thing','other'],dtype=object) + 's', имеют смысл и работают. Причина в том, что numpy подчиняется использованию объекта +. Pandas, как и многие другие модули, наполнены такого рода организационным поведением. В комбинаторике следует ожидать, что такие вещи, как np.array([2**100,3**100],dtype=object) % 1234, будут работать (как они и работают в настоящее время, используя произвольные встроенные целые числа Python). Фактически, при создании массива размером более int64 происходит автоматическое приведение к типу object.

Bobby Ocean 01.07.2024 02:03

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

Bobby Ocean 01.07.2024 02:05

Я думаю, что «ошибка» — это не столько ошибка, сколько запланированная функция: StackOverflow — не место для обсуждения проектных решений в сторонних библиотеках. Я понимаю вашу точку зрения и советую вам сообщить о проблеме разработчикам. Но я бы порекомендовал обосновать обнаруженный вами недостаток конструкции, поскольку я не думаю, что это «ошибка», поскольку она не противоречит спецификации.

Grismar 01.07.2024 02:06

Тем не менее, я думаю, что ответил на вопрос «почему» так хорошо, как и можно было ожидать (по сути, это первая строка моего ответа). Я не могу решить, что вы не согласны с причиной (и это не самое подходящее место).

Grismar 01.07.2024 02:07

Я ценю ваш анализ и обсуждение. И я согласен, что никогда бы не опубликовал это, если бы с самого начала был убежден, что это ошибка, и ждал отзывов. Однако после рассмотрения нескольких примеров с большими целыми числами, строками, смешанными типами объектов Python и т. д. кажется гораздо более очевидным, что это «ошибка», а не задуманная.

Bobby Ocean 01.07.2024 02:11
Ответ принят как подходящий
In [323]: X = np.array([5888275684537373439, 1945629710750298993],dtype=object)

Случай 1 — не слишком большое целое число во втором аргументе:

In [324]: X+[1158941147679947299,0]
Out[324]: array([7047216832217320738, 1945629710750298993], dtype=object)

То же самое, если мы явно создадим массив объектов:

In [325]: X+np.array([1158941147679947299,0],object)
Out[325]: array([7047216832217320738, 1945629710750298993], dtype=object)

2-й случай — преобразование в числа с плавающей запятой:

In [326]: X+[11589411476799472995,0]
Out[326]: array([1.7477687161336848e+19, 1.945629710750299e+18], dtype=object)

Опять же, с явным объектом все в порядке:

In [327]: X+np.array([11589411476799472995,0],object)
Out[327]: array([17477687161336846434, 1945629710750298993], dtype=object)

Преобразование списка в массив без спецификации dtype создает число с плавающей запятой, которое распространяется по сумме:

In [328]: np.array([11589411476799472995,0])
Out[328]: array([1.15894115e+19, 0.00000000e+00])

где в первом случае достаточно мало, чтобы быть int64:

In [329]: np.array([1158941147679947299,0])
Out[329]: array([1158941147679947299,                   0], dtype=int64)

третий случай - оставшееся int:

In [330]: X+[115894114767994729956,0]
Out[330]: array([121782390452532103395, 1945629710750298993], dtype=object)

In [331]: X+np.array([115894114767994729956,0],object)
Out[331]: array([121782390452532103395, 1945629710750298993], dtype=object)

Это достаточно большой размер, чтобы оставаться объектным типом:

In [332]: np.array([115894114767994729956,0])
Out[332]: array([115894114767994729956, 0], dtype=object)

Таким образом, ключевое отличие заключается в том, как список преобразуется в массив. Object dtype — это запасной вариант, который используется, когда невозможно создать «обычный» числовой массив. Вы всегда должны предполагать, что object математика dtype — это «приемный ребенок», что-то, что выбрано на втором месте.

Второй случай без 0 — это другой dtype:

In [334]: np.array([11589411476799472995])
Out[334]: array([11589411476799472995], dtype=uint64)

Никогда не разумно делать предположения о том, когда список преобразуется в массив dtype объекта. Если эта функция важна, сделайте ее явной!

Ваш ответ очень полезен. Однако я не согласен с вашей оценкой того, что пользователь должен знать, что «dtype — это «приемный ребенок»». Проблема не в том, что пользователь предполагает согласованность с A+[2**62,0], A+[2**63,0], A+[2**64,0], A+[2**62,2**62], A+[2**63,2**63], A+[2**64,2**64], A+2**62, A+2**63, A+2**64. Скорее, numpy должен относиться к этому по-другому. Либо выдайте ошибку/предупреждение, либо сделайте все списки одинаковыми, либо сделайте все большие целые числа одинаковыми, либо сделайте все маленькие целые числа одинаковыми. Здесь у нас ничего этого нет.

Bobby Ocean 02.07.2024 18:44

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