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

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

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

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

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

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

9 ответов

Решение

Массивы 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!

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 стоит усилий.

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

Функциональность: вы получаете много встроенного с помощью 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

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

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

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

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

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

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

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

Также обратите внимание, что есть поддержка временных рядов на основе NumPy в scikits временных рядов:

http://pytseries.sourceforge.net/

Для регрессии я почти уверен, что NumPy будет на несколько порядков быстрее и удобнее, чем списки даже для задачи 100^3.

Стандартным изменяемым многоэлементным контейнером в Python является список. Благодаря динамической типизации Python мы можем даже создавать гетерогенные списки. Чтобы разрешить эти гибкие типы, каждый элемент в списке должен содержать информацию о собственном типе, счетчике ссылок и другую информацию. То есть каждый элемент является полным объектом Python. В особом случае, когда все переменные одного типа, большая часть этой информации является избыточной; гораздо эффективнее хранить данные в массиве фиксированного типа (в стиле NumPy). Массивы фиксированного типа в стиле NumPy лишены этой гибкости, но гораздо более эффективны для хранения данных и управления ими.

  • NumPy - это не другой язык программирования, а модуль расширения Python. Он обеспечивает быстрые и эффективные операции с массивами однородных данных. Numpy имеет фиксированный размер создания.
  • В Python: списки записываются в квадратных скобках. Эти списки могут быть однородными или разнородными.
  • Основные преимущества использования Numpy Arrays Over Python Lists:
    1. Он потребляет меньше памяти.
    2. Быстро по сравнению со списком Python.
    3. Удобно использовать.

По скорости я не уверен в этом. Вот краткий пример: я создал функцию (из 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 секунд. Если кто-то может указать, почему я очень ценю это.

Кстати, программа на чистом C, которая является более или менее копией версии функции NumPy, выполняется менее чем за 0,04 с. Скорость 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;
    }
Другие вопросы по тегам