Каковы преимущества NumPy над регулярными списками Python?
Каковы преимущества NumPy перед обычными списками Python?
У меня около 100 серий финансовых рынков, и я собираюсь создать массив кубов размером 100x100x100 = 1 миллион ячеек. Я буду регрессировать (с 3 переменными) каждый x с каждым y и z, чтобы заполнить массив стандартными ошибками.
Я слышал, что для "больших матриц" я должен использовать NumPy, а не списки Python, из соображений производительности и масштабируемости. Дело в том, что я знаю списки Python, и они, кажется, работают для меня.
Каковы будут преимущества, если я перейду на NumPy?
Что если бы у меня было 1000 рядов (то есть 1 миллиард ячеек с плавающей запятой в кубе)?
Ответы
Ответ 1
Массивы NumPy более компактны, чем списки Python - список списков, которые вы описываете, в Python, будет занимать не менее 20 МБ или около того, в то время как массив NumPy 3D с плавающей точкой с одинарной точностью в ячейках будет вписываться в 4 MB. Доступ к чтению и записи элементов также выполняется быстрее с помощью NumPy.
Возможно, вам все равно, что всего за миллион ячеек, но вы определенно хотели бы за миллиард ячеек - ни один подход не поместился бы в 32-битной архитектуре, но с 64-битными сборками NumPy уйдет с 4 GB или около того, одному Python понадобится по крайней мере около 12 ГБ (много указателей, размер которых вдвое больше) - гораздо более дорогостоящее устройство!
Разница в основном обусловлена "косвенностью" - список Python представляет собой массив указателей на объекты Python, по меньшей мере 4 байта на указатель плюс 16 байтов для даже самого маленького объекта Python (4 для указателя типа, 4 для справки count, 4 для значения - и распределители памяти округляются до 16). Массив NumPy представляет собой массив однородных значений - числа с одинарной точностью занимают по 4 байта, с двойной точностью - 8 байтов. Менее гибкий, но вы платите за гибкость стандартных списков Python!
Ответ 2
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 стоит усилий.
Ответ 3
Алекс упомянул об эффективности памяти, и Роберто упомянул удобство, и это оба хорошие моменты. В качестве еще нескольких идей я упомянул скорость и функциональность.
Функциональность: вы получаете много встроенных функций с NumPy, 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
Ответ 4
Вот хороший ответ из FAQ на сайт scipy.org:
Какие преимущества предлагают массивы NumPy над (вложенными) списками Python?
Списки Pythons - это эффективные контейнеры общего назначения. Они поддерживают (справедливо) эффективная вставка, удаление, добавление и конкатенация, и понимание Pythons-списков упрощает их создание и манипулировать. Однако они имеют определенные ограничения: они не поддерживают "векторизованные" операции, такие как элементное сложение и умножение, и тот факт, что они могут содержать объекты разных типов, означает что Python должен хранить информацию о типе для каждого элемента и должен введите код диспетчеризации типа при работе на каждом элементе. Эта также означает, что очень немногие операции с списками могут выполняться посредством эффективные C-циклы - каждая итерация потребует проверки типов и других Бухгалтерия API Python.
Ответ 5
Обратите также внимание на то, что существует поддержка таймсерий, основанных на NumPy, в записях времени:
http://pytseries.sourceforge.net
Для регрессии я уверен, что NumPy будет на порядок быстрее и удобнее списков даже для проблемы 100 ^ 3.
Ответ 6
Все они выдвинули на первый план почти все основные различия между массивом numpy и списком Python, я просто опишу их здесь:
-
При создании Numpy массивы имеют фиксированный размер, в отличие от списков Python (которые могут динамически расти). Изменение размера ndarray создаст новый массив и удалит оригинал.
-
Все элементы в массиве Numpy должны быть одного и того же типа данных (мы также можем иметь гетерогенный тип, но это не позволит вам выполнять математические операции) и, следовательно, иметь одинаковый размер в памяти
-
Numpy массивы облегчают продвижение математических и других типов операций на большом количестве данных. Как правило, такие операции выполняются более эффективно и с меньшим количеством кода, чем это возможно при использовании последовательных сборок питонов
Ответ 7
Numpy предоставляет ряд очень мощных математических инструментов для работы с массивами или матрицами numpy, если хотите. Поскольку numpy очень хорошо оптимизирован и написан на C, он работает намного быстрее, чем если бы вы написали свой код на python. Не говоря уже о том, что было бы больно в задницу самому кодировать все функции.
Ответ 8
Скорее, я не уверен. Вот краткий пример: я создал функцию (из x), которая возвращает список простых чисел между 2 и x:
-
Регулярная функция Python с использованием списков:
def findprimeupto(x):
primes = []
n_primes = []
for i in range(2, x):
if not (i in n_primes):
primes.append(i)
n_primes.append(i)
for j in range(len(primes)):
if i > n_primes[j]:
n_primes[j] += primes[j]
return primes
import time
start_time = time.time()
findprimeupto(10000)
print("--- %s seconds ---" % str(time.time() - start_time))
-
и C-подобная функция Python с использованием массивов NumPy:
import numpy
def findprimeupto(x):
primes = numpy.array(numpy.zeros(x), dtype=numpy.int32)
n_primes = numpy.array(numpy.zeros(x), dtype=numpy.int32)
primeslen = 0
for i in range(2, x):
flag = 1
for j in range(primeslen):
if n_primes[j] == i:
flag = 0
break
if flag:
primes[primeslen] = i
n_primes[primeslen] = i
primeslen += 1
for j in range(primeslen):
if i > n_primes[j]:
n_primes[j] += primes[j]
return [primeslen, primes]
import time
start_time = time.time()
result = findprimeupto(10000)
#for i in range(result[0]):
# print('{:d} '.format(result[1][i]), end="")
print()
print("--- %s seconds ---" % str(time.time() - start_time))
Первая, предположительно медленная реализация с использованием списков, выполняется за 0,6 секунды, а более поздней, предположительно быстрой реализации NumPy, требуется 50 секунд. Если кто-то может указать, почему я был бы очень благодарен.
BTW, чистая программа C, которая является более или менее копией версии функции NumPy функции, выполняется менее чем за 0,04 s. Скорость C еще более очевидна при больших x:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void findprimesupto(int n, int *primeslen, int *primes, int *n_primes) {
int i, j, flag;
*primeslen = 0;
for (i=2; i <= n; i++) {
for (j=0, flag=1; j < *primeslen; j++)
if (n_primes[j] == i) {
flag = 0;
break;
}
if (flag) {
primes[*primeslen] = i;
n_primes[*primeslen] = i;
(*primeslen)++;
}
for (j=0; j < *primeslen; j++)
if (i > n_primes[j])
n_primes[j] += primes[j];
}
}
int main() {
int n = 10000, primeslen = 0, i;
int *primes, *n_primes;
clock_t start, diff;
start = clock();
primes = malloc(n * sizeof(int));
n_primes = malloc(n * sizeof(int));
findprimesupto(n, &primeslen, primes, n_primes);
/* for (i=0; i < primeslen; i++)
printf("%d ", primes[i]);
printf("\n");
*/
diff = clock() - start;
printf("Time: %f s\n", (float) diff / (float) CLOCKS_PER_SEC);
free(primes);
free(n_primes);
return 0;
}