9. Манипулирование формой массивов

Форму массива определяет количество элементов вдоль каждой оси:

>>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
>>> a
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
>>>
>>> a.shape    #  Узнаем форму массива
(4, 3)
>>>
>>> a.ravel()    #  "Сплющивает" массив до одной оси
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

Изменить форму массива можно разными способами:


>>> a.reshape(2, 6)    #  Возвращает массив с измененной формой
array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12]])
>>> 
>>> a.reshape(3, 4)
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>>
>>> a.T    #  Возвращает транспонированный массив
array([[ 1,  4,  7, 10],
       [ 2,  5,  8, 11],
       [ 3,  6,  9, 12]])
>>>
>>> #  Обратите внимание на то, что возвращается новый массив
... #  в то время как исходный массив не изменяется.
... 
>>> a
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

Функция reshape не изменяет исходный массив. Если же вам необходимо изменить форму существующего массива, то можно воспользоваться методом resize.

>>> a
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
>>>
>>> a.resize((2, 6))
>>> a
array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12]])

Если в функции reshape один из размеров задать равным -1, то этот размер будет вычислен автоматически:

>>> a
array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12]])
>>>
>>> a.reshape(3, -1)
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>>
>>> a.reshape(4, -1)
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
>>>
>>> a.reshape(-1, 3)
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

9.1. Объединение массивов

Объединение массивов возможно по разным осям:

>>> a = np.arange(4).reshape(2, 2)
>>> b = np.arange(6, 10).reshape(2, 2)
>>>
>>> a
array([[0, 1],
       [2, 3]])
>>> b
array([[6, 7],
       [8, 9]])
>>> 
>>> np.vstack((a, b))
array([[0, 1],
       [2, 3],
       [6, 7],
       [8, 9]])
>>>
>>> np.hstack((a, b))
array([[0, 1, 6, 7],
       [2, 3, 8, 9]])

Как видим функция vstack объединяет массивы по вертикали, а hstack по горизонтали. Причем количество элементов вдоль объединяемых осей должно быть одинаковым:

>>> a = np.arange(2)
>>> b = np.arange(5, 9).reshape(2, 2)
>>>
>>> a
array([0, 1])
>>> b
array([[5, 6],
       [7, 8]])
>>>
>>> np.vstack((a, b))
array([[0, 1],
       [5, 6],
       [7, 8]])
>>> 
>>> a = np.arange(3)
>>> a
array([0, 1, 2])
>>>
>>> np.vstack((a, b))    #  Выдаст ошибку

Функция hstack объединяет массивы по горизонтали и если объединить два одномерных массива, то получится одномерный массив. То есть количество осей вдоль которых происходит объединение остается неизменным - при вертикальном объединении не меняется количество столбцов, а при горизонотальном количество строк.

Совсем иначе обстоит дело с функцией column_stack. Функция column_stack представляет одномерные массивы в виде столбцов после чего объединяет их. Для двумерных массивов column_stack эквивалентна hstack:

>>> a = np.arange(2)
>>> b = np.arange(2,4)
>>>
>>> a
array([0, 1])
>>> b
array([2, 3])
>>>
>>> np.column_stack((a, b))
array([[0, 2],
       [1, 3]])
>>>
>>> a = np.arange(4)
>>> b = np.arange(4,8)
>>>
>>> a
array([0, 1, 2, 3])
>>> b
array([4, 5, 6, 7])
>>>
>>> np.column_stack((a, b))
array([[0, 4],
       [1, 5],
       [2, 6],
       [3, 7]])
>>>
>>> a = np.arange(4).reshape(2,2)
>>> b = np.arange(4,8).reshape(2, 2)
>>>
>>> a
array([[0, 1],
       [2, 3]])
>>> b
array([[4, 5],
       [6, 7]])
>>>
>>> np.column_stack((a, b))
array([[0, 1, 4, 5],
       [2, 3, 6, 7]])
>>>
>>> np.hstack((a, b))
array([[0, 1, 4, 5],
       [2, 3, 6, 7]])

