Каковы преимущества NumPy перед обычными списками Python?

466

Каковы преимущества NumPy перед обычными списками Python?

У меня около 100 серий финансовых рынков, и я собираюсь создать массив кубов размером 100x100x100 = 1 миллион ячеек. Я буду регрессировать (с 3 переменными) каждый x с каждым y и z, чтобы заполнить массив стандартными ошибками.

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

Каковы будут преимущества, если я перейду на NumPy?

Что если бы у меня было 1000 рядов (то есть 1 миллиард клеток с плавающей запятой в кубе)?

Томас Браун
источник

Ответы:

727

Массивы NumPy более компактны, чем списки Python - список списков, который вы описываете в Python, займет не менее 20 МБ или около того, в то время как 3D-массив NumPy с плавающей точкой одинарной точности в ячейках уместится в 4 МБ. Доступ к чтению и письму также быстрее с NumPy.

Может быть, вас не волнует только миллион ячеек, но вы определенно хотели бы иметь миллиард ячеек - ни один из подходов не подходит для 32-битной архитектуры, но с 64-битными сборками NumPy обходится без 4 ГБ или около того Для одного только Python потребуется по крайней мере около 12 ГБ (много указателей, которые удваиваются в размере) - гораздо более дорогая часть оборудования!

Разница в основном связана с «косвенностью» - список Python представляет собой массив указателей на объекты Python, по крайней мере 4 байта на указатель плюс 16 байтов даже для самого маленького объекта Python (4 для указателя типа, 4 для счетчика ссылок, 4 для значения - и распределители памяти округляются до 16). Массив NumPy - это массив однородных значений - числа одинарной точности занимают 4 байта каждое, числа двойной точности - 8 байтов. Менее гибкий, но вы платите существенно за гибкость стандартных списков Python!

Алекс Мартелли
источник
Я пытался использовать «sys.getsizeof ()» для сравнения размеров списков Python и массивов NumPy с одинаковым количеством элементов, и это, похоже, не указывает на то, что массивы NumPy были намного меньше. В этом ли проблема или sys.getsizeof () испытывает проблемы с определением размера массива NumPy?
Джек Симпсон
3
@JackSimpson getsizeofне надежен. В документации четко указано, что: учитывается только потребление памяти, непосредственно относящееся к объекту, а не потребление памяти объектами, к которым он относится. Это означает, что если у вас есть вложенные списки Python, размер элементов не учитывается.
Бакуриу
4
getsizeofв списке указывается только объем ОЗУ, который использует сам объект списка, и объем ОЗУ, используемый указателями в массиве данных, но не говорит, сколько ОЗУ используют объекты, на которые ссылаются эти указатели.
PM 2Ring
@ AlexMartelli, не могли бы вы дать мне знать, где вы взяли эти цифры?
lmiguelvargasf
Просто наперед, ваша оценка размера эквивалентного списка списков списков в Python отключена. Массив размером 4 ГБ из C floats (4 байта) будет преобразован в нечто ближе к 32 ГБ lists и Python float(которые на самом деле являются C doubles), а не 12 ГБ; каждый floatна 64-битном Python занимает ~ 24 байта (при условии отсутствия потерь в выравнивании в распределителе), плюс еще 8 байтов в listдля хранения ссылки (и это игнорирует перераспределение и заголовки объектов для самих lists, что может добавить еще один ГБ в зависимости от точно, сколько перераспределения происходит).
ShadowRanger
232

NumPy не просто более эффективен; это также более удобно. Вы получаете множество векторных и матричных операций бесплатно, что иногда позволяет избежать ненужной работы. И они также эффективно реализованы.

Например, вы можете прочитать свой куб прямо из файла в массив:

x = numpy.fromfile(file=open("data"), dtype=float).reshape((100, 100, 100))

Сумма по второму измерению:

s = x.sum(axis=1)

Найдите, какие ячейки выше порога:

(x > 0.5).nonzero()

Удалите каждый четный фрагмент по третьему измерению:

x[:, :, ::2]

Кроме того, многие полезные библиотеки работают с массивами NumPy. Например, статистический анализ и визуализация библиотек.

Даже если у вас нет проблем с производительностью, изучение NumPy стоит усилий.

Роберто Бонваллет
источник
Спасибо - вы предоставили еще одну вескую причину в своем третьем примере, поскольку я буду искать в матрице ячейки выше порогового значения. Более того, я загружался из sqlLite. Файловый подход будет намного эффективнее.
Томас Браун
112

Алекс упомянул эффективность памяти, а Роберто упомянул удобство, и это хорошие моменты. Для еще нескольких идей я упомяну скорость и функциональность .

Функциональность: вы получаете много встроенного с NumPy, FFT, сверточными данными, быстрым поиском, базовой статистикой, линейной алгеброй, гистограммами и т. Д. И действительно, кто может жить без FFT?

Скорость: вот тест для суммирования по списку и массиву NumPy, показывающий, что сумма в массиве NumPy в 10 раз быстрее (в этом тесте пробег может варьироваться).

from numpy import arange
from timeit import Timer

Nelements = 10000
Ntimeits = 10000

x = arange(Nelements)
y = range(Nelements)

t_numpy = Timer("x.sum()", "from __main__ import x")
t_list = Timer("sum(y)", "from __main__ import y")
print("numpy: %.3e" % (t_numpy.timeit(Ntimeits)/Ntimeits,))
print("list:  %.3e" % (t_list.timeit(Ntimeits)/Ntimeits,))

который в моих системах (пока я выполняю резервное копирование) дает:

numpy: 3.004e-05
list:  5.363e-04
tom10
источник
44

Вот хороший ответ из FAQ на сайте scipy.org :

Какие преимущества предлагают массивы NumPy перед (вложенными) списками Python?

Списки Python являются эффективными контейнерами общего назначения. Они поддерживают (достаточно) эффективную вставку, удаление, добавление и объединение, а понимание списков в Python позволяет легко создавать и манипулировать ими. Однако у них есть определенные ограничения: они не поддерживают «векторизованные» операции, такие как поэлементное сложение и умножение, и тот факт, что они могут содержать объекты разных типов, означает, что Python должен хранить информацию о типе для каждого элемента и выполнять код диспетчеризации типов. при работе на каждом элементе. Это также означает, что очень мало операций со списком может быть выполнено эффективными циклами Си - каждая итерация потребует проверки типов и другой бухгалтерии API Python.

Элиэзер
источник
9

Все они выдвинули на первый план почти все основные различия между массивом numpy и списком Python, я просто опишу их здесь:

  1. При создании Numpy массивы имеют фиксированный размер, в отличие от списков Python (которые могут динамически расти). Изменение размера ndarray создаст новый массив и удалит оригинал.

  2. Все элементы в массиве Numpy должны быть одного типа данных (мы также можем иметь гетерогенный тип, но это не позволит вам выполнять математические операции) и, следовательно, иметь одинаковый размер в памяти

  3. Numpy массивы облегчают продвижение математических и других типов операций на большом количестве данных. Как правило, такие операции выполняются более эффективно и с меньшим количеством кода, чем это возможно при использовании последовательностей питонов

Парвез Хан
источник