Иллюстрированное руководство по изменению формы массивов NumPy
Рассказываем, как перекраивать массивы NumPy в трех измерениях. Шпаргалка в конце руководства обобщает приемы работы с методами reshape, stack и ravel.
Трудность NumPy в том, что операции над многомерными массивами бывает сложно представить. Хаус Лин подготовил шпаргалки, которые сделают преобразование массивов нагляднее. Руководство подойдёт и для знакомства с NumPy.
1. Создаем массив NumPy
Аналог range для массивов. Чтобы создать простой массив, используем функцию np.arange()
. Удобно взять небольшое составное число, например, 12.
import numpy as np a1 = np.arange(1, 13) # числа от 1 до 12 print(a1.shape) > (12,) print(a1) > [ 1 2 3 4 5 6 7 8 9 10 11 12]
2. Изменяем форму с помощью метода reshape()
Оси и измерения. Чтобы изменить форму массива a1
, используем метод reshape()
. Преобразуем одномерный массив из 12 чисел в двумерную таблицу размером 3×4. Первое число – количество строк, второе – столбцов. Строки соответствуют оси (англ. axis) 0, столбцы – оси 1. Ещё их называют измерениями (англ. dimensions).
a1_2d = a1.reshape(3, 4) print(a1_2d.shape) > (3, 4) print(a1_2d) > [[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]]
Автоматическое вычисление размерности. Если нужно, чтобы NumPy сам определил размер незаданного измерения, передайте на этой позиции значение -1
:
a1.reshape(3, 4) a1.reshape(-1, 4) # то же самое, что a1.reshape(3, 4) a1.reshape(3, 4) a1.reshape(3, -1) # то же самое, что a1.reshape(3, 4) a1.reshape(2, 6) a1.reshape(2, -1) # то же самое, что a1.reshape(2, 6)
3. Изменяем форму по разным направлениям
Порядок переноса С. По умолчанию reshape()
перестраивает исходный массив по оси 0. То есть в нашем примере отдельные числа из одномерного массива «переносятся» в двумерный построчно. Поведение можно изменить, передав значение параметру order
. Дефолтный метод похож на то, как преобразование реализовано в C. Параметр order
по умолчанию имеет значение 'C'
:
a1.reshape(3, 4) a1.reshape(3, 4, order='C') # даст идентичный результат
Порядок переноса Fortran. Если вы привыкли к тому, как преобразуются массивы в MATLAB, то это делается так же, как в Fortran. Поэтому используйте значение 'F'
:
a1.reshape(3, 4, order='F') # столбец за столбцом > [[ 1 4 7 10] [ 2 5 8 11] [ 3 6 9 12]]
Какую размерность имеет исходный массив a1
? Может показаться, что массив a1 имеет размерность (1, 12)
. Но это одномерный массив с размерностью(12, )
. Чтобы преобразовать одномерный массив к двумерному, используем метод reshape()
:
print(a1) # какая форма? > [ 1 2 3 4 5 6 7 8 9 10 11 12] print(a1.shape) > (12,) a1_1_by_12 = a1.reshape(1, -1) # преобразуем к 1x12 print(a1_1_by_12) # обратите внимание на двойные скобки > [[ 1 2 3 4 5 6 7 8 9 10 11 12]] print(a1_1_by_12.shape) # двумерный массив > (1, 12)
4. Схлопываем массив до одномерного
Из 2D в 1D. Метод ravel()
преобразует многомерные массивы в одномерные. Тот же параметр order
определяет, «схлопнется» ли массив построчно или столбец за столбцом:
print(a1_2d) > [[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] print(a1_2d.ravel()) # строка за строкой > [ 1 2 3 4 5 6 7 8 9 10 11 12] print(a1_2d.ravel(order='F')) # столбец за столбцом > [ 1 5 9 2 6 10 3 7 11 4 8 12]
5. Повышаем размерность, сшивая массивы друг с другом
Создадим два массива:
a1 = np.arange(1, 13) print(a1) > [ 1 2 3 4 5 6 7 8 9 10 11 12] a2 = np.arange(13, 25) print(a2) > [13 14 15 16 17 18 19 20 21 22 23 24]
Чтобы соединить два одномерных масива в двумерный, используем метод np.stack()
. Параметр axis
указывает индекс оси в результирующем массиве, по которой соединяются входные. По умолчанию axis = 0
, и массивы стыкуются строка к строке:
stack0 = np.stack((a1, a1, a2, a2)) print(stack0.shape) > (4, 12) print(stack0) > [[ 1 2 3 4 5 6 7 8 9 10 11 12] [ 1 2 3 4 5 6 7 8 9 10 11 12] [13 14 15 16 17 18 19 20 21 22 23 24] [13 14 15 16 17 18 19 20 21 22 23 24]]
Чтобы соединить массивы столбец к столбцу, явным образом передаем axis = 1
:
stack1 = np.stack((a1, a1, a2, a2), axis=1) print(stack1.shape) > (12, 4) print(stack1) > [[ 1 1 13 13] [ 2 2 14 14] [ 3 3 15 15] [ 4 4 16 16] [ 5 5 17 17] [ 6 6 18 18] [ 7 7 19 19] [ 8 8 20 20] [ 9 9 21 21] [10 10 22 22] [11 11 23 23] [12 12 24 24]]
Как не повышать число измерений? Функция hstack()
соединяет массивы горизонтально без изменения размерности. Если применить ее к одномерным массивам, получится длинный одномерный массив:
stack_long = np.hstack((a1, a2)) print(stack_long.shape) > (24,) print(stack_long) > [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]
6. Создаем трехмерный массив
Где применяются многомерные массивы? Массивы, живущие в трёх и более измерениях, активно используются в глубоком обучении. Например, для операций с изображениями. В работе с нейросетями преобразование многомерных массивов – обычное дело.
Для начала создадим два двумерных массива размером 3×4:
a1 = np.arange(1, 13).reshape(3, -1) a2 = np.arange(13, 25).reshape(3, -1) print(a1) > [[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] print(a2) > [[13 14 15 16] [17 18 19 20] [21 22 23 24]]
Теперь соберём из них трёхмерный массив:
a3_0 = np.stack((a1, a2)) # дефолтное axis=0, одна плоскость ложится на другую a3_1 = np.stack((a1, a2), axis=1) a3_2 = np.stack((a1, a2), axis=2) print(a3_0.shape) > (2, 3, 4) print(a3_1.shape) > (3, 2, 4) print(a3_2.shape) > (3, 4, 2)
Как выглядят массивы:
print(a3_0) > [[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] [[13 14 15 16] [17 18 19 20] [21 22 23 24]]] print(a3_1) > [[[ 1 2 3 4] [13 14 15 16]] [[ 5 6 7 8] [17 18 19 20]] [[ 9 10 11 12] [21 22 23 24]]] print(a3_2) > [[[ 1 13] [ 2 14] [ 3 15] [ 4 16]] [[ 5 17] [ 6 18] [ 7 19] [ 8 20]] [[ 9 21] [10 22] [11 23] [12 24]]]
Чтобы извлечь плоскость массива a1
из трехмерного, нужно передать 0
по соответствующему индексу оси:
print(a1) > [[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] a3_0[0, :, :] a3_1[:, 0, :] a3_2[:, :, 0]
Чтобы то же самое сделать для двумерного массива a2
, на тех же позициях вместо 0
отправляем индекс 1
. Символы двоеточия указывают, что нужно взять все элементы соответствующей оси.
7. Схлопываем многомерные массивы
Метод ravel()
действует и для массивов с числом размерностей больше 2:
print(a3_0) > [[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] [[13 14 15 16] [17 18 19 20] [21 22 23 24]]] print(a3_0.ravel()) > [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] print(a3_0.ravel(order='F')) > [ 1 13 5 17 9 21 2 14 6 18 10 22 3 15 7 19 11 23 4 16 8 20 12 24]
8. Изменяем форму многомерных массивов
Метод reshape()
также работает с массивами любой размерности. Главное условие, которое нужно соблюдать – количество элементов в массиве не должно меняться:
print(a3_0) # исходный размер 2x3x4 > [[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] [[13 14 15 16] [17 18 19 20] [21 22 23 24]]] print(a3_0.reshape(4, -1)) # преобразуем к 4x6 > [[ 1 2 3 4 5 6] [ 7 8 9 10 11 12] [13 14 15 16 17 18] [19 20 21 22 23 24]] print(a3_0.reshape(4, -1, order='F')) # делаем то же самое, но в F-стиле > [[ 1 9 6 3 11 8] [13 21 18 15 23 20] [ 5 2 10 7 4 12] [17 14 22 19 16 24]] print(a3_0.reshape(4, 2, 3)) # преобразуем к трехмерному массиву другой формы > [[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]] [[13 14 15] [16 17 18]] [[19 20 21] [22 23 24]]]
Заключение
Напоследок приведем изображение со всеми элементами рассказа вместе (оригинальный pdf). Напишите нам в комментариях, если что-то осталось непонятным и требует пояснений.