Функция row_stack эквивалентна vstack для любых входных массивов:

>>> a = np.arange(4)
>>> b = np.arange(4,8)
>>>
>>> a
array([0, 1, 2, 3])
>>> b
array([4, 5, 6, 7])
>>>
>>> np.row_stack((a, b))
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
>>>
>>> a = np.arange(2).reshape(2,1)
>>> b = np.arange(2,4).reshape(2,1)
>>>
>>> a
array([[0],
       [1]])
>>> b
array([[2],
       [3]])
>>>
>>> np.row_stack((a, b))
array([[0],
       [1],
       [2],
       [3]])

Если размерность массивов больше двух, то их так же можно объединить. Функция hstack объединит их вдоль второй оси, а функция vstack вдоль первой. Если необходимо объединить массивы вдоль какой-то определенной оси то можно воспользоваться функцией concatenate в которой номер необходимой оси можно передать в качестве параметра.

В некоторых случаях бывает необходимо объединять срезы массивов вдоль оси длинна которой должна быть равна 1. В таких случаях можно воспользоваться объектом newaxis, который и создает ось длинной 1.

>>> from numpy import newaxis
>>>
>>> a = np.arange(2)
>>> a
array([0, 1])
>>>
>>> b = np.arange(2,4)
>>> b
array([2, 3])
>>>
>>> a[:, newaxis]
array([[0],
       [1]])
>>>
>>> b[:, newaxis]
array([[2],
       [3]])
>>>
>>> np.hstack((a[:, newaxis], b[:, newaxis]))
array([[0, 2],
       [1, 3]])
>>>
>>> c = np.arange(16).reshape(4, 4)
>>> c
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
>>>
>>> c[0, :, newaxis]
array([[0],
       [1],
       [2],
       [3]])
>>>
>>> c[:, 1, newaxis]
array([[ 1],
       [ 5],
       [ 9],
       [13]])
>>>
>>> c[1:3, 1:3, newaxis]
array([[[ 5],
        [ 6]],

       [[ 9],
        [10]]])
>>>
>>> c[1:3, 1:3, newaxis].shape
(2, 2, 1)

Есть еще один способ быстро объединять и создавать массивы - это объекты r_ и c_. Они позволяют объединить массивы вдоль одной оси или позволяют создать новый массив с использованием операций среза.

>>> d = np.r_[0:5]    #  Создает массив из среза
>>> d
array([0, 1, 2, 3, 4])
>>>
>>> d = np.r_[0:5, 3, 2, 1, 0]    #  Создает массив из среза и последовательности
>>> d
array([0, 1, 2, 3, 4, 3, 2, 1, 0])
>>> 
>>> d = np.r_[np.array([1, 2]), np.array([5, 6])]     #  Все равно создаст массив с одной строкой
>>> d
array([1, 2, 5, 6])
>>>
>>> d = np.r_[[1,2,3], [9,8,7]]     #  "d" по прежнему состоит из одной строки
>>> d
array([1, 2, 3, 9, 8, 7])
>>>
>>> np.c_[0:5]
array([[0],
       [1],
       [2],
       [3],
       [4]])

9.2. Разделение массивов

Разделить массив вдоль горизонтальной оси можно с помощью функции hsplit, а вдоль вертикальной vsplit:

>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>>
>>> np.hsplit(a, 2)
[array([0, 1, 2, 3, 4]), array([5, 6, 7, 8, 9])]
>>>
>>> np.hsplit(a, 3)    #  Выдаст ошибку: 10 на 3 нацело не делится
>>> np.vsplit(a, 2)    #  Так же выдаст ошибку: у "а" нет вертикальной оси
>>>
>>> a = np.arange(16).reshape(4,4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
>>>
>>> np.vsplit(a, 2)
[array([[0, 1, 2, 3],
       [4, 5, 6, 7]]),
array([[ 8,  9, 10, 11],
       [12, 13, 14, 15]])]

Если размерность массива больше 2 и делить его нужно вдоль определенной оси, то можно воспользоваться функцией array_split, указав необходимую ось.