Язык Python стал популярен
благодаря своей богатой библиотеке. Я его воспринимаю как некоего начальника,
который через свои команды раздает задачи подчиненным. Они выполняются и на
выходе получается некий результат работы программы:
Из всех известных
мне языков высокого уровня, этот, наверное, самый высокий, а потому, один из
самых удобных в реализации алгоритмов. Но чтобы им успешно пользоваться нужно
уметь раздавать приказы подчиненным. И в этой серии занятий мы познакомимся с
еще одним исполнителем языка Python – пакетом NumPy.
Вообще, NumPy предназначен
для выполнения научных вычислений и активно используется не только в качестве
самостоятельной библиотеки учеными и преподавателями по всему миру, но и входит
в состав многих других популярных пакетов. С одним из них – Keras, мы с вами
недавно познакомились, когда изучали основы работы НС. И вы могли заметить, что
вначале программ фигурировала строчка:
Это, как раз,
выполнение импорта того самого пакета NumPy.
Но почему он
стал так популярен? Причин несколько. Самое главное, критичные по скорости
вычисления фрагменты реализованы на языках Си и Фортран. Также он имеет
довольно простой и продуманный синтаксис, а, значит, им легко пользоваться. Ну
и, наконец, богатство возможностей этой библиотеки, начиная с базовых
математических функций и заканчивая работой с полиномами, линейной алгеброй и
многомерными матрицами (тензорами). Все это очень часто используется в
инженерных задачах, отсюда и высокая популярность пакета.
Установка NumPy
Я думаю, вы
прониклись уважением к этому пакету, и пришла пора прикоснуться к «святому
граалю». В начале, как всегда, его нужно установить. Сделать это чрезвычайно
просто, достаточно выполнить в терминале команду:
pip install numpy
Не удивляйтесь,
если этот пакет у вас уже установлен, так как он входит в состав многих других
библиотек. Проверить установку можно командой:
Если такая
программа выполняется без ошибок, то этот «святой грааль» уже присутствует на
вашем устройстве и готов к истязаниям.
У вас здесь уже
может возникнуть вопрос: почему импорт записан в таком виде? А не просто: import
numpy? Можно и так, но тогда в программе все время придется использовать
префикс numpy. Гораздо удобнее писать две буквы «np». Поэтому
общепринятой практикой стало импортирование этого пакета именно в таком виде. Я
буду следовать сложившейся традиции и делать также.
Фундаментальный элемент NumPy – массив (array)
Отлично,
сложнейший этап установки и импорта пакета позади. Пришло время сделать первые
шаги и вначале познакомиться с его фундаментальным элементом – однородным
многомерным массивом. В NumPy элементы массива имеют единый тип
данных. Их индексы описываются кортежем целых неотрицательных чисел.
Размерность кортежа – это ранг массива (то есть, размерность массива), а каждое
число в кортеже представляет свою отдельную ось:
Как создать
массив в NumPy? Существует много
способов, но базовый реализуется через функцию:
numpy.array(object,
dtype=None, …)
Здесь в качестве
первого параметра object может выступать список или кортеж, а также функция или объект, возвращающий список или
кортеж. Второй параметр dtype – это тип элементов массива. Если
указано значение None, то тип будет определяться автоматически на основе
переданных данных. Подробнее об этой функции можно, как всегда, почитать на
странице официальной документации:
https://numpy.org/doc/stable/reference/arrays.ndarray.html
Итак, в самом
простом варианте можно создать одномерный массив так:
a = np.array([1, 2, 3, 4])
В результате
получим объект типа array с элементами 1, 2, 3, 4:
array([1, 2, 3,
4])
Какой будет тип
у этих элементов? Мы можем его посмотреть с помощью атрибута dtype, выполнив в
консоли строчку:
Увидим:
dtype('int32')
То есть,
автоматически был применен целочисленный тип размерностью 32 бит. Ну, хорошо, а
что если попробовать создать массив с разными типами его элементов, например,
так:
a = np.array([1, 2, "3", True])
В результате
увидим, следующее содержимое:
array(['1',
'2', '3', 'True'], dtype='<U11')
Все элементы
стали строкового типа. Этот пример показывает, что в массивах NumPy используется
единый тип данных его элементов: или все целочисленные, или строковые, или
вещественные и так далее. Смешение типов в рамках одного массива не
допускается.
Отлично, это мы
сделали. Как теперь можно обращаться к отдельным элементам массива? Для этого
используется общий синтаксис:
<имя массива>[<кортеж
индексов>]
Например, для
нашего одномерного случая, мы можем взять первый элемент из массива a, следующим
образом:
Увидим значение ‘1’.
Обратите внимание, первый элемент имеет индекс 0, а не 1. Единица – это уже
второй элемент:
a[1] # возвращает 2-й элемент со значением ‘2’
Для изменения
значения элемента, достаточно присвоить ему новое значение, например:
в результате
получим массив:
array(['1',
'123', '3', 'True'], dtype='<U11')
А что будет,
если мы попробуем присвоить значение другого типа данных, например, число:
Ошибки не будет,
а значение автоматически будет преобразовано в строку:
array(['1',
'234', '3', 'True'], dtype='<U11')
Разработчики
пакета NumPy постарались
сделать его максимально дружественным, чтобы инженер сосредотачивался именно на
решении задачи, а не на нюансах программирования. Поэтому везде, там где это
допустимо, пакет NumPy берет на себя разрешение подобных
нюансов. И, как показала практика, это очень удобно и заметно облегчает жизнь
нам, простым смертным.
Минутка восхищения или что такого в массивах NumPy
Но, все-таки,
что такого в массивах NumPy, что они повсеместно используются в
разных библиотеках? Давайте я приведу несколько примеров, и вы сами все
увидите.
Предположим, мы
определили одномерный массив с числами от 1 до 9:
a = np.array([1,2,3,4,5,6,7,8,9])
Мы уже знаем как
взять один отдельный элемент, но что будет, если прописать индексы для всех 9
элементов:
На выходе увидим
одномерный массив из двоек:
array([2, 2, 2,
2, 2, 2, 2, 2, 2])
Или, так:
тогда получим
аналогичный массив, но размерностью 5 элементов:
array([2, 2, 2,
2, 2])
Как видите,
индексирование здесь более гибкое, чем у обычных списков Python. Или, вот еще один характерный пример:
a[ [True, True, False, False, False, False, True, True, True] ]
Результат будет
следующим:
array([1, 2, 7,
8, 9])
То есть,
остаются элементы со значениями True и отбрасываются со значениями False. Обо всем этом
мы еще будем подробно говорить.
Еще один пример.
Предположим, нам понадобилось представить одномерный массив a в виде матрицы
3х3. Нет ничего проще, меняем его размерность:
и получаем
заветный результат:
array([[1,
2, 3],
[4, 5, 6],
[7, 8,
9]])
Далее, можем
обращаться к элементам матрицы b так:
или так:
В обоих случаях
будет взят один и тот же элемент со значением 6.
Все это лишь
мимолетный взгляд на возможности пакета NumPy. Я здесь лишь
хотел показать, насколько сильно отличаются массивы array от списков
языка Python, и если вы
хотите овладеть этим инструментом, то эта серия занятий для вас.