#python #numpy
Вопрос:
Я хотел бы скопировать массив numpy 2D в третье измерение. Например, учитывая массив 2D numpy:
import numpy as np
arr = np.array([[1, 2], [1, 2]])
# arr.shape = (2, 2)
преобразуйте его в 3D-матрицу с 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. Просто new_arr = np.массив([a]*3) , где a = [[1,2],[1,2]] работал на меня.
2. @JStrahl: если a имеет форму (r,c), то
np.array([a]*3)
создается массив формы (3,r,c), в то время как я предполагаю, что OP хочет (r,c,3).3. @минут ОП хочет (3, r, c), как видно из new_arr.shape = (3,2,2). Так что это работает.
4. В качестве вопроса о том, как копировать, вставлять и использовать [[1, 2], [1, 2]] в качестве примера, это плохо. Я надеюсь, что смогу отказаться от голоса.
5. @Symbol1 Я этого не уловил — в чем проблема?
Ответ №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
Комментарии:
1. Чтобы убедиться, что это действительно дает правильный результат, вы также можете распечатать
b[:,:,0]
,b[:,:,1]
, иb[:,:,2]
. Каждый срез третьего измерения является копией исходного 2D-массива. Это не так очевидно, просто взглянувprint(b)
.2. В чем разница между None и np.newaxis? Когда я протестировал его, он дал тот же результат.
3. @wedran Они точно такие же —
np.newaxis
это просто псевдонимNone
Ответ №2:
Другой способ-использовать numpy.dstack
. Предположим, что вы хотите повторить матрицу a
num_repeats
раз:
import numpy as np
b = np.dstack([a]*num_repeats)
Хитрость заключается в том, чтобы обернуть матрицу a
в список из одного элемента, а затем с помощью *
оператора многократно дублировать элементы в этом списке num_repeats
.
Например, если:
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 срезами в третьем измерении.
Комментарии:
1. Как это соотносится с
reshape
? Быстрее? дает ту же структуру? Это определенно аккуратнее.2. @AnderBiguri Я никогда не проводил сравнительный анализ… Я поместил это здесь в первую очередь для полноты картины. Будет интересно провести время и увидеть различия.
3. Я только что сделал img = np.dstack([arr] * 3) и работал отлично! Спасибо
4. Подумал, что я мог бы предложить лучший результат для повышения эффективности. Будучи старым постом, люди, возможно, пропустили это. Добавлено решение по этому вопросу и ответу.
Ответ №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
примером, получим тайминги и проверим, что вывод является a 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:
Теперь это также может быть достигнуто с помощью np.tile следующим образом:
import numpy as np
a = np.array([[1,2],[1,2]])
b = np.tile(a,(3, 1,1))
b.shape
(3,2,2)
b
array([[[1, 2],
[1, 2]],
[[1, 2],
[1, 2]],
[[1, 2],
[1, 2]]])
Ответ №5:
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)
Отредактируйте @Mr. F, чтобы сохранить порядок измерений:
B=B.T
Комментарии:
1. Это приводит к массиву N x 2 x 2 для меня, например
B.shape
, печатает(N, 2, 2)
для любого значенияN
. Если вы транспонируетеB
сB.T
помощью, то это соответствует ожидаемому результату.2. @Мистер Ф — Вы правы. Это будет транслироваться по первому измерению, и таким образом
B[0], B[1],...
вы получите правильный срез, который я приведу и скажу, что его легче печатать , чем использоватьB[:,:,0], B[:,:,1]
, и т. Д.3. Это может быть проще для ввода, но, например, если вы делаете это с данными изображений, это было бы в значительной степени неверно, поскольку почти все алгоритмы ожидают, что соглашения линейной алгебры будут использоваться для 2D-срезов пиксельных каналов. Трудно представить приложения, в которых вы начинаете с 2D-массива, обрабатываете строки и столбцы в соответствии с определенным соглашением, а затем вам нужно несколько копий одного и того же, расширяющихся на новую ось, но внезапно вы хотите, чтобы значение первой оси изменилось на новую ось…
4. @Мистер Ф — О, конечно. Я не могу догадаться, в каких приложениях вы захотите использовать 3D-матрицу в будущем. Тем не менее, все зависит от приложения. Фу-у-у, я предпочитаю то, к
B[:,:,i]
чему так хорошо привык.
Ответ №6:
Вот пример трансляции, который делает именно то, что было запрошено.
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]
и т. д.
Ответ №7:
Einops обеспечивает более удобный и читаемый способ записи повторов:
y = einops.repeat(x, 'i j -> 3 i j')
Этот код добавляет еще одну ось длиной 3 в массив 2D numpy.