2D diziyi 3. boyuta, N kez kopyalayın (Python)


108

Uyuşmuş bir 2D diziyi üçüncü bir boyuta kopyalamak istiyorum. Örneğin, (2D) numpy dizisi verildiğinde:

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

yeni bir boyutta bu tür N kopya ile bir 3B matrise dönüştürün. arrN = 3 ile hareket ederek , çıktı şöyle olmalıdır:

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

Yanıtlar:


146

Muhtemelen en temiz yol np.repeatşunları kullanmaktır :

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]]

Bunu söyledikten sonra, yayını kullanarak dizilerinizi tamamen tekrarlamaktan sık sık kaçınabilirsiniz . Örneğin, bir (3,)vektör eklemek istediğimi varsayalım:

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

için a. Ben içeriğini kopyalamak olabilir adaha sonra içeriğini kopyalamak, üçüncü boyutta 3 kez cbenim Dizilerin her ikisi de böylece, hem birinci ve ikinci boyutta iki kez (2, 2, 3), daha sonra toplamını hesaplamak. Ancak bunu yapmak çok daha basit ve daha hızlıdır:

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

Burada a[..., None]şekli (2, 2, 1)ve c[None, None, :]şekli vardır (1, 1, 3)*. Toplamı hesapladığımda, sonuç 1 boyutunun boyutları boyunca 'yayınlanır' ve bana bir şekil sonucu verir (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]]

Yayın, çok güçlü bir tekniktir çünkü bellekte girdi dizilerinizin tekrarlanan kopyalarını oluşturmanın ek yükünü önler.


* Açıklık için onları dahil etsem de, Noneindisler caslında gerekli değildir - ayrıca yapabilirsiniz a[..., None] + c, yani bir (2, 2, 1)diziye karşı bir (3,)dizi yayınlayabilirsiniz . Bunun nedeni, dizilerden birinin diğerinden daha az boyuta sahip olması durumunda, iki dizinin yalnızca sondaki boyutlarının uyumlu olması gerektiğidir. Daha karmaşık bir örnek vermek gerekirse:

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

Bu gerçekten doğru sonucu verir doğrulamak için ayrıca yazdırabilirsiniz b[:,:,0], b[:,:,1]ve b[:,:,2]. Her bir üçüncü boyut dilimi, orijinal 2D dizinin bir kopyasıdır. Bu sadece bakmak kadar açık değil print(b).
ely

None ve np.newaxis arasındaki fark nedir? Test ettiğimde aynı sonucu verdi.
monolith

1
@wedran Tam olarak aynı - np.newaxissadece bir takma adNone
ali_m

27

Başka bir yol kullanmaktır numpy.dstack. Matris a num_repeatszamanlarını tekrarlamak istediğinizi varsayarsak :

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

İşin püf noktası, matrisi atek bir öğeden oluşan bir listeye sarmak , ardından *bu listedeki öğeleri çoğaltmak için operatörü kullanmaktır num_repeats.

Örneğin, eğer:

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

Bu [1 2; 1 2], üçüncü boyutta diziyi 5 kez tekrarlar . Doğrulamak için (IPython'da):

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)

Sonunda matrisin şeklinin 2 x 2üçüncü boyutta 5 dilim olduğunu görebiliriz .


Bu nasıl karşılaştırılır reshape? Daha hızlı? aynı yapıyı veriyor? Kesinlikle daha temiz.
Ander Biguri

@AnderBiguri Ben asla kıyaslamadım ... Bunu öncelikle bütünlük için buraya koydum. Zaman ve farklılıkları görmek ilginç olacak.
rayryeng

1
Az önce img = np.dstack ([arr] * 3) yaptım ve iyi çalıştı! Teşekkür ederim
thanos.a

1
Verimlilik için görüntülenmiş bir çıktı önerebileceğimi düşündüm. Eski bir gönderi olduğu için insanlar bunu kaçırmış olabilir. Bu Soru-Cevap bölümüne bir çözüm eklendi.
Divakar

1
IMHO en okunabilir çözüm, ancak karşılaştırma için diğer yöntemlerle karşılaştırmak harika olurdu.
mrgloom

16

Bir görünüm kullanın ve ücretsiz çalışma süresi kazanın! Genel n-dimdizileri genişletmek içinn+1-dim

