Які переваги NumPy над звичайними списками Python?


466

Які переваги NumPy над звичайними списками Python?

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

Я чув, що для "великих матриць" я повинен використовувати NumPy на відміну від списків Python, з міркувань продуктивності та масштабованості. Справа в тому, що я знаю списки Python, і вони, здається, працюють на мене.

Якими будуть переваги, якщо я перейду на NumPy?

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

Відповіді:


727

Масиви NumPy є більш компактними, ніж списки Python - список списків, як ви описуєте, в Python, зайняв би щонайменше 20 Мб або близько того, в той час як NumPy 3D-масив з одноточною плаваючою плиткою в клітинках розміщуватиметься в 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 Гб float(4 байти) перекладається на щось ближче до 32 Гб list, а на Python floats (що насправді є C doubles), а не на 12 ГБ; кожен float64-розрядний 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

44

Ось приємна відповідь із FAQ на веб-сайті scipy.org :

Які переваги надають масиви NumPy над (вкладеними) списками Python?

Списки Python - це ефективні контейнери загального призначення. Вони підтримують (досить) ефективне вставлення, видалення, додавання та конкатенацію, а розуміння списку Python полегшує їх конструювання та маніпулювання. Однак у них є певні обмеження: вони не підтримують "векторизовані" операції, такі як додавання та множення елементів, а той факт, що вони можуть містити об'єкти різних типів, означає, що Python повинен зберігати інформацію про тип для кожного елемента, а також повинен виконувати диспетчерський код типу при роботі над кожним елементом. Це також означає, що дуже мало операцій зі списком можна здійснити за допомогою ефективних циклів C - кожна ітерація потребує перевірки типів та іншої бухгалтерії API Python.


9

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

  1. Numpy масиви мають фіксований розмір при створенні, на відміну від списків python (які можуть динамічно зростати). Зміна розміру ndarray створить новий масив та видалить оригінал.

  2. Усі елементи в масиві Numpy повинні бути одного типу даних (ми можемо мати і неоднорідний тип, але це не дозволить вам мати математичні операції) і, таким чином, буде однакового розміру в пам'яті

  3. Масивні масиви полегшуються вдосконаленням математичних та інших видів операцій над великою кількістю даних. Зазвичай такі операції виконуються ефективніше і з меншим кодом, ніж це можливо, використовуючи пітони, побудовані в послідовностях

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.