numpy.stackは、配列同士を重ねて、次元数が一つ上の配列を生成する関数です。numpy.concatenateは配列を連結しますが、numpy.stackは配列を重ねて次元数を一つ上げるのが特徴です。
ここでしっかりと操作を確認しておきましょう。
1. numpy.stackの使い方
まずは、numpy.stackの書き方を確認しましょう。
書き方:
np.stack(arrays, axis=0, out=None)
パラメーター:
引数 | 型 | 解説 |
arrays | sequence of array_like | ここで指定した配列を重ねます。重ねる配列の組み合わせはリストかタプルで指定します。また配列同士は、全ての場合でshapeが完全に一致している必要があります。 |
axis(optional) | int | 配列を重ねる軸を指定します。 |
out(optional) | ndarray | 配列を重ねた戻り値を、既存の配列に上書きしたい場合に指定します。重ねた配列と、上書きする既存の配列はshapeが一致している必要があります。 |
戻り値:
要素を重ねて次元が1つ上になった新しいndarray |
一緒に確認したい関数:
- hstack: 配列を水平に連結
- vstack: 配列を垂直に連結
- dstack: 配列を奥行き方向に連結
- concatenate: 配列を連結
- split
- block
それでは実際のコードで確認しましょう。
1.1. 1次元配列を重ねて2次元配列を生成
numpy.stackは、1次元配列同士を重ねると2次元配列を生成します。戻り値は2次元配列になるので、行と列のどちらを軸にして重ねるのかを、オプション引数「axis=」で指定します。
2次元配列では、「axis=0」が行、「axis=1」が列なので、どちらに重ねていきたいかで使い分けることができます。
例えば、shape(N,)の1次元配列を2つ重ねるとします。
shape(N,)の1次元配列を2つ重ねると、「axis=0」では行を重ねたshape(2, N)の2次元配列を生成します。「axis=1」では列を重ねたshape(N, 2)の2次元配列が生成されます。
重ねる配列の数が増えるごとに生成される2次元配列のshapeは下表のように変わっていきます。
axisの指定/ 重ねる配列の数 | 2つ | 3つ | 5つ |
axis=0 | shape(2, N) | shape(3, N) | shape(5, N) |
axis=1 | shape(N, 2) | shape(N, 3) | shape(N, 5) |
ご覧のように「axis=0」では行が増え、「axis=1」では列が増えていきます。
なお、いずれの場合も、重ねる配列のshapeは揃っている必要があります。これはaxisの指定と、重ねる配列の次元数に関わらず、です。
それでは、実際に見ていきましょう。
1.1.1. axis=0(デフォルト値)で行を重ねる
1次元配列同士にnp.stack()を「axis=0(デフォルト値)」で実行した場合、配列は2次元配列になり、行として重ねられます。
例えば、shape(N,)の1次元配列を2つ重ねるとshape(2, N)の、3つ重ねるとshape(3, N)の2次元配列になります。
以下のコードでは、shape(3,)の1次元配列を2つ重ねているので、shape(2, 3)の2次元配列が生成されています。なお、引数で「axis=0」と指定してもいいのですが、これはデフォルト値なので、特にあらためて指定はしていません。
また、コード内で使用しているnumpy.arangeは『numpy.arange – 連番の配列を生成する方法』で解説しています。
import numpy as np
# 元の配列①を生成
arr1 = np.arange(3)
# 元の配列②を生成
arr2 = np.arange(3, 6)
# 配列を横に重ねる
arr3 = np.stack((arr1, arr2))
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr3: 新しい配列\n', arr3, '\n')
print('arr3のshape\n', arr3.shape, '\n')
print('arr3の次元数\n', arr3.ndim)
既存の配列に別の配列を重ねるのではなく、指定の配列を重ねた新しい配列を生成している点もご確認ください。
3つ以上の1次元配列を重ねると、重ねた分だけ行が増えていきます。以下のコードでは、shape(3,)の1次元配列を3つ重ねているので、shape(3, 3)の2次元配列が生成されています。
# 元の配列③を生成
arr4 = np.arange(7, 10)
# 3つの配列を重ねる
arr5 = np.stack((arr1, arr2, arr4))
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr4: 元の配列③\n', arr4, '\n')
print('arr5: 新しい配列\n', arr5, '\n')
print('arr5のshape\n', arr5.shape, '\n')
print('arr5の次元数\n', arr5.ndim)
なお、1次元配列の場合は、重ねる配列のshape(列数)は必ず一致している必要があります。もしshapeが異なる配列を重ねようとすると、次のようにエラーになります。
''' 全ての配列のshapeが同じでなければエラー '''
# 元の配列④を生成(1×4)
arr6 = np.arange(4, 9)
print('結合する配列のshape確認')
print('arr1: 元の配列①のshape\n', arr1.shape)
print('arr6: 元の配列④のshape\n', arr6.shape)
np.stack((arr1, arr6))
なお、これは1次元配列同士で、np.vstack()を実行しているのと同じことになります。3次元配列までの場合は、そちらを使うことが多いので、以下のページで確認しておきましょう。
1.1.2. axis=1で列を重ねる
shape(N,)の2つの1次元配列同士を、axis=1を指定してnp.stack()で重ねると、それぞれの配列の要素を列として重ねたshape(N, 2)の2次元配列となります。3つ重ねるとshape(N, 3)の2次元配列となります。
以下のコードでは、shape(3,)の2次元配列を2つ重ねているので、shape(3, 2)の2次元配列が生成されています。
import numpy as np
# 元の配列①を生成
arr1 = np.arange(3)
# 元の配列②を生成
arr2 = np.arange(3, 6)
# 配列を横に重ねる
arr3 = np.stack((arr1, arr2), axis=1)
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr3: 新しい配列\n', arr3, '\n')
print('arr3のshape\n', arr3.shape, '\n')
print('arr3の次元数\n', arr3.ndim)
3つ以上の1次元配列を重ねると、重ねた分だけ列が増えていきます。以下のコードでは、shape(3, )の1次元配列を3つ重ねているので、shape(3, 3)の2次元配列が生成されています。
# 元の配列③を生成
arr4 = np.arange(7, 10)
# 3つの配列を重ねる
arr5 = np.stack((arr1, arr2, arr4), axis=1)
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr4: 元の配列③\n', arr4, '\n')
print('arr5: 新しい配列\n', arr5, '\n')
print('arr5のshape\n', arr5.shape, '\n')
print('arr5の次元数\n', arr5.ndim)
axis=1を指定して1次元配列同士を重ねる場合も、shapeが異なっていればエラーになる点は同じです。
1.2. 2次元配列同士を重ねて3次元配列を生成
続いて2次元配列同士を重ねる場合を見ていきましょう。
2次元配列同士を重ねると、3次元配列が生成されます。3次元配列は、「axis=0(奥行き)」、「axis=1(行)」、「axis=2(列)」の3つの軸がありますので、どれを軸にしたいのかを指定する必要があります。
例えば、shape(N, M)の2次元配列を重ねるとします。
「axis=0」で重ねる場合は、2つ重ねるとshape(2, N, M)、3つ重ねるとshape(3, N, M)の3次元配列になります。
「axis=1」で重ねる場合は、2つ重ねるとshape(N, 2, M)、3つ重ねるとshape(N, 3, M)の3次元配列になります。
「axis=2」で重ねる場合は、2つ重ねるとshape(N, M, 2)、3つ重ねるとshape(N, M, 3)の3次元配列になります。
重ねる配列の数が増えるごとに生成される3次元配列のshapeは下表のように変わっていきます。
axisの指定/ 重ねる配列の数 | 2つ | 3つ | 5つ |
axis=0 | shape(2, N, M) | shape(3, N, M) | shape(5, N, M) |
axis=1 | shape(N, 2, M) | shape(N, 3, M) | shape(N, 5, M) |
axis=2 | shape(N, M, 2) | shape(N, M, 3) | shape(N, M, 5) |
ご覧のように「axis=0」では奥行きが増え、「axis=1」では行が増え、「axis=2」では列が増えていきます。
それでは、実際のコードを見ていきましょう。
1.2.1. axis=0(デフォルト値)で奥行きを重ねる
まずは、デフォルト値である「axis=0」の場合を見ていきましょう。この場合は、奥行きが重ねられていきます。
例えば、shape(N, M)の2次元配列を2つ重ねるとshape(2, N, M)、3つ重ねるとshape(3, N, M)の3次元配列になります。
実際に以下のコードで確認していきましょう。shape(2, 3)の2次元配列を2つ重ねているので、shape(2, 2, 3)の3次元配列が生成されています。
なお、オプション引数axis=0はデフォルト値なので、コード内では、あえて指定することはしていません。また、コード内で使用しているreshape()は、『numpy.reshape – 配列の形状を変換』で解説しています。
import numpy as np
''' 2次元配列以上の場合 '''
# 元の配列①を生成
arr1 = np.arange(6).reshape(2, 3)
# 元の配列②を生成
arr2 = np.arange(6, 12).reshape(2, 3)
# 2つの配列を奥行きを軸に重ねる
arr3 = np.stack((arr1, arr2))
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr3: 新しい配列\n', arr3, '\n')
print('arr3のshape\n', arr3.shape, '\n')
print('arr3の次元数\n', arr3.ndim)
3つ以上の2次元配列を重ねると、その数だけ奥行きが増えていきます。
以下のコードではshape(2, 3)の2次元配列を3つ重ねているので、shape(3, 2, 3)の3次元配列になっています。
''' 3つの2次元配列を奥行きを軸に重ねる '''
# 元の配列③を生成
arr4 = np.arange(12, 18).reshape(2, 3)
# 3つの配列を奥行きを軸に重ねる
arr5 = np.stack((arr1, arr2, arr4))
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr4: 元の配列③\n', arr4, '\n')
print('arr5: 新しい配列\n', arr5, '\n')
print('arr5のshape\n', arr5.shape, '\n')
print('arr5の次元数\n', arr5.ndim)
このように2次元配列をaxis=0で重ねていくと、奥行きが増えていきます。
1.2.2. axis=1で行を重ねる
続いて、「axis=1」を指定した場合を見ていきましょう。
3次元配列の「axis=1」は行です。そのため、行を重ねた3次元配列を生成します。例えば、shape(N, M)の2次元配列を2つ重ねるとshape(N, 2, M)、3つ重ねるとshape(N, 3, M)の3次元配列が生成されます。
以下のコードで確認しましょう。
shape(2, 3)の2次元配列を2つ重ねているので、shape(2, 2, 3)の3次元配列が生成されています。
import numpy as np
''' 2次元配列以上の場合 '''
# 元の配列①を生成
arr1 = np.arange(6).reshape(2, 3)
# 元の配列②を生成
arr2 = np.arange(6, 12).reshape(2, 3)
# 2つの配列を行を軸に重ねる
arr3 = np.stack((arr1, arr2), axis=1)
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr3: 新しい配列\n', arr3, '\n')
print('arr3のshape\n', arr3.shape, '\n')
print('arr3の次元数\n', arr3.ndim)
3つ以上の2次元配列を重ねる場合も同じです。
以下のコードでは、shape(2, 3)の2次元配列を3つ重ねているので、shape(2, 3, 3)の3次元配列が生成されています。
''' 3つの2次元配列を行を軸に重ねる'''
# 元の配列③を生成
arr4 = np.arange(12, 18).reshape(2, 3)
# 3つの配列を行を軸に重ねる
arr5 = np.stack((arr1, arr2, arr4), axis=1)
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr4: 元の配列③\n', arr4, '\n')
print('arr5: 新しい配列\n', arr5, '\n')
print('arr5のshape\n', arr5.shape, '\n')
print('arr5の次元数\n', arr5.ndim)
このように2次元配列を、「axis=1」で重ねていくと、行が増えていきます。
1.2.3. axis=2で列を重ねる
次に、「axis=2」を指定した場合です。
3次元配列の「axis=2」は列です。そのため列を重ねた3次元配列を生成します。例えば、shape(N, M)の2次元配列を2つ重ねるとshape(N, M, 2)、3つ重ねるとshape(N, M, 3)の3次元配列が生成されます。
以下のコードで確認しましょう。
shape(2, 3)の2次元配列を2つ重ねているので、shape(2, 3, 2)の3次元配列が生成されています。
import numpy as np
''' 2次元配列以上の場合 '''
# 元の配列①を生成
arr1 = np.arange(6).reshape(2, 3)
# 元の配列②を生成
arr2 = np.arange(6, 12).reshape(2, 3)
# 2つの配列を列を軸に重ねる
arr3 = np.stack((arr1, arr2), axis=2)
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr3: 新しい配列\n', arr3, '\n')
print('arr3のshape\n', arr3.shape, '\n')
print('arr3の次元数\n', arr3.ndim)
3つ以上の2次元配列を重ねる場合同じです。
以下のコードでは、shape(2, 3)の2次元配列を3つ重ねているので、shape(2, 3, 3)の3次元配列が生成されています。
''' 3つの2次元配列を列を軸に重ねる'''
# 元の配列③を生成
arr4 = np.arange(12, 18).reshape(2, 3)
# 3つの配列を行を軸に重ねる
arr5 = np.stack((arr1, arr2, arr4), axis=2)
print('arr1: 元の配列①\n', arr1, '\n')
print('arr2: 元の配列②\n', arr2, '\n')
print('arr4: 元の配列③\n', arr4, '\n')
print('arr5: 新しい配列\n', arr5, '\n')
print('arr5のshape\n', arr5.shape, '\n')
print('arr5の次元数\n', arr5.ndim)
このように2次元配列をaxis=2で重ねていくと、奥行きが増えていきます。
1.3. その他のtips
1.3.1. axisは負の値で指定することもできる
あえてコードは書きませんが、axisは負の値で指定することもできます。その場合は、以下の画像で表しているように、1次元配列同士を重ねて2次元配列を生成する場合は、-1は列に、-2は行になります。
2次元配列同士を重ねて3次元配列を生成する場合は、-1は列に、-2は行に、-3は奥行きになります。
どちらでも使いやすい方を使って構いません。
1.3.2. オプション引数「out=」で既存の配列を変更
np.stack()は、既存の配列を重ねた新しい配列を生成します。もし、既存の配列を重ねた結果で上書きしたい場合は、オプション引数の「out=」で指定します。
以下のコードをご覧ください。
arr1とarr2を重ねた結果を、既存の配列arr3に上書きしています。
import numpy as np
# 元の配列①を生成
arr1 = np.arange(3)
# 元の配列②を生成
arr2 = np.arange(3, 6)
# 上書きするために初期化した配列を生成
arr3 = np.zeros((2, 3))
# arr1とarr2を重ねた結果でarr3を上書き
np.stack((arr1, arr2), out=arr3)
# arr3をprintして確認してみましょう。
print(arr3)
ただし、上書き先の配列のshapeは、既存の配列を重ねて生成した配列のshapeと一致していなければエラーになります。
''' 重ねた配列と異なるshapeを指定した場合はエラー '''
arr4 = np.zeros((3, 2)) # 3×2の2次元配列
# 配列を重ねて生成した配列は2×3なので一致しない
np.stack((arr1, arr2), out=arr4)
''' 重ねた配列と異なるshapeを指定した場合はエラー '''
arr4 = np.zeros((3, 2)) # 3×2の2次元配列
# 配列を重ねて生成した配列は2×3なので一致しない
np.stack((arr1, arr2), out=arr4)
2. まとめ
ここまで見てきたようにnumpy.stackは、axisの指定は実行後の配列を基準に行います。1次元配列同士を重ねるなら、軸は2次元配列のもので指定します。また2次元配列同士を重ねるなら、軸は3次元配列のもので指定します。
まずは、この点になれるようにすると良いでしょう。
また以下の関数もあわせて確認すると、より理解が深まることでしょう。
- concatenate: 配列を連結
- hstack: 配列を水平に連結
- vstack: 配列を垂直に連結
- dstack: 配列を奥行き方向に連結
- split
- block
コメント