Предисловие переводчика
И снова здравствуйте! Продолжаем наш цикл статей по переводу мана о numpy. Приятного чтения.
Операторы сравнения и тестирование значений
Булево сравнение может быть использовано для поэлементного сравнения массивов одинаковых длин. Возвращаемое значение это массив булевых True/False значений:
>>> a = np.array([1, 3, 0], float)
>>> b = np.array([0, 3, 2], float)
>>> a > b
array([ True, False, False], dtype=bool)
>>> a == b
array([False, True, False], dtype=bool)
>>> a <= b
array([False, True, True], dtype=bool)Результат сравнения может быть сохранен в массиве:
>>> c = a > b
>>> c
array([ True, False, False], dtype=bool)Массивы могут быть сравнены с одиночным значением:
>>> a = np.array([1, 3, 0], float)
>>> a > 2
array([False, True, False], dtype=bool)Операторы any и all могут быть использованы для определения истинны ли хотя бы один или все элементы соответственно:
>>> c = np.array([ True, False, False], bool)
>>> any(c)
True
>>> all(c)
FalseКомбинированные булевы выражения могут быть применены к массивам по принципу элемент — элемент используя специальные функции logical_and, logical_or и logical_not:
>>> a = np.array([1, 3, 0], float)
>>> np.logical_and(a > 0, a < 3)
array([ True, False, False], dtype=bool)
>>> b = np.array([True, False, True], bool)
>>> np.logical_not(b)
array([False, True, False], dtype=bool)
>>> c = np.array([False, True, False], bool)
>>> np.logical_or(b, c)
array([ True, True, True], dtype=bool)Функция where создает новый массив из двух других массивов одинаковых длин используя булев фильтр для выбора межу двумя элементами. Базовый синтаксис: where(boolarray,
truearray, falsearray):
>>> a = np.array([1, 3, 0], float)
>>> np.where(a != 0, 1 / a, a)
array([ 1. , 0.33333333, 0. ])С функцией where так же может быть реализовано «массовое сравнение»:
>>> np.where(a > 0, 3, 2)
array([3, 3, 2])Некоторые функции дают возможность тестировать значения в массиве. Функция nonzero возвращает кортеж индексов ненулевых значений. Количество элементов в кортеже равно количеству осей в массиве:
>>> a = np.array([[0, 1], [3, 0]], float)
>>> a.nonzero()
(array([0, 1]), array([1, 0]))Также можно проверить значения на конечность и NaN(not a number):
>>> a = np.array([1, np.NaN, np.Inf], float)
>>> a
array([ 1., NaN, Inf])
>>> np.isnan(a)
array([False, True, False], dtype=bool)
>>> np.isfinite(a)
array([ True, False, False], dtype=bool)Хотя здесь мы использовали константы numpy чтобы добавить значения NaN и бесконечность, они могут быть результатами применения стандартных математических операций.
Выбор элементов массива и манипуляция с ними
Мы уже видели, как и у списков, элементы массива можно получить используя операцию доступа по индексу. Однако, в отличии от списков, массивы также позволяют делать выбор элементов используя другие массивы. Это значит, что мы можем использовать массив для фильтрации специфических подмножеств элементов других массивов.
Булевы массивы могут быть и��пользованы как массивы для фильтрации:
>>> a = np.array([[6, 4], [5, 9]], float)
>>> a >= 6
array([[ True, False],
[False, True]], dtype=bool)
>>> a[a >= 6]
array([ 6., 9.])Стоит заметить, что когда мы передаем булев массив a>=6 как индекс для операции доступа по индексу массива a, возвращаемый массив будет хранить только True значения. Также мы можем записать массив для фильтрации в переменную:
>>> a = np.array([[6, 4], [5, 9]], float)
>>> sel = (a >= 6)
>>> a[sel]
array([ 6., 9.])Более замысловатая фильтрация может быть достигнута использованием булевых выражений:
>>> a[np.logical_and(a > 5, a < 9)]
>>> array([ 6.])В придачу к булеву выбору, также можно использовать целочисленные массивы. В этом случае, целочисленный массив хранит индексы элементов, которые будут взяты из массива. Рассмотрим следующий одномерный пример:
>>> a = np.array([2, 4, 6, 8], float)
>>> b = np.array([0, 0, 1, 3, 2, 1], int)
>>> a[b]
array([ 2., 2., 4., 8., 6., 4.])Иными словами, когда мы используем b для получения элементов из a, мы берем 0-й, 0-й, 1-й, 3-й, 2-й и 1-й элементы a в этом порядке. Списки также могут быть использованы как массивы для фильтрации:
>>> a = np.array([2, 4, 6, 8], float)
>>> a[[0, 0, 1, 3, 2, 1]]
array([ 2., 2., 4., 8., 6., 4.])Для многомерных массивов, нам необходимо передать несколько одномерных целочисленных массивов в оператор доступа индексу (Прим. переводчика: в нашем случае индексы это массивы) для каждой оси. Потом каждый из массивов проходит такую последовательность: первый элемент соответствует индексу строки, который является первым элементом массива b, второй элемент соответствует индексу столбца, который является первым элементом массива c и так далее. (Прим. переводчика: первый массив [2, 2] и второй [1, 4], имеем на выходе элементы с индексами [2, 1] и [2, 4]) Пример:
>>> a = np.array([[1, 4], [9, 16]], float)
>>> b = np.array([0, 0, 1, 1, 0], int)
>>> c = np.array([0, 1, 1, 1, 1], int)
>>> a[b,c]
array([ 1., 4., 16., 16., 4.])Специальная функция take доступна для выполнения выборки с целочисленными массивами. Это работает также как и использования оператора взятия по индексу:
>>> a = np.array([2, 4, 6, 8], float)
>>> b = np.array([0, 0, 1, 3, 2, 1], int)
>>> a.take(b)
array([ 2., 2., 4., 8., 6., 4.])Функция take также предоставляет аргумент axis (ось) для взятия подсекции многомерного массива вдоль какой-либо оси. (Прим. переводчика: по строкам или столбцам (для двумерных массивов)).
>>> a = np.array([[0, 1], [2, 3]], float)
>>> b = np.array([0, 0, 1], int)
>>> a.take(b, axis=0)
array([[ 0., 1.],
[ 0., 1.],
[ 2., 3.]])
>>> a.take(b, axis=1)
array([[ 0., 0., 1.],
[ 2., 2., 3.]])В противоположность к функции take есть функция put, которая будет брать значения из исходного массива и записывать их на специфические индексы в другом put-массиве.
>>> a = np.array([0, 1, 2, 3, 4, 5], float)
>>> b = np.array([9, 8, 7], float)
>>> a.put([0, 3], b)
>>> a
array([ 9., 1., 2., 8., 4., 5.])Заметим, что значение 7 из исходного массива b не было использовано, так как только 2 индекса [0, 3] указаны. Исходный массив будет повторен если необходимо в случае не соответствия длин:
>>> a = np.array([0, 1, 2, 3, 4, 5], float)
>>> a.put([0, 3], 5)
>>> a
array([ 5., 1., 2., 5., 4., 5.])Векторная и матричная математика
NumPy обеспечивает много функций для работы с векторами и матрицами. Функция dot возвращает скалярное произведение векторов:
>>> a = np.array([1, 2, 3], float)
>>> b = np.array([0, 1, 1], float)
>>> np.dot(a, b)
5.0Функция dot также может умножать матрицы:
>>> a = np.array([[0, 1], [2, 3]], float)
>>> b = np.array([2, 3], float)
>>> c = np.array([[1, 1], [4, 0]], float)
>>> a
array([[ 0., 1.],
[ 2., 3.]])
>>> np.dot(b, a)
array([ 6., 11.])
>>> np.dot(a, b)
array([ 3., 13.])
>>> np.dot(a, c)
array([[ 4., 0.],
[ 14., 2.]])
>>> np.dot(c, a)
array([[ 2., 4.],
[ 0., 4.]])Также можно получить скалярное, тензорное и внешнее произведение матриц и векторов. Заметим, что для векторов внутреннее и скалярное произведение совпадает.
>>> a = np.array([1, 4, 0], float)
>>> b = np.array([2, 2, 1], float)
>>> np.outer(a, b)
array([[ 2., 2., 1.],
[ 8., 8., 4.],
[ 0., 0., 0.]])
>>> np.inner(a, b)
10.0
>>> np.cross(a, b)
array([ 4., -1., -6.])NumPy также предоставляет набор встроенных функций и методов для работы с линейной алгеброй. Это всё можно найти в под-модуле linalg. Этими модулями также можно оперировать с вырожденными и невырожденными матрицами. Определитель матрицы ищется таким образом:
>>> a = np.array([[4, 2, 0], [9, 3, 7], [1, 2, 1]], float)
>>> a
array([[ 4., 2., 0.],
[ 9., 3., 7.],
[ 1., 2., 1.]])
>>> np.linalg.det(a)
-48.Также можно найти собственный вектор и собственное значение матрицы:
>>> vals, vecs = np.linalg.eig(a)
>>> vals
array([ 9. , 2.44948974, -2.44948974])
>>> vecs
array([[-0.3538921 , -0.56786837, 0.27843404],
[-0.88473024, 0.44024287, -0.89787873],
[-0.30333608, 0.69549388, 0.34101066]])Невырожденная матрица может быть найдена так:
>>> b = np.linalg.inv(a)
>>> b
array([[ 0.14814815, 0.07407407, -0.25925926],
[ 0.2037037 , -0.14814815, 0.51851852],
[-0.27777778, 0.11111111, 0.11111111]])
>>> np.dot(a, b)
array([[ 1.00000000e+00, 5.55111512e-17, 2.22044605e-16],
[ 0.00000000e+00, 1.00000000e+00, 5.55111512e-16],
[ 1.11022302e-16, 0.00000000e+00, 1.00000000e+00]])Одиночное разложение (аналог диагонализации не квадратной матрицы) может быть достигнут так:
>>> a = np.array([[1, 3,4], [5, 2, 3]], float)
>>> U, s, Vh = np.linalg.svd(a)
>>> U
array([[-0.6113829 , -0.79133492],
[-0.79133492, 0.6113829 ]])
>>> s
array([ 7.46791327, 2.86884495])
>>> Vh
array([[-0.61169129, -0.45753324, -0.64536587],
[ 0.78971838, -0.40129005, -0.46401635],
[-0.046676 , -0.79349205, 0.60678804]])Заканчиваем третью часть. Удачи и до скорого!
