Иллюстрированное руководство по изменению формы многомерных массивов 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] 
Иллюстрированное руководство по изменению формы многомерных массивов NumPy
Создан одномерный массив из 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

Автоматическое вычисление размерности. Если нужно, чтобы 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]] 
Иллюстрированное руководство по изменению формы многомерных массивов NumPy

Какую размерность имеет исходный массив 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. Повышаем размерность, сшивая массивы друг с другом

Иллюстрированное руководство по изменению формы многомерных массивов NumPy

Создадим два массива:

 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]] 
Иллюстрированное руководство по изменению формы многомерных массивов NumPy

Теперь соберём из них трёхмерный массив:

 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) 
Иллюстрированное руководство по изменению формы многомерных массивов NumPy

Как выглядят массивы:

 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() действует и для многомерных массивов.

Иллюстрированное руководство по изменению формы многомерных массивов NumPy
 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). Напишите нам в комментариях, если что-то осталось непонятным и требует пояснений.

Иллюстрированное руководство по изменению формы многомерных массивов NumPy

proglib.io

Добавить комментарий

Ваш e-mail не будет опубликован.

11 + 3 =