Рубріки: Теория

NumPy: основы, которые должен знать каждый

Андрей Коваленко

В этой статье мы рассмотрим математическую библиотеку NumPy (и попутно обсудим MATLAB, массивы и Python), предназначенную для эффективной работы с массивами в Python.

Содержание:
1. Общие сведения
2. NumPy и MATLAB
3. Установка NumPy
4. Массивы в NumPy и операции над ними
Заключение

1. Общие сведения

Любой зрелый язык программирования со временем обзаводится библиотекой для математических вычислений. В C# это Math.NET Numerics, в Ruby — SciRuby, Fortran (кстати, что это?) сам по себе представляет огромную математическую библиотеку или, если угодно, целый город, состоящий из библиотек.

Если быть более точным, NumPy — это высокоэффективная библиотека для обработки больших (огромных) массивов и матриц, включающая математические функции для обработки этих типов данных.

Проект начался в 2005 году как улучшение существующей библиотеки Numeric, достаточно популярной, но имеющей проблемы с производительностью и поддерживаемостью.

Код Numeric (и его последователя NumArray) был полностью переписан и оптимизирован. Сейчас NumPy — общепризнанный проект с открытым кодом и обладает огромным сообществом. SciPy, библиотека Python для математических вычислений, включает некоторые компоненты NumPy, в частности, свой основной тип данных — массив.

2. NumPy и MATLAB

Если сравнивать NumPy с другим ПО для математических вычислений, например MATLAB, то в своей области — линейная алгебра, операции над векторами (массивами) и матрицами — «герой» нашей статьи покрывает абсолютное большинство нужд, обеспечиваемых последним, а в некоторых областях даже превосходит его.

Ключевые различия NumPy и MATLAB

MATLAB NumPy
В MATLAB базовый тип даже для скалярных (в частности, числовых) данных — многомерный массив.

Массивы в MATLAB хранятся как двумерные массивы из чисел с плавающей точкой с двойной точностью, кроме случаев, когда вы сами указываете количество измерений и тип.

Операции над двумерными экземплярами этих массивов основываются на матричных операциях линейной алгебры.

В NumPy, базовый тип — это многомерный массив. Переменные типа «массив» в NumPy обычно хранятся в n-мерных массивах с «минимальным» (наименее затратном в смысле занимаемой памяти) типом, необходимым для хранения объектов в последовательности, кроме случаев, когда вы задаете количество измерений и тип.

NumPy производит поэлементные операции, поэтому перемножение двумерных массивов операцией «*» — это не перемножение матриц, это именно умножение элемента-на-элемент. Оператор «@», доступный начиная с версии Python 3.5, может использоваться для привычного перемножения матриц.

MATLAB начинает нумерацию (индексацию) начиная с 1; a(1) — это первый элемент. NumPy, так же как Python и абсолютное большинство современных языков программирования, начинает индексацию с 0; первым элементом будет a[0].
Язык сценариев (скриптовый язык) MATLAB был создан для поддержки привычной нотации линейной алгебры, поэтому синтаксис для некоторых операций с массивами более компактный, чем в NumPy.

 

С другой стороны, API для создания графических интерфейсов и, шире, полноценных приложений — достаточно трудоемкий процесс.

NumPy основан на Python, языке программирования общего назначения. Преимущество NumPy — в полноценном и неограниченном доступе к питоновским библиотекам, включая SciPy, Matplotlib, Pandas, OpenCV, и так далее. Вдобавок, Python часто встроен (embedded) в качестве скриптового языка в другое программное и даже аппаратное (например, NVIDIA CUDA) обеспечение, тем самым позволяя использовать NumPy в этих платформах.
Нарезание (slicing) массивов в MATLAB использует семантику передачи по ссылке, с «ленивым» (отложенным) копированием во время записи, чтобы избежать создания копий данных до момента когда они необходимы. Операции нарезания копируют части массива. Операция нарезания в NumPy использует передачу по ссылке, что позволяет избежать копирования аргументов. Результаты операций нарезания на самом деле представления (Views) массивов, а не копии массивов.

3. Установка NumPy

Если у вас установлен IPython (интерактивный Python), можно использовать его. В принципе, подойдет любая оболочка Python, позволяющая запускать интерактивный режим (IDE).

Если вы новичок в мире Python, самый простой способ — скачать PyCharm от JetBrains (есть бесплатная Community Edition), после чего добавить пакет (Package) NumPy: View -> Tool Windows -> Python Packages:

После чего нажать Install в окне пакетов (справа).

Теперь вызываем интерактивную панель (Tools -> Python or Debug Console) и начинаем работу.

4. Массивы в NumPy и операции над ними

Центральный объект в NumPy — массив. Семантически он подобен спискам (Lists) Python, но его элементы должны иметь одинаковый тип, например int или float. Но, в отличие от списков, операции над массивами производятся намного быстрее, иногда на порядок.

