2017-01-17 4 views
4

Кажется, я нашел ловушку с использованием .sum() на массивах numpy, но я не могу найти объяснения. По сути, если я попытаюсь суммировать большой массив, тогда я начинаю получать бессмысленные ответы, но это происходит без звука, и я не могу понять, насколько результат достаточно для Google.numpy.sum() дает странные результаты на больших массивах

Например, это работает точно так же, как и ожидалось:

a = sum(xrange(2000)) 
print('a is {}'.format(a)) 

b = np.arange(2000).sum() 
print('b is {}'.format(b)) 

Давать тот же результат для обоих:

a is 1999000 
b is 1999000 

Однако, это не работает:

c = sum(xrange(200000)) 
print('c is {}'.format(c)) 

d = np.arange(200000).sum() 
print('d is {}'.format(d)) 

Отдавая следующий результат:

c is 19999900000 
d is -1474936480 

И на еще большем массиве можно получить положительный результат. Это более коварно, потому что я не могу определить, что что-то необычное происходит вообще. Например это:

e = sum(xrange(100000000)) 
print('e is {}'.format(e)) 

f = np.arange(100000000).sum() 
print('f is {}'.format(f)) 

дает это:

e is 4999999950000000 
f is 887459712 

Я догадался, что это было связано с типами данных и в самом деле, даже с помощью питона float кажется, чтобы исправить эту проблему:

e = sum(xrange(100000000)) 
print('e is {}'.format(e)) 

f = np.arange(100000000, dtype=float).sum() 
print('f is {}'.format(f)) 

Предоставление:

e is 4999999950000000 
f is 4.99999995e+15 

У меня нет фона в Comp. Sci. и оказался застрявшим (возможно, это обман). Вещи, которые я пробовал:

  1. numpy массивы имеют фиксированный размер. Неа; this, похоже, я должен сначала нажать MemoryError.
  2. Возможно, у меня может быть 32-разрядная установка (возможно, не актуальна); Нет, я последовал за this и подтвердил, что у меня 64-разрядная версия.
  3. Другие примеры странных sum поведение; nope (?) Я нашел this, но я не вижу, как это применимо.

Может кто-нибудь, пожалуйста, кратко объясните, что мне не хватает, и скажите мне, что мне нужно для чтения? Кроме того, за исключением того, что каждый раз задавать dtype, есть ли способ остановить это или дать предупреждение?

Возможно отношение:

Windows 7

numpy 1.11.3

Исчерпывание Enthought Навес на Python 2.7.9

+1

вероятно потому, что 'numpy' целые полагаться на целых C-типа, тогда как python имеет неограниченный целочисленный диапазон. Поплавки ... плавают. Они могут быть очень высокими. –

+1

Проверьте 'np.arange (5) .dtype'. Вероятно, он использует 32-битные целые числа вместо 64-битных. Кроме того, убедитесь, что вы выполняете все эти проверки на одной и той же установке Python. – user2357112

+0

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

ответ

5

В Windows (также в 64-разрядной системе) по умолчанию используется целочисленное число NumPy, если вы конвертируете из Python ints 32-разрядный. В Linux и Mac он 64-битный.

Укажите 64-разрядное целое число, и он будет работать:

d = np.arange(200000, dtype=np.int64).sum() 
print('d is {}'.format(d)) 

Выход:

c is 19999900000 
d is 19999900000 

Хотя не самый элегантный, вы можете сделать некоторые monkey patching, используя functools.partial:

from functools import partial 

np.arange = partial(np.arange, dtype=np.int64) 

С этого момента np.arange работает по умолчанию с 64-битными целыми числами.

+1

Это имеет смысл для меня, но один из моих ключевых вопросов заключался в том, есть ли способ отметить это. Слишком легко для этого подпадать под мой радар при отладке; есть ли способ определить, что я пропустил это? – roganjosh

+0

Добавлено возможное решение. –

3

Я не NumPy специалист, но может воспроизвести ваш arange(200000) результат в чистом Python:

>>> s = 0 
>>> for i in range(200000): 
...  s += i 
...  s &= 0xffffffff 
>>> s 
2820030816 
>>> s.bit_length() 
32 
>>> s - 2**32 # adjust for that "the sign bit" is set 
-1474936480 

Другими словами, результат вы видите то, что я ожидаю, если numpy делает свою арифметику подписали 32-битные целые числа с 2-мя словами.

Поскольку я не эксперт numpy, я не могу предложить хороший подход, чтобы никогда не удивляться (я бы оставил это как комментарий, но тогда я не смог показать красиво отформатированный код).

+1

Начните поиск «двух дополнений». При использовании различных фиксированных битовых ширин (8, 16, 32, 64) это схема, в которой практически все компьютерное оборудование использует для представления целых чисел. На низкоуровневом языке, таком как 'C' (который кодируется CPython и numpy), такими родными типами устройств являются те, с которыми вы работаете. Для реализации Python-иллюзии бесконечно широких двух целых чисел требуется много кода под обложками. –

+0

Огромное спасибо, сейчас я читаю. Это «много кода под обложками» защитило меня от большого количества, спасибо :) В этом случае Вим связался с открытым билетом с 'numpy' в своем ответе; было признано, что в этом конкретном случае нет ошибки переполнения (что меня толкало), но оно было открыто уже много лет :( – roganjosh

3

Это явно целочисленный тип numpy, переполненный 32-битными. Как правило, вы можете настроить NumPy потерпеть неудачу в таких ситуациях с помощью np.seterr:

>>> import numpy as np 
>>> np.seterr(over='raise') 
{'divide': 'warn', 'invalid': 'warn', 'over': 'warn', 'under': 'ignore'} 
>>> np.int8(127) + np.int8(2) 
FloatingPointError: overflow encountered in byte_scalars 

Однако sum явно документированы с поведением «ошибка не поднимается при переполнении», так что вы можете быть не повезло здесь. Использование numpy часто является компромиссом производительности для удобства!

Однако вы можете вручную указать DTYPE для аккумулятора, например:

>>> a = np.ones(129) 
>>> a.sum(dtype=np.int8) # will overflow 
-127 
>>> a.sum(dtype=np.int64) # no overflow 
129 

Часы билет #593, потому что это открытый вопрос, и это может быть исправлено Numpy дэвов когда-то.

+0

Большое спасибо за включение билета. Я подозревал, что это переполнение, но отсутствие любое предупреждение заставило меня подвергнуть сомнению то, что на самом деле происходит. Это, в сочетании с тем, что Windows автоматически не преобразует int в 32-битный, меня смущает. – roganjosh

2

Тип целочисленного значения по умолчанию Numpy совпадает с типом C long. Теперь это не гарантируется 64-бит на 64-битной платформе. На самом деле, в Windows, long is всегда 32-бит.

В результате сумма numpy переполняет значение и возвращается назад.

К сожалению, насколько мне известно, существует no Способ изменения по умолчанию dtype. Вы должны указывать его как np.int64 каждый раз.

Вы можете попробовать создать свой собственный arange:

def arange(*args, **kw): 
    return np.arange(dtype=np.int64, *args, **kw) 

, а затем использовать эту версию вместо Numpy годов.

EDIT:Если вы хотите, чтобы пометить это, вы можете просто поставить что-то вроде этого в верхней части кода:

assert np.array(0).dtype.name != 'int32', 'This needs to be run with 64-bit integers!' 
Смежные вопросы