Библиотека NumPy для Python — это основа науки о данных и биоинформатики. При этом, хоть каждому программисту Python и знакомо имя пакета для установки:

pip install numpy

и команда импорта библиотеки:

import numpy as np

в практических задачах мало кто использует её в явном виде. Это происходит потому, что библиотеки для прикладного анализа данных, такие как pandas, Matplotlib, scikit-learn, опираются на NumPy и вызывают её "под капотом". В реальных задачах чаще используются библиотеки самого высокого уровня, но знать NumPy на теоретическом уровне очень важно.

Что умеет NumPy?

Ключевая функция NumPy — оперировать с 1-мерными и 2-мерными массивами чисел (которые в линейной алгебре известны под именем векторов и матриц, соответственно) без написания цикла пользователем. Сами циклы запускаются, но выполняются скрыто, а код операций пишется так же, как в тетради по линейной алгебре.

Например, если array1 — это двухмерная матрица, то операция

array1 + 3

сложит каждый элемент матрицы со скаляром.

Если vector1 и vector2 — векторы одинаковой размерности, то операция

vector1*vector2

выполнит их адамарово (поэлементное) умножение.

В NumPy есть и другие полезные функции, которые облегчают вычисления. Например, если нужно извлечь квадратные корни из целого массива чисел (например, array1), то функция

np.sqrt(array1)

извлечёт корень из каждого элемента массива и вернёт корни таким же списком. Такие функции называются универсальными функциями.

Подробная документация по NumPy представлена на сайте numpy.org, где в окне браузера можно поработать с NumPy в интерактивном режиме. Если вы только начинаете изучать Python, "поиграйтесь" с ней как с линейно-алгебраическим калькулятором.

Кстати, для изучения прикладной линейной алгебры в том ключе, в котором она реализована в NumPy, подойдет книга:

Коэн М.И. Прикладная линейная алгебра для исследователей данных / пер. с англ. А. В. Логунова. — М.: ДМК-Пресс, 2023. — 328 с.: ил.

Индексирование и транспонирование

Существуют особо частотные операции библиотеки NumPy, и к ним относятся индексирование и транспонирование. При работе с большими данными их часто придётся вызывать в явном виде.

Индексированием называется выбор элемента в массиве NumPy по его индексу — положению или “номеру” в матрице.

Если my_array — это ваша матрица, то команда вида

my_array[i, j]

вернёт элемент, находящийся на пересечении строки i и столбца j.

Но не всё так просто!

Обратите внимание на левую часть Рис.1. Пусть my_array — это матрица 4×3, обведённая прямоугольной рамкой. Что будет, если задать элемент my_array[1, 2]? Интуитивно кажется, что программа вернёт 4 — ведь четвёрка в первой строке и втором столбце. Но Python возвращает 47, потому что он считает с нуля. Первый элемент в любом массиве для него — всегда нулевой. Такое языки называются нуль-индексными.

Рис. 1. Индексирование и транспонирование в NumPy
Рис. 1. Индексирование и транспонирование в NumPy

Это делает обращение к строкам и столбцам таблицы по номерам резко неудобным. Но, к счастью, есть хорошее решение — библиотека pandas с её фреймами данных, которая работает “поверх” NumPy. О ней мы уже частично рассказывали в предыдущей статье.

Помимо индексирования, часто бывает нужно уложить двумерный массив набок, поменяв местами строки и столбцы. Для этого в NumPy есть целых два решения!

Прежде всего, у каждого массива NumPy как объекта (Python — объектно-ориентированный язык, здесь почти всё — объект) есть метод из одной буквы — T. Он как раз и осуществляет транспонирование:

my_array.T

Но есть и более привычный вариант — вызов функции transpose из библиотеки NumPy с передачей ей нашей матрицы в качестве аргумента. Например, если вы импортировали NumPy как np (это её стандартное имя при импорте), то транспонирование осуществляется так:

np.transpose(my_array)

Сама по себе операция транспонирования встречается в биоинформатических задачах часто, почти на уровне рутины — некоторые библиотеки и методы лучше работают с данными в строках, некоторые — со столбцами. Знание команд для транспонирования позволяет проводить его “одним махом”, не отвлекаясь на преобразование данных и на написание нового кода.

Перемножение векторов и матриц

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

В то же время многие базовые понятия и методы машинного обучения требуют понимания этих операций. Без точечного умножения векторов, например, нельзя реализовать математическую операцию свёртки — и свёрточные нейросети, используемые в компьютерном зрении и даже в первой версии AlphaFold. А простейшая линейная регрессия требует матричного умножения: для двух переменных можно записать всё без него, но для множественной линейной регрессии без матриц уже не обойтись.

Конечно, обычно операции с векторами и матрицами выполняются “под капотом” библиотек более высокого уровня типа pandas и scikit-learn. Но знание способа их реализации в NumPy тоже полезно. Умение реализовать функции для машинного обучения “с нуля” пригодится для разработки своего математического аппарата (в биоинформатике это бывает нужно часто) или реализации кода на другом языке. Например, на R.

Точечное (скалярное) произведение векторов — это произведение векторов, выраженное одним числом, показывающим связь между ними. Чтобы найти его, нужно поэлементно перемножить оба вектора и найти сумму элементов получившегося вектора. Формула этого действия приведена на Рис. 2 слева вверху.

Пример: [1 11 6 4] • [22 3 5 4] = 22 + 33 + 30 + 16 = 101

Рис. 2. Точечное (скалярное) произведение векторов и стандартное произведение матриц
Рис. 2. Точечное (скалярное) произведение векторов и стандартное произведение матриц

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

При стандартном умножении матриц коммутативный закон не выполняется — то есть порядок множителей имеет значение, и перемена множителей местами меняет произведение. При этом умножить друг на друга можно не любые матрицы: число столбцов в первом множителе должно быть равно число строк во втором множителе. Произведение будет представлять собой матрицу, в которой строк будет столько же, сколько в первом множителе, а столбцов — столько же, сколько во втором. При этом каждый (i,j)-й элемент матрицы-произведения будет представлять собой точечное произведение i-й строки первого множителя и j-го столбца второго множителя. Формула приведена на Рис. 2 слева внизу, а визуализация этого действия — на Рис. 2 справа.

Самое интересное, что в NumPy обе операции — точечное умножение векторов и стандартное умножение матриц — обеспечиваются одной функцией np.dot(a,b). Если a и b — векторы, то NumPy вернёт их точечное произведение, если матрицы — то их стандартное произведение.

import numpy as np
c = np.dot(a,b)

Это имеет математический смысл: при точечном умножении векторов NumPy просто рассматривает первый вектор как единственную строку матрицы, а второй — как единственный столбец другой матрицы. И получается “матрица” из одного элемента — то есть просто скаляр, как и должно быть при точечном умножении векторов. Такой вот частный случай.

Стандартное произведение матриц позволяет “закодировать” в одной матрице все связи между всеми элементами двух матриц — множителей. Именно поэтому такая операция незаменима в статистике и машинном обучении. Например, частные случаи умножения двух матриц приводят к широко используемым в статистике ковариационным матрицам и матрицам перестановок, к которым мы ещё вернёмся в следующих постах.

А если вы дочитали этот пост до конца и ещё что-то при этом поняли — то шансы освоить машинное обучение у вас уже неплохие. Оставайтесь с нами!

Больше образовательных материалов — в том числе в коротком формате — можно найти в нашем телеграм-канале eduopenbio.

Комментарии (0)