>>> import numpy as np
>>> from numpy import *

Для упрощения интерактивной работы в консоли, импортируем numpy:

Базовые операции над массивами

array — функция создания массива из списка:

a = np.array([7, 2, 4, 1], float)
>>> a
array([7., 2., 4., 1.])
>>> type(a)
<class 'numpy.ndarray'>

Функция array принимает два аргумента: список, который будет сконвертирован в массив, и тип элемента массива. Доступ к элементам массива осуществляется так же, как и к элементам списка:

>>> a[:2]
array([7., 2.])
>>> a[1]
2.0
>>> a[3] = 14
>>> a
array([ 7.,  2.,  4., 14.])

NumPy поддерживает многомерные массивы. Двумерный массив обычно называют матрицей.

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a
array([[ 1.,  2.,  3.],
[ 4.,  5.,  6.]])
>>> a[0,0]
1.0
>>> a[0,1]
2.0

Нарезание массива (array slicing) работает с многомерными массивами по аналогии с одномерными. Каждый срез применяется как фильтр для установленного измерения. Если вам надо указать, что используются все элементы данного измерения, используйте «:» для этого измерения:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a[1,:]
array([ 4.,  5.,  6.])
>>> a[:,2]
array([ 3.,  6.])
>>> a[-1:, -2:]
array([[ 5.,  6.]])

У объекта ndarray есть атрибут shape, который возвращает размерность и размеры массива; для матрицы он вернет количество строк и столбцов в матрице:

>>> a.shape
(2, 3)

Атрибут dtype объекта ndarray возвращает тип данных элемента массива:

>>> a.dtype
dtype('float64')

float64 — это формальный идентификатор типа float, чисел с двойной точностью, которые используются для представления действительных чисел в NumPy.

Метод len возвращает длину первого измерения:

a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> len(a)
2

Метод in проверяет, содержится ли указанный элемент в данном массиве:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> 3 in a
True
>>> 11 in a
False

Многомерные массивы можно создавать из одномерных с помощью метода reshape. Это позволяет избежать сложной нотации с запятыми при задании новых массивов. В следующем примере мы создаем двумерный массив из списка десяти элементов:

