В этой статье мы рассмотрим математическую библиотеку NumPy (и попутно обсудим MATLAB, массивы и Python), предназначенную для эффективной работы с массивами в Python.
Содержание:
1. Общие сведения
2. NumPy и MATLAB
3. Установка NumPy
4. Массивы в NumPy и операции над ними
Заключение
Любой зрелый язык программирования со временем обзаводится библиотекой для математических вычислений. В C# это Math.NET Numerics, в Ruby — SciRuby, Fortran (кстати, что это?) сам по себе представляет огромную математическую библиотеку или, если угодно, целый город, состоящий из библиотек.
Если быть более точным, NumPy — это высокоэффективная библиотека для обработки больших (огромных) массивов и матриц, включающая математические функции для обработки этих типов данных.
Проект начался в 2005 году как улучшение существующей библиотеки Numeric, достаточно популярной, но имеющей проблемы с производительностью и поддерживаемостью.
Код Numeric (и его последователя NumArray) был полностью переписан и оптимизирован. Сейчас NumPy — общепризнанный проект с открытым кодом и обладает огромным сообществом. SciPy, библиотека Python для математических вычислений, включает некоторые компоненты NumPy, в частности, свой основной тип данных — массив.
Если сравнивать NumPy с другим ПО для математических вычислений, например MATLAB, то в своей области — линейная алгебра, операции над векторами (массивами) и матрицами — «герой» нашей статьи покрывает абсолютное большинство нужд, обеспечиваемых последним, а в некоторых областях даже превосходит его.
Ключевые различия NumPy и MATLAB
MATLAB | NumPy |
В MATLAB базовый тип даже для скалярных (в частности, числовых) данных — многомерный массив. Массивы в MATLAB хранятся как двумерные массивы из чисел с плавающей точкой с двойной точностью, кроме случаев, когда вы сами указываете количество измерений и тип. Операции над двумерными экземплярами этих массивов основываются на матричных операциях линейной алгебры. | В NumPy, базовый тип — это многомерный массив. Переменные типа «массив» в NumPy обычно хранятся в n-мерных массивах с «минимальным» (наименее затратном в смысле занимаемой памяти) типом, необходимым для хранения объектов в последовательности, кроме случаев, когда вы задаете количество измерений и тип. NumPy производит поэлементные операции, поэтому перемножение двумерных массивов операцией « |
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 ) массивов, а не копии массивов. |
Если у вас установлен IPython (интерактивный Python), можно использовать его. В принципе, подойдет любая оболочка Python, позволяющая запускать интерактивный режим (IDE
).
Если вы новичок в мире Python, самый простой способ — скачать PyCharm от JetBrains (есть бесплатная Community Edition), после чего добавить пакет (Package
) NumPy: View -> Tool Windows -> Python Packages:
После чего нажать Install в окне пакетов (справа).
Теперь вызываем интерактивную панель (Tools -> Python or Debug Console) и начинаем работу.
Центральный объект в 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 и основные функции.
Более подробно об этой библиотеке можно можно узнать из этого обзорного видео:
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…