回答:
おそらく最もクリーンな方法は次のようにすること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次元で3回の内容をコピーし、次にc
1次元と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
np.newaxis
のエイリアスですNone
別の方法はを使用すること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
ますか?もっと早く?同じ構造を与えますか?その間違いなくすっきり。
n-dim
配列を拡張するn+1-dim
NumPy1.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
3番目/最後の軸に沿ってスタック
arr
3番目の軸に沿って入力をスタックするには、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
も、タイミングに関しては何も変化せず、両方ともタイミング単位では無視できます。したがって、メモリとパフォーマンスの両方で効率的です!
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)
@ Mr.Fを編集して、次元の順序を維持します。
B=B.T
B.shape
出力(N, 2, 2)
しますN
。で転置するB
とB.T
、予想される出力と一致します。
B[0], B[1],...
あなたに私が主張するだろう、右のスライスを、与えると、その者がより簡単に入力するのではなく使用に言うB[:,:,0], B[:,:,1]
、など
B[:,:,i]
は慣れているものと同じようにそれを好みます。
b[:,:,0]
、b[:,:,1]
とb[:,:,2]
。3次元の各スライスは、元の2D配列のコピーです。これを見るだけではそれほど明白ではありませんprint(b)
。