>>> a = np.array(range(10), float)
>>> a
array([ 0.,  1.,  2., 3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> a = a.reshape(5, 2)
>>> a
array([[ 0.,  1.],
[ 2.,  3.],
[ 4.,  5.],
[ 6.,  7.],
[ 8.,  9.]])
>>> a.shape
(5, 2)

Как видим, получился новый массив из пяти строк и двух столбцов. Если нам нужен массив из двух строк и пяти столбцов, переставляем аргументы в вызове reshape:

>>> a = np.array(range(10), float)
>>> a
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>> a = a.reshape(2, 5)
>>> a
array([[0., 1., 2., 3., 4.],
       [5., 6., 7., 8., 9.]]

Массив можно сконвертировать в привычный «питоновский» список:

>>> a = np.array([1, 2, 3], float)
>>> a.tolist()
[1.0, 2.0, 3.0]
>>> list(a)
[1.0, 2.0, 3.0]

Массивы также можно сериализовать (записать) в строку, а впоследствии десериализовать (прочитать) из строки. Это полезно при записи/чтении массива в/из файла. Для сериализации и десериализации используется пара методов tostring/fromstring:

>>> a = array([1, 2, 3], float)
>>> s = a.tostring()
>>> s
'\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@'
>>> np.fromstring(s)
array([ 1.,  2.,  3.

Для заполнения массива определенным значением используется метод fill:

>>> a = array([1, 2, 3], float)
>>> a
array([ 1.,  2.,  3.])
>>> a.fill(0)
>>> a
array([ 0.,  0.,  0.])

Для транспонирования двумерного массива (то есть матрицы) существует метод с предсказуемым именем transpose:

>>> a = np.array(range(6), float).reshape((2, 3))
>>> a
array([[ 0.,  1.,  2.],
[ 3.,  4.,  5.]])
>>> a.transpose()
array([[ 0.,  3.],
[ 1.,  4.],
[ 2.,  5.]])

Многомерный массив можно «раскатать» (сделать одномерным) методом flatten:

>>> a = np.array([[1, 2, 3], [4, 5, 6]], float)
>>> a
array([[ 1.,  2.,  3.],
[ 4.,  5.,  6.]])
>>> a.flatten()
array([ 1.,  2.,  3.,  4.,  5.,  6.])

Так же как для строк, для массивов определена операция склеивания (конкатенации), методом concatenate:

>>> a = np.array([1,2], float)
>>> b = np.array([3,4,5,6], float)
>>> c = np.array([7,8,9], float)
>>> np.concatenate((a, b, c))
array([1., 2., 3., 4., 5., 6., 7., 8., 9.]

Для многомерных массивов можно задать ось (измерение), вдоль которой будет осуществляться конкатенация. По умолчанию конкатенация происходит по первому измерению:

>>> a = np.array([[1, 2], [3, 4]], float)
>>> b = np.array([[5, 6], [7,8]], float)
>>> np.concatenate((a,b))
array([[ 1.,  2.],
[ 3.,  4.],
[ 5.,  6.],
[ 7.,  8.]])
>>> np.concatenate((a,b), axis=0)
array([[ 1.,  2.],
[ 3.,  4.],
[ 5.,  6.],
[ 7.,  8.]])
>>> 
np.concatenate((a,b), axis=1)
array([[ 1.,  2.,  5.,  6.],
[ 3.,  4.,  7.,  8.]

Если у нас есть массив и мы хотим на его основе получить массив с большей размерностью, мы используем псевдоконстанту newaxis:

>>> a = np.array([1, 2, 3], float)
>>> a
array([1., 2., 3.])
>>> a[:,np.newaxis]
array([[ 1.],
[ 2.],
[ 3.]])
>>> a[:,np.newaxis].shape
(3,1)
>>> b[np.newaxis,:]
array([[ 1.,  2.,  3.]])
>>> b[np.newaxis,:].shape
(1,3)

Дополнительные способы создания массивов

Функция arange аналогична функции range, только создает массив:

>>> np.arange(5, dtype=float)
array([ 0.,  1.,  2.,  3.,  4.])
>>> np.arange(1, 6, 2, dtype=int)
array([1, 3, 5])

Функции zeros и ones создают новые массивы, автоматически заполненные нулями и единицами соответственно.

>>> np.ones((2,3), dtype=float)
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
>>> np.zeros(7, dtype=int)
array([0, 0, 0, 0, 0, 0, 0

Для создания единичной матрицы, то есть матрицы, в которой все элементы — нули, за исключением элементов главной диагонали, которые равны 1, используется функция identity:

>>> np.identity(4, dtype=float)
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])

Функция eye создает матрицу с единицами на произвольной диагонали, номер которой задается вторым аргументом:

>>> np.eye(4, k=1, dtype=float)
array([[ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.],
       [ 0.,  0.,  0.,  0.]])

В данном примере единицами заполняется «первая» диагональ (k=1).

Математические операции над массивами

Как уже было упомянуто в сравнительной таблице MATLAB и NumPy, в последнем операции над массивами выполняются поэлементно.

Следовательно, массивы, участвующие в таких операциях должны иметь одинаковую размерность. Простейшие арифметические операции над массивами — сложение, вычитание, умножение, деление, взятие остатка, возведение в степень:

>>> a = np.array([1,2,3], float)
>>> b = np.array([5,2,6], float)
>>> a + b
array([6., 4., 9.])
>>> a - b
array([-4., 0., -3.])
>>> a * b
array([5., 4., 18.])
>>> b / a
array([5., 1., 2.])
>>> a % b
array([1., 0., 3.])
>>> b**a
array([5., 4., 216.])

Еще раз обратите внимание, что «*» — это именно поэлементное умножение, в том числе и двумерных массивов, то есть матриц.

Поскольку арифметические операции поэлементные, при несовпадении длины (не размерностей!) аргументов будет сгенерировано исключение:

>>> a = np.array([1,2,3], float)
>>> b = np.array([4,5], float)
>>> a + b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: operands could not be broadcast together with shapes (3,) (2,)

Однако если не совпадают размерности массивов, они будут преобразованы для операций. Массив с меньшей размерностью будет «клонирован», то есть повторен:

>>> a = np.array([[1, 2], [3, 4], [5, 6]], float)
>>> b = np.array([-1, 3], float)
>>> a
array([[ 1.,  2.],
       [ 3.,  4.],
       [ 5.,  6.]])
>>> b
array([-1.,  3.])
>>> a + b
array([[ 0.,  5.],
       [ 2.,  7.],
       [ 4.,  9.]])

Аналогично простейшим операциям, массивы могут быть аргументами математических функций, таких как sqrt, sin, cos, tan, log, log10 и других:

>>> a = np.array([1, 4, 9], float)
>>> np.sqrt(a)
array([ 1.,  2.,  3.])

Мы рассмотрели малую часть огромного количества функций для работы с массивами, поддерживаемыми NumPy. Большинство из них описаны в кратком руководстве https://sites.engineering.ucsb.edu/~shell/che210d/numpy.pdf; более полная документация доступна на сайте Массачусетского технологического института: http://web.mit.edu/dvp/Public/numpybook.pdf.

Заключение

В этой статье мы кратко рассмотрели NumPy — библиотеку Python для работы с массивами, ее историю, сравнение с MATLAB и основные функции.

Более подробно об этой библиотеке можно можно узнать из этого обзорного видео:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023