Які переваги 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.