salin array 2D ke dimensi 3, N kali (Python)

106

Saya ingin menyalin array 2D numpy ke dimensi ketiga. Misalnya, diberikan numpy array (2D):

import numpy as np
arr = np.array([[1,2],[1,2]])
# arr.shape = (2, 2)

mengubahnya menjadi matriks 3D dengan N salinan seperti itu dalam dimensi baru. Bertindak arrdengan N = 3, outputnya harus:

new_arr = np.array([[[1,2],[1,2]],[[1,2],[1,2]],[[1,2],[1,2]]])
# new_arr.shape = (3, 2, 2)
anon01
sumber

Jawaban:

146

Mungkin cara terbersih adalah dengan menggunakan 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]]

Karena itu, Anda sering dapat menghindari pengulangan array Anda sama sekali dengan menggunakan penyiaran . Misalnya, saya ingin menambahkan (3,)vektor:

c = np.array([1, 2, 3])

kepada a. Saya dapat menyalin konten sebanyak a3 kali di dimensi ketiga, lalu menyalin konten cdua kali di dimensi pertama dan kedua, sehingga kedua array saya (2, 2, 3), lalu menghitung jumlahnya. Namun, jauh lebih sederhana dan lebih cepat untuk melakukan ini:

d = a[..., None] + c[None, None, :]

Di sini, a[..., None]memiliki bentuk (2, 2, 1)dan c[None, None, :]bentuk (1, 1, 3)*. Ketika saya menghitung jumlahnya, hasilnya 'disiarkan' sepanjang dimensi ukuran 1, memberi saya hasil bentuk (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]]

Penyiaran adalah teknik yang sangat ampuh karena menghindari overhead tambahan yang terlibat dalam pembuatan salinan berulang dari larik input Anda dalam memori.


* Meskipun saya memasukkannya untuk kejelasan, Noneindeks ke dalam csebenarnya tidak diperlukan - Anda juga bisa melakukannya a[..., None] + c, yaitu menyiarkan (2, 2, 1)array terhadap (3,)array. Ini karena jika salah satu array memiliki dimensi yang lebih sedikit daripada yang lain maka hanya dimensi trailing dari dua array yang perlu kompatibel. Untuk memberikan contoh yang lebih rumit:

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
ali_m
sumber
Untuk memverifikasi bahwa ini memang memberikan hasil yang tepat, Anda juga dapat mencetak b[:,:,0], b[:,:,1]dan b[:,:,2]. Setiap potongan dimensi ketiga adalah salinan dari larik 2D asli. Ini tidak begitu jelas hanya dengan melihat print(b).
ely
Apa perbedaan antara None dan np.newaxis? Saat saya mengujinya, ternyata hasilnya sama.
monolit
1
@wedran Mereka persis sama - np.newaxishanyalah alias dariNone
ali_m
27

Cara lain adalah dengan menggunakan numpy.dstack. Misalkan Anda ingin mengulang waktu matriks a num_repeats:

import numpy as np
b = np.dstack([a]*num_repeats)

Triknya adalah dengan membungkus matriks ake dalam daftar satu elemen, kemudian menggunakan *operator untuk menduplikasi elemen dalam num_repeatswaktu daftar ini .

Misalnya, jika:

a = np.array([[1, 2], [1, 2]])
num_repeats = 5

Ini mengulangi larik sebanyak [1 2; 1 2]5 kali di dimensi ketiga. Untuk memverifikasi (di 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)

Pada akhirnya kita bisa melihat bahwa bentuk matriksnya adalah 2 x 2, dengan 5 irisan pada dimensi ketiga.

rayryeng
sumber
Bagaimana ini dibandingkan dengan reshape? Lebih cepat? memberikan struktur yang sama? Ini pasti lebih rapi.
Ander Biguri
@AnderBiguri Saya tidak pernah membandingkan ... Saya meletakkan ini di sini terutama untuk kelengkapan. Ini akan menarik untuk melihat waktu dan perbedaannya.
rayryeng
1
Saya baru saja melakukan img = np.dstack ([arr] * 3) dan bekerja dengan baik! Terima kasih
thanos.
1
Pikir saya bisa mengusulkan keluaran dilihat untuk efisiensi. Menjadi pos lama, orang mungkin melewatkannya. Menambahkan solusi pada T&J ini.
Divakar
1
IMHO solusi yang paling mudah dibaca, tetapi akan sangat bagus untuk membandingkannya dengan metode lain untuk perbandingan.
mrgloom
16

Gunakan tampilan dan dapatkan runtime gratis! Perluas n-dimlarik generik ken+1-dim

