Скопировать 2D-массив в 3-й размер, N раз (Python)
Я хотел бы скопировать массив 2D в третье измерение. Например, учитывая (2D) массив Numpy:
import numpy as np
arr = np.array([[1,2],[1,2]])
# arr.shape = (2, 2)
преобразовать его в трехмерную матрицу с N такими копиями в новом измерении. Действуя на arr
с N = 3, вывод должен быть:
new_arr = np.array([[[1,2],[1,2]],[[1,2],[1,2]],[[1,2],[1,2]]])
# new_arr.shape = (3, 2, 2)
Ответы
Ответ 1
Вероятно, самый чистый способ - использовать np.repeat
:
a = np.array([[1, 2], [1, 2]])
print(a.shape)
# (2, 2)
# indexing with np.newaxis inserts a new 3rd dimension, which we then repeat the
# array along, (you can achieve the same effect by indexing with None, see below)
b = np.repeat(a[:, :, np.newaxis], 3, axis=2)
print(b.shape)
# (2, 2, 3)
print(b[:, :, 0])
# [[1 2]
# [1 2]]
print(b[:, :, 1])
# [[1 2]
# [1 2]]
print(b[:, :, 2])
# [[1 2]
# [1 2]]
Сказав это, вы можете часто избегать повторения ваших массивов, используя трансляцию. Например, допустим, я хотел добавить вектор (3,)
:
c = np.array([1, 2, 3])
к a
. Я мог скопировать содержимое a
3 раза в третьем измерении, то копировать содержимое c
дважды в обеих первых и втором размеров, так что оба моих массивы были (2, 2, 3)
, а затем вычислить их сумму. Однако сделать это намного проще и быстрее:
d = a[..., None] + c[None, None, :]
Здесь a[..., None]
имеет форму (2, 2, 1)
а c[None, None, :]
имеет форму (1, 1, 3)
*. Когда я вычисляю сумму, результат "транслируется" по размерам размера 1, давая мне результат формы (2, 2, 3)
:
print(d.shape)
# (2, 2, 3)
print(d[..., 0]) # a + c[0]
# [[2 3]
# [2 3]]
print(d[..., 1]) # a + c[1]
# [[3 4]
# [3 4]]
print(d[..., 2]) # a + c[2]
# [[4 5]
# [4 5]]
Вещание является очень мощным методом, поскольку позволяет избежать дополнительных затрат, связанных с созданием повторяющихся копий ваших входных массивов в памяти.
* Хотя я включил их для ясности, индексы None
в c
самом деле не нужны - вы также можете сделать a[..., None] + c
, то есть передать массив (2, 2, 1)
против (3,)
массив. Это связано с тем, что если один из массивов имеет меньше размеров, чем другой, то только совместимые конечные размеры двух массивов должны быть совместимы. Чтобы привести более сложный пример:
a = np.ones((6, 1, 4, 3, 1)) # 6 x 1 x 4 x 3 x 1
b = np.ones((5, 1, 3, 2)) # 5 x 1 x 3 x 2
result = a + b # 6 x 5 x 4 x 3 x 2
Ответ 2
Другой способ - использовать numpy.dstack
. Предположим, что вы хотите повторить матрицу a
num_repeats
раз:
import numpy as np
b = np.dstack([a]*num_repeats)
Трюк состоит в том, чтобы обернуть матрицу a
в список одного элемента, а затем с помощью оператора *
дублировать элементы в этом списке num_repeats
times.
Например, если:
a = np.array([[1, 2], [1, 2]])
num_repeats = 5
Это повторяет массив [1 2; 1 2]
5 раз в третьем измерении. Чтобы проверить (в IPython):
In [110]: import numpy as np
In [111]: num_repeats = 5
In [112]: a = np.array([[1, 2], [1, 2]])
In [113]: b = np.dstack([a]*num_repeats)
In [114]: b[:,:,0]
Out[114]:
array([[1, 2],
[1, 2]])
In [115]: b[:,:,1]
Out[115]:
array([[1, 2],
[1, 2]])
In [116]: b[:,:,2]
Out[116]:
array([[1, 2],
[1, 2]])
In [117]: b[:,:,3]
Out[117]:
array([[1, 2],
[1, 2]])
In [118]: b[:,:,4]
Out[118]:
array([[1, 2],
[1, 2]])
In [119]: b.shape
Out[119]: (2, 2, 5)
В конце мы видим, что форма матрицы 2 x 2
, с 5 срезами в третьем измерении.
Ответ 3
Используйте представление и получите бесплатное время выполнения! Расширьте универсальные массивы n-dim
на n+1-dim
Представленный в NumPy 1.10.0
, мы можем использовать numpy.broadcast_to
, чтобы просто сгенерировать представление 3D
во входном массиве 2D
. Преимущество - отсутствие дополнительных затрат памяти и практически свободное время выполнения. Это было бы важно в тех случаях, когда массивы большие, и мы можем работать с представлениями. Кроме того, это будет работать с общими случаями n-dim
.
Я бы использовал слово stack
вместо copy
, поскольку читатели могут спутать его с копированием массивов, которые создают копии памяти.
Стек вдоль первой оси
Если мы хотим разместить вход arr
вдоль первой оси, решение с np.broadcast_to
для создания представления 3D
будет -
np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here
Сложить вдоль третьей/последней оси
Чтобы поместить вход arr
вдоль третьей оси, решение для создания представления 3D
будет -
np.broadcast_to(arr[...,None],arr.shape+(3,))
Если нам действительно нужна копия памяти, мы всегда можем добавить туда .copy()
. Следовательно, решения будут -
np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()
Здесь показано, как работает наложение для двух случаев, показано с информацией об их форме для примера -
# Create a sample input array of shape (4,5)
In [55]: arr = np.random.rand(4,5)
# Stack along first axis
In [56]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[56]: (3, 4, 5)
# Stack along third axis
In [57]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[57]: (4, 5, 3)
Те же решения будут работать для расширения входа n-dim
на выход вида n+1-dim
вдоль первой и последней осей. Давайте рассмотрим некоторые более сложные случаи -
Случай ввода 3D:
In [58]: arr = np.random.rand(4,5,6)
# Stack along first axis
In [59]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[59]: (3, 4, 5, 6)
# Stack along last axis
In [60]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[60]: (4, 5, 6, 3)
случай ввода 4D:
In [61]: arr = np.random.rand(4,5,6,7)
# Stack along first axis
In [62]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[62]: (3, 4, 5, 6, 7)
# Stack along last axis
In [63]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[63]: (4, 5, 6, 7, 3)
и так далее.
Задержки
Давайте используем большой пример 2D
, получим время и проверим, что выход view
.
# Sample input array
In [19]: arr = np.random.rand(1000,1000)
Позвольте доказать, что предлагаемое решение действительно является представлением. Мы будем использовать укладку вдоль первой оси (результаты будут очень похожи на укладку вдоль третьей оси) -
In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True
Пусть время покажет, что оно практически бесплатно -
In [20]: %timeit np.broadcast_to(arr,(3,)+arr.shape)
100000 loops, best of 3: 3.56 µs per loop
In [21]: %timeit np.broadcast_to(arr,(3000,)+arr.shape)
100000 loops, best of 3: 3.51 µs per loop
С точки зрения просмотра, увеличение N
с 3
до 3000
ничего не изменило в таймингах, и оба они незначительны в единицах времени. Следовательно, эффективны как по памяти, так и по производительности!
Ответ 4
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)
Изменить @Mr.F, чтобы сохранить порядок измерений:
B=B.T
Ответ 5
Здесь показан пример трансляции, который выполняет именно то, что было запрошено.
a = np.array([[1, 2], [1, 2]])
a=a[:,:,None]
b=np.array([1]*5)[None,None,:]
Тогда b*a
- желаемый результат, а (b*a)[:,:,0]
создает array([[1, 2],[1, 2]])
, который является исходным a
, как и (b*a)[:,:,1]
и т.д.