2D配列を3次元にN回コピーします(Python)


105

派手な2D配列を3次元にコピーしたいのですが。たとえば、(2D)numpy配列があるとします。

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

新しい次元でN個のそのようなコピーを持つ3Dマトリックスに変換します。作用するarrN = 3と、出力は次のようになります。

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

回答:


146

おそらく最もクリーンな方法は次のようにすること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])

aa3次元で3回の内容をコピーし、次にc1次元と2次元の両方で2倍の内容をコピーして、両方の配列がになるようにして(2, 2, 3)、それらの合計を計算します。ただし、これを行う方がはるかに簡単で迅速です。

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

ここでa[..., None]は、形状(2, 2, 1)c[None, None, :]形状(1, 1, 3)*があります。合計を計算すると、サイズ1の次元に沿って結果が「ブロードキャスト」され、shapeの結果が得られます(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,)。これは、配列の1つが他よりも次元数が少ない場合、2つの配列の末尾の次元のみが互換性を必要とするためです。より複雑な例を挙げます:

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

これは確かに正しい結果を与えることを確認するために、あなたはまた、プリントアウトすることができb[:,:,0]b[:,:,1]b[:,:,2]。3次元の各スライスは、元の2D配列のコピーです。これを見るだけではそれほど明白ではありませんprint(b)
2015

Noneとnp.newaxisの違いは何ですか?私がそれをテストしたとき、それは同じ結果を与えました。
モノリス

1
@wedranそれらはまったく同じです- np.newaxisのエイリアスですNone
ali_m

27

別の方法はを使用すること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]3番目の次元で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であり、3次元に5つのスライスがあることがわかります。


これはどのように比較しreshapeますか?もっと早く?同じ構造を与えますか?その間違いなくすっきり。
Ander Biguri 2016

@AnderBiguriベンチマークしたことはありません...完全を期すために、これをここに入れました 時間をかけて、違いを見るのは興味深いでしょう。
rayryeng 2016

1
私はちょうどimg = np.dstack([arr] * 3)を実行し、うまくいきました!ありがとう
thanos.a

1
効率化のために、表示される出力を提案できると考えました。古い投稿なので、人々はそれを逃したかもしれません。このQ&Aにソリューションを追加しました。
Divakar

1
私見の最も読みやすいソリューションですが、比較のために他の方法と比較してベンチマークするのは素晴らしいことです。
mrgloom

16

ビューを使用して無料のランタイムを入手してください!汎用n-dim配列を拡張するn+1-dim

NumPy1.10.0で導入されたので、入力配列にビューをnumpy.broadcast_to生成するだけで活用できます。利点は、追加のメモリオーバーヘッドがなく、ランタイムが実質的に解放されることです。これは、配列が大きく、ビューを操作する場合に不可欠です。また、これは一般的なケースでも機能します。3D2Dn-dim

読者がそれをメモリのコピーを作成する配列のコピーと混同する可能性があるため、私はのstack代わりにこの単語を使用copyします。

最初の軸に沿ってスタック

arr最初の軸に沿って入力を積み重ねたい場合、ビューnp.broadcast_toを作成するためのソリューションは3D-

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

3番目/最後の軸に沿ってスタック

arr3番目の軸に沿って入力をスタックするには、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()

以下は、2つのケースのスタッキングの仕組みです。サンプルケースの形状情報とともに示されています-

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

提案されたソリューションが実際にビューであることを証明しましょう。最初の軸に沿ったスタッキングを使用します(結果は3番目の軸に沿ったスタッキングと非常に似ています)-

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も、タイミングに関しては何も変化せず、両方ともタイミング単位では無視できます。したがって、メモリとパフォーマンスの両方で効率的です!


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

@ Mr.Fを編集して、次元の順序を維持します。

B=B.T

これにより、N x 2 x 2の配列が生成されます。たとえば、の値をB.shape出力(N, 2, 2)しますN。で転置するBB.T、予想される出力と一致します。
2015

@ Mr.F-あなたは正しいです。これは、最初の次元に沿って放送され、そうするだろうB[0], B[1],...あなたに私が主張するだろう、右のスライスを、与えると、その者がより簡単に入力するのではなく使用に言うB[:,:,0], B[:,:,1]、など
rayryeng

タイプする方が簡単かもしれませんが、たとえば、画像データを使用してこれを行っている場合、ほとんどすべてのアルゴリズムが線形代数の規則をピクセルチャネルの2Dスライスに使用することを期待しているため、ほとんど正しくありません。2D配列で開始し、特定の規則で行と列を処理し、次に同じものの複数のコピーを新しい軸に拡張したいアプリケーションを想像するのは難しいが、突然、最初の軸の意味を次のように変更したい新しい軸になります...
2015

@ Mr.F-確かに。将来、3Dマトリックスをどのアプリケーションに使用するかはわかりません。そうは言っても、それはすべてアプリケーションに依存します。FWIW、私B[:,:,i]は慣れているものと同じようにそれを好みます。
rayryeng 2015

2

以下は、要求された内容を正確に実行するブロードキャストの例です。

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]


1

これは、次のように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]]])
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.