На страницах школьного курса мы говорили о недостатках стандартного массива List в Python, указывая, в первую очередь, на его низкую производительность. Фактически, “отраслевой” заменой массиву list
является массивы библиотеки NumPy, предоставляющей высокопроизводительные алгоритмы обработки массивов, сравнимые с компилируемыми языками. Приведем небольшой пример программы для сравнения стабильной сортировки массивов List и NumPy с замером скорости работы и объема памяти, занимаемой этими объектами, для 1000000
элементов.
import numpy as np import timeit as t import sys from random import randint L = [] Size = 1000000 for j in range(Size): L.append(randint(10, 99)) # Получили массив List Ar = np.array(L, dtype=np.int8) # Получили массив NumPy print('Размеры объектов в памяти:') print('List ->', sys.getsizeof(L)) print('NumPy ->', sys.getsizeof(Ar)) ################ Сортровка List ################## a = t.default_timer() L.sort() time = t.default_timer() - a print('Время выполнения для List -> {:.3f}'.format(time)) ############## Сортровка массива NumPy ########### a = t.default_timer() Ar.sort(kind='mergesort') time = t.default_timer() - a print('Время выполнения для NumPy -> {:.3f}'.format(time))
Вывод
Размеры объектов в памяти: List -> 8448728 NumPy -> 1000104 Время выполнения для List -> 0.107 Время выполнения для NumPy -> 0.002
Приятной особенностью NumPy является вполне вразумительный способ определения типа данных элементов массива (как для целых, так и для действительных типов), что позволяет существенно (более чем в 8 раз) сократить объем используемой памяти, если необходимо хранить элементы малой ширины типа. Массив array
в python тоже имеет подобные возможности, но в NumPy, на мой взгляд, используется более понятный синтаксис. Не взирая на то, что разработчики python в последних версиях существенно нарастили производительность List, тем не менее NumPy показывает пятидесятикратное превосходство по производительности!
Что же представляет из себя NumPy? NumPy – это научная библиотека, которая предоставляет объект многомерного массива и большой набор специальных функций для его обработки. Между массивами NumPy и стандартными последовательностями Python есть важные отличия:
- Массивы NumPy при создании имеют фиксированный размер;
- Все элементы в массиве NumPy имеют один и тот же тип данных и, следовательно, имеют одинаковый размер в памяти. (Но можно иметь массивы объектов, что позволяет использовать массивы элементов разного размера).
Массив NumPy – Ar
, в программе выше, был создан на основе обычного массива List, в типичном стиле python. В следующей программе, перечислены другие способы инициализации массива NumPy.
import numpy as np # 1 - Явная инициализация Ar = np.array([1, 2, 3, 4, 5]) print('1 =>', Ar) # 2 - С указанием целого типа (ширина 2 байт) Ar = np.array([1, 2, 3, 4, 5], dtype=np.int16) print('2 =>', Ar) # 3 - С указанием действительного типа (ширина 2 байт) Ar = np.array([1, 2, 3, 4, 5], dtype=np.float16) print('3 =>', Ar) # 4 - Ранжирование целых Ar = np.arange(10, 100, 10, dtype=np.int64) print('4 =>', Ar) # 5 - Ранжирование действительных Ar = np.arange(0.1, 0.8, 0.1) print('5 =>', Ar) # 6 - Ранжирование с отрицательными значениями Ar = np.arange(-5, 6) print('6 =>', Ar) # 7 - Рандомизация целых Ar = np.random.randint(10, 100, 10) print('7 =>', Ar) # 8 - Рандомизация действительных Ar = np.random.rand(10) print('8 =>', Ar) # 9 - Рандомизация на отрезке [a, b) по формуле: # (b - a) * random_sample() + a Ar = 10 * np.random.random_sample(10) - 5 print('9 =>', Ar) # 10 - Массив нулевых значений Ar = np.zeros(10, dtype=np.byte) print('10 =>', Ar)
Вывод:
1 => [1 2 3 4 5] 2 => [1 2 3 4 5] 3 => [1. 2. 3. 4. 5.] 4 => [10 20 30 40 50 60 70 80 90] 5 => [0.1 0.2 0.3 0.4 0.5 0.6 0.7] 6 => [-5 -4 -3 -2 -1 0 1 2 3 4 5] 7 => [93 96 75 58 12 90 16 22 21 48] 8 => [0.25654758 0.2743223 0.56383247 0.44199293 0.34784903 0.66442022 0.68438964 0.63694037 0.63224638 0.82797918] 9 => [ 3.03670668 4.47320688 -0.26251012 3.17943722 3.16389912 -4.91981051 -1.93778032 0.15214871 -4.91833127 4.90292445] 10 => [0 0 0 0 0 0 0 0 0 0]
Помимо простого генератора псевдо-случайных чисел (показанного в этой программе), класс random
имеет полный набор функций распределения случайных величин (такие как биноминальное, гамма, нормальное и мн. др.).
В программах выше показана работа с линейным массивом, он же вектор, но NumPy может работать и с многомерными массивами (с матрицей – в двумерном представлении). Более того, библиотека имеет внушительный набор специальных функций для работы с матрицами. В программе ниже демонстрируются базовые приемы работы с матрицами.
import numpy as np # Явная инициализация Ar1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(Ar1) # Заполнение случайными числами Ar2 = np.floor(10 * np.random.randn(3, 3)) print(Ar2) # Ранжирование Ar3 = np.arange(1, 10).reshape(3, 3) print(Ar3) # Единичная матрица Ar4 = np.ones((3, 3), dtype=np.byte) print(Ar4) # Сложение матриц Ar = Ar3 + Ar4 print(Ar) # Вычитание матриц Ar = Ar3 - Ar4 print(Ar) # Умножение матрицы на число Ar = 3 * Ar print(Ar) # Умножение матриц Ar = Ar @ Ar3 print(Ar) # Транспонирование матрицы Ar = np.transpose(Ar) print(Ar)
Вывод:
[[1 2 3] [4 5 6] [7 8 9]] [[ -6. -19. -7.] [ -3. -17. -16.] [ 0. -14. 6.]] [[1 2 3] [4 5 6] [7 8 9]] [[1 1 1] [1 1 1] [1 1 1]] [[ 2 3 4] [ 5 6 7] [ 8 9 10]] [[0 1 2] [3 4 5] [6 7 8]] [[ 0 3 6] [ 9 12 15] [18 21 24]] [[ 54 63 72] [162 198 234] [270 333 396]] [[ 54 162 270] [ 63 198 333] [ 72 234 396]]
В тех программах на Python, в которых производится работа с массивами и нужна скорость, несомненно, необходимо использовать массивы NumPy, вместо медленного List. Впрочем, разработчики самого Python не стесняются это признавать. Таким образом, любая серьезная разработка на языке python не обходится без NumPy. На сайте библиотеки вы найдете подробную документацию и множество примеров использования NumPy.
Массивы NumPy часто применяются для отрисовки графиков с другой известной библиотекой Matplotlib. Ниже демонстрируется простой пример вывода синусоиды с помощью этих двух библиотек.
import numpy as np import matplotlib.pyplot as plt fig, ax = plt.subplots() Ar = np.sin(10 * np.linspace(0, 1)) ax.plot(Ar, '-o', ms=10, alpha=0.7, mfc='red') ax.grid() plt.show()
Вывод:
О библиотеке Matplotlib мы расскажем в отдельной статье.