Sunulan numpy1.10.0 biz kaldıraç numpy.broadcast_tosadece oluşturmak için 3Diçine bir görünümü 2Ddizi. Bunun faydası, fazladan bellek yükü ve neredeyse ücretsiz çalışma süresi olacaktır. Bu, dizilerin büyük olduğu ve görünümlerle çalışmamızın uygun olduğu durumlarda çok önemlidir. Ayrıca, bu genel n-dimdurumlarda işe yarar .

Okuyucular bellek kopyalarını oluşturan dizilerin kopyalanmasıyla karıştırabileceğinden, stackyerine kelimeyi kullanırdım copy.

İlk eksen boyunca yığın

arrİlk eksen boyunca girdiyi istiflemek istersek , görünümü np.broadcast_tooluşturmanın çözümü 3Dşu olacaktır:

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

Üçüncü / son eksen boyunca yığın

Girişleri arrüçüncü eksen boyunca istiflemek için 3Dgörünüm oluşturmanın çözümü şu olacaktır:

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

Aslında bir bellek kopyasına ihtiyacımız varsa, her zaman .copy()oraya ekleyebiliriz . Dolayısıyla çözümler şu şekilde olacaktır -

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

Örnek bir vaka için şekil bilgileriyle gösterilen iki kasa için istifleme şu şekilde çalışır:

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

Aynı çözüm (ler), çıktıyı ilk ve son eksenler boyunca görüntülemek n-dimiçin bir girdiyi genişletmek için işe n+1-dimyarar. Daha yüksek karanlık durumları inceleyelim -

3D giriş durumu:

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 giriş durumu:

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)

ve bunun gibi.

Zamanlamalar

Büyük bir örnek 2Dvaka kullanalım ve zamanlamaları alalım ve çıktının bir view.

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

Önerilen çözümün gerçekten bir görüş olduğunu kanıtlayalım. İlk eksen boyunca istiflemeyi kullanacağız (sonuçlar üçüncü eksen boyunca istifleme için çok benzer olacaktır) -

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

Neredeyse ücretsiz olduğunu göstermek için zamanlamaları alalım -

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

Bir görünüm olmak artan Nden 3üzere 3000zamanlamaları değiştirildi hiçbir şey ve her iki zamanlama birimleri önemsizdir. Bu nedenle hem bellek hem de performans açısından verimli!


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

Boyut sırasını korumak için @ Mr.F'yi düzenleyin:

B=B.T

Bu benim için bir N x 2 x 2 dizisi ile sonuçlanır, örneğin değeri ne olursa olsun B.shapeyazdırır . Eğer devrik Eğer ile o zaman beklenen çıktıyı eşleşir. (N, 2, 2)NBB.T
ely

@ Bay F - Haklısınız. Bu, ilk boyut boyunca yayınlanacak ve bunu yapmak B[0], B[1],...size doğru dilimi verecektir, bunu kullanmaktan çok B[:,:,0], B[:,:,1]
yazmaktan

Yazması daha kolay olabilir, ancak örneğin bunu görüntü verileriyle yapıyorsanız, bu büyük ölçüde yanlış olacaktır, çünkü neredeyse tüm algoritmalar doğrusal cebir kurallarının piksel kanallarının 2D dilimlerinde kullanılmasını bekleyecektir. Bir 2B diziyle başladığınız, satırları ve sütunları belirli bir kural ile işlemden geçirdiğiniz ve sonra aynı şeyin birden fazla kopyasının yeni bir eksene doğru genişlemesini istediğiniz, ancak birdenbire ilk eksenin anlamını değiştirmesini istediğiniz bir uygulama hayal etmek zor. yeni eksen olun ...
ely

@ Bay F - Oh kesinlikle. Gelecekte 3B matrisi hangi uygulamaları kullanmak isteyeceğinizi tahmin edemiyorum. Bununla birlikte, her şey uygulamaya bağlıdır. FWIW, B[:,:,i]alışık olduğum şeylerin yanı sıra tercih ederim .
rayryeng

2

İşte tam olarak istenen şeyi yapan bir yayın örneği.

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

Sonra b*aistenen sonuç ve orijinal olan (b*a)[:,:,0]üretir array([[1, 2],[1, 2]]), olduğu agibi (b*a)[:,:,1], vb.


2

Bu, artık aşağıdaki gibi np.tile kullanılarak da elde edilebilir :

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]]])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.