NumPy, часть 2: базовые операции над массивами

Здравствуйте! Я продолжаю работу над пособием по python-библиотеке NumPy.

В прошлой части мы научились создавать массивы и их печатать. Однако это не имеет смысла, если с ними ничего нельзя делать.

Сегодня мы познакомимся с операциями над массивами.

Базовые операции

Математические операции над массивами выполняются поэлементно. Создается новый массив, который заполняется результатами действия оператора.

>>> import numpy as np
>>> a = np.array([20, 30, 40, 50])
>>> b = np.arange(4)
>>> a + b
array([20, 31, 42, 53])
>>> a - b
array([20, 29, 38, 47])
>>> a * b
array([  0,  30,  80, 150])
>>> a / b  # При делении на 0 возвращается inf (бесконечность)
array([         inf,  30.        ,  20.        ,  16.66666667])
<string>:1: RuntimeWarning: divide by zero encountered in true_divide
>>> a ** b
array([     1,     30,   1600, 125000])
>>> a % b  # При взятии остатка от деления на 0 возвращается 0
<string>:1: RuntimeWarning: divide by zero encountered in remainder
array([0, 0, 0, 2])

Для этого, естественно, массивы должны быть одинаковых размеров.

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

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

>>> a + 1
array([21, 31, 41, 51])
>>> a ** 3
array([  8000,  27000,  64000, 125000])
>>> a < 35  # И фильтрацию можно проводить
array([ True,  True, False, False], dtype=bool)

NumPy также предоставляет множество математических операций для обработки массивов:

>>> np.cos(a)
array([ 0.40808206,  0.15425145, -0.66693806,  0.96496603])
>>> np.arctan(a)
array([ 1.52083793,  1.53747533,  1.54580153,  1.55079899])
>>> np.sinh(a)
array([  2.42582598e+08,   5.34323729e+12,   1.17692633e+17,
         2.59235276e+21])

Полный список можно посмотреть здесь.

Многие унарные операции, такие как, например, вычисление суммы всех элементов массива, представлены также и в виде методов класса ndarray.

>>> a = np.array([[1, 2, 3], [4, 5, 6]])
>>> np.sum(a)
21
>>> a.sum()
21
>>> a.min()
1
>>> a.max()
6

По умолчанию, эти операции применяются к массиву, как если бы он был списком чисел, независимо от его формы. Однако, указав параметр axis, можно применить операцию для указанной оси массива:

>>> a.min(axis=0)  # Наименьшее число в каждом столбце
array([1, 2, 3])
>>> a.min(axis=1)  # Наименьшее число в каждой строке
array([1, 4])

Индексы, срезы, итерации

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

>>> a = np.arange(10) ** 3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[1]
1
>>> a[3:7]
array([ 27,  64, 125, 216])
>>> a[3:7] = 8
>>> a
array([  0,   1,   8,   8,   8,   8,   8, 343, 512, 729])
>>> a[::-1]
array([729, 512, 343,   8,   8,   8,   8,   8,   1,   0])
>>> del a[4:6]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: cannot delete array elements
>>> for i in a:
...     print(i ** (1/3))
...
0.0
1.0
2.0
2.0
2.0
2.0
2.0
7.0
8.0
9.0

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

>>> b = np.array([[  0, 1, 2, 3],
...               [10, 11, 12, 13],
...               [20, 21, 22, 23],
...               [30, 31, 32, 33],
...               [40, 41, 42, 43]])
...
>>> b[2,3]  # Вторая строка, третий столбец
23
>>> b[(2,3)]
23
>>> b[2][3]  # Можно и так
23
>>> b[:,2]  # Третий столбец
array([ 2, 12, 22, 32, 42])
>>> b[:2]  # Первые две строки
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13]])
>>> b[1:3, : : ]  # Вторая и третья строки
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

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

>>> b[-1]  # Последняя строка. Эквивалентно b[-1,:]