Diperkenalkan di NumPy1.10.0 , kita dapat memanfaatkan numpy.broadcast_tountuk menghasilkan 3Dtampilan ke dalam 2Dlarik input. Manfaatnya tidak akan ada overhead memori tambahan dan runtime yang hampir bebas. Ini akan menjadi penting dalam kasus di mana array besar dan kami baik-baik saja untuk bekerja dengan view. Juga, ini akan bekerja dengan n-dimkasus umum .

Saya akan menggunakan kata tersebut stacksebagai pengganti copy, karena pembaca mungkin bingung dengan menyalin array yang membuat salinan memori.

Tumpuk di sepanjang sumbu pertama

Jika kita ingin menumpuk input di arrsepanjang sumbu pertama, solusi dengan np.broadcast_tomembuat 3Dtampilan adalah -

np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here

Tumpuk di sepanjang sumbu ketiga / terakhir

Untuk menumpuk input di arrsepanjang sumbu ketiga, solusi untuk membuat 3Dtampilan adalah -

np.broadcast_to(arr[...,None],arr.shape+(3,))

Jika kita benar-benar membutuhkan salinan memori, kita selalu dapat menambahkannya di .copy()sana. Oleh karena itu, solusinya adalah -

np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()

Berikut cara kerja penumpukan untuk dua kasing, yang ditunjukkan dengan informasi bentuknya untuk contoh kasing -

# 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)

Solusi yang sama akan bekerja untuk memperluas n-dimmasukan untuk n+1-dimmelihat keluaran sepanjang sumbu pertama dan terakhir. Mari jelajahi beberapa casing redup yang lebih tinggi -

Kasus masukan 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)

Kasus masukan 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)

dan seterusnya.

Pengaturan waktu

Mari gunakan contoh 2Dkasus yang besar dan dapatkan pengaturan waktu dan verifikasi keluaran menjadi a view.

# Sample input array
In [19]: arr = np.random.rand(1000,1000)

Mari kita buktikan bahwa solusi yang diusulkan memang sebuah pandangan. Kami akan menggunakan penumpukan di sepanjang sumbu pertama (hasilnya akan sangat mirip untuk penumpukan di sepanjang sumbu ketiga) -

In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True

Mari kita dapatkan pengaturan waktu untuk menunjukkan bahwa ini hampir gratis -

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

Sebagai tampilan, meningkat Ndari 3menjadi 3000tidak mengubah apa pun pada pengaturan waktu dan keduanya dapat diabaikan pada unit pengaturan waktu. Karenanya, efisien baik dalam memori dan kinerja!

Divakar
sumber
3
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)

Edit @ Mr. F, untuk menjaga urutan dimensi:

B=B.T
yevgeniy
sumber
Ini menghasilkan array N x 2 x 2 untuk saya, misalnya B.shapemencetak (N, 2, 2)untuk nilai berapapun N. Jika Anda transpos Bdengan B.Tmaka sesuai dengan output yang diharapkan.
ely
@ Mr.F - Anda benar. Ini akan menyiarkan sepanjang dimensi pertama, dan melakukan itu B[0], B[1],...akan memberi Anda bagian yang tepat, yang menurut saya akan lebih mudah untuk B[:,:,0], B[:,:,1]
diketik
Mungkin lebih mudah untuk mengetik, tetapi misalnya jika Anda melakukan ini dengan data gambar, sebagian besar akan salah, karena hampir semua algoritme akan mengharapkan konvensi aljabar linier digunakan untuk irisan 2D saluran piksel. Sulit membayangkan aplikasi di mana Anda memulai dengan array 2D, memperlakukan baris dan kolom dengan konvensi tertentu, dan kemudian Anda ingin beberapa salinan dari hal yang sama itu meluas ke sumbu baru, tetapi tiba-tiba Anda ingin sumbu pertama berubah artinya menjadi menjadi poros baru ...
ely
@ Tn. F - Oh tentu. Saya tidak bisa menebak aplikasi apa yang Anda inginkan untuk menggunakan matriks 3D di masa mendatang. Meski begitu, semuanya tergantung pada aplikasinya. FWIW, saya lebih suka B[:,:,i]dan itulah yang biasa saya lakukan.
rayryeng
2

Berikut adalah contoh penyiaran yang melakukan persis seperti yang diminta.

a = np.array([[1, 2], [1, 2]])
a=a[:,:,None]
b=np.array([1]*5)[None,None,:]

Maka b*aadalah hasil yang diinginkan dan (b*a)[:,:,0]menghasilkan array([[1, 2],[1, 2]]), yang asli a, seperti halnya (b*a)[:,:,1], dll

Mike O'Connor
sumber
2

Ini sekarang juga bisa dicapai menggunakan np.tile sebagai berikut:

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]]])
FBruzzesi
sumber