配列の連結
NumPy の配列を連結(結合)する関数の一覧です。
numpy.concatenate()
numpy.concatenate()を使うと、複数の配列を 連結 できます。
numpy.concatenate((a1, a2, ...), axis=0, out=None)
a1, a2, a3, … には配列を渡します。
2 次元以上の配列を渡す場合は axis を指定できます (デフォルトは axis=0)。
# NUMPY_CONCATENATE # In[1] import numpy as np a1 = np.array([[1, 2, 3], [4, 5, 6]]) a2 = np.array([[7, 8, 9]]) a3 = np.array([[1, 1], [2, 2]]) # 2次元配列同士を縦軸方向に連結 x = np.concatenate((a1, a2)) # 2次元配列同士を横軸方向に連結 y = np.concatenate((a1, a3), axis=1) print("配列x\n{}\n\n".format(x)) print("配列y\n{}".format(y)) # 配列x # [[1 2 3] # [4 5 6] # [7 8 9]] # 配列y # [[1 2 3 1 1] # [4 5 6 2 2]]
numpy.vstack()
numpy.vstack() は、受け取った配列を縦積みします。引数には配列のシーケンス(リストやタプル)を渡します。この関数は numpy.concatenate() で axis=0 を渡した場合と同じ機能をもちます。
# NUMPY_VSTACK # In[1] import numpy as np a1 = np.array([[1, 1, 1]]) a2 = np.array([[2, 2, 2]]) # 1次元配列同士を縦軸(axis=0)方向に連結 x = np.vstack((a1, a2)) print(x) # [[1 1 1] # [2 2 2]]
numpy.hstack()
numpy.hstack()は、受け取った配列を横軸(axis=1)方向に連結します。引数には配列のシーケンス(リストやタプル)を渡します。この関数は numpy.concatenate() で axis=1 を渡したときと同じ機能をもちます。
# NUMPY_HSTACK # In[1] import numpy as np a1 = np.array([[1, 1]]) a2 = np.array([[2, 2]]) # 1次元配列同士を横軸(axis=1)方向に連結 x = np.hstack((a1, a2)) print(x) # [[1 1 2 2]]
numpy.dstack()
numpy.dstack() は受け取った配列 a1, a2, … を深さ方向に連結します。
numpy.dstack((a1, a2, ...))
深さ方向 (公式ドキュメントでは “depth”) というと何だか曖昧な表現ですが、要するに axis=2 に沿って連結するという意味です。例として、2 次元配列を numpy.dstack() で結合してみます。
# NUMPY_DSTACK # In[1] import numpy as np # 全ての要素が0の3×3配列 a = np.zeros((3, 3)) # 全ての要素が1の3×3配列 b = np.ones((3, 3)) # aとbを深さ(axis=2)方向に連結 stack_ab = np.dstack((a, b)) print(stack_ab) ''' [[[0. 1.] [0. 1.] [0. 1.]] [[0. 1.] [0. 1.] [0. 1.]] [[0. 1.] [0. 1.] [0. 1.]]] '''
上の例では同じ形状の 2 次元配列を下の図のように「重ね合わせて」います。
このように重ね合わせた配列の形状は 3 次元となるので、戻り値は 3 次元配列です。実行結果を見ても、3 次元配列を頭の中でイメージし難いのですが、shape 属性で形状を確かめてみましょう。
# In[2] print(stack_ab.shape) # (3, 3, 2)
3×3 の配列が 2 つ重なっていることがわかります。引数に 1 次元配列を渡すこともできます。試してみましょう。
# In[3] c = [0, 0, 0] d = [1, 1, 1] e = [2, 2, 2] # a,b,cを奥行き方向に連結 stack_cde = np.dstack((c, d, e)) print(stack_cde) # [[[0 1 2] # [0 1 2] # [0 1 2]]]
渡した配列の形状が 1 次元であっても、下図のように縦でも横でもなく、深さ方向に重ねているので、戻り値はやはり 3 次元配列となっています。
numpy.dstack() に渡す配列 a1, a2, … は、それぞれ縦横 (axis=0, axis=1) のサイズが揃っていなければなりませんが、深さ方向 (axis=2) のサイズに関しては制限がありません。たとえば、In[1] で作成した配列 stack_ab に 2 次元配列 a をさらに重ねて、深さ 3 の 3 次元配列を生成することもできます。
# In[4] # stack_abとaを深さ方向に連結 stack_aba = np.dstack((stack_ab, a)) print(stack_aba) ''' [[[0. 1. 0.] [0. 1. 0.] [0. 1. 0.]] [[0. 1. 0.] [0. 1. 0.] [0. 1. 0.]] [[0. 1. 0.] [0. 1. 0.] [0. 1. 0.]]] '''
配列の分割
NumPy の配列を分割する関数の一覧です。
numpy.split()
numpy.split() は受け取った配列を 均等分割 します。
numpy.split(array, indices_or_sections, axis=0)
array には配列オブジェクトを渡します。第 2 引数に整数 N を渡すと、指定した axis (デフォルトは 0) に沿って配列を N 等分します。
# NUMPY_SPLIT # In[1] import numpy as np # 9個の要素をもつ1次元配列を定義 x = np.ones(9, dtype = "int32") # xを3つの配列に分割 y1, y2, y3 = np.split(x, 3) print(y1, y2, y3) # [1 1 1] [1 1 1] [1 1 1]
第 2 引数に分割点インデックスのリストを渡すこともできます。
# In[2] # x = [1 2 3 4 5 6 7 8 9] x = np.arange(1, 10) # xをインデックス3,5の位置で分割 y1, y2, y3 = np.split(x, [3, 5]) print(y1, y2, y3) # [1 2 3] [4 5] [6 7 8 9]
numpy.array_split()
numpy.array_split() は受け取った配列を分割します。
numpy.array_split(array, indices_or_sections, axis=0)
array には配列オブジェクトを渡します。第 2 引数に整数 N を渡すと、指定した axis (デフォルトは 0) に沿って配列を N 等分します。
numpy.split(x, N) は配列 x が N で割り切れないとエラーを返しますが、numpy.array_split(x, N) は、x が N で割り切れないときには、余りの要素数を先頭の配列から順に割り振ります。たとえば、11 個の要素で構成される配列を 3 分割する場合、4 個、4 個、3 個の要素数をもつ配列に分割します。
# NUMPY_ARRAY_SPLIT # In[1] import numpy as np # x = [0 1 2 3 4 5 6 7 8 9 10] x = np.arange(11) # 配列xを3等分 y = np.array_split(x, 3) print(y) # [array([0, 1, 2, 3]), array([4, 5, 6, 7]), array([ 8, 9, 10])]
第 2 引数に分割点インデックスのリストを渡すこともできます。
その場合は numpy.split() と同じはたらきをします。
numpy.vsplit()
numpy.vsplit() は配列を縦軸方向に分割します。
# NUMPY_VSPLIT # In[1] import numpy as np # 乱数を固定 np.random.seed(0) # 1~9の乱数を要素にもつ4×3配列 x = np.random.randint(1, 10, (4, 3)) print("配列x\n{}\n".format(x)) # 縦軸に沿って配列を2分割 y1, y2 = np.vsplit(x, 2) print("配列y1\n{}\n".format(y1)) print("配列y2\n{}".format(y2)) ''' 配列x [[6 1 4] [4 8 4] [6 3 5] [8 7 9] 配列y1 [[6 1 4] [4 8 4]] 配列y2 [[6 3 5] [8 7 9]] '''
numpy.hsplit()
numpy.hsplit() は配列を横軸方向に分割します。
# NUMPY_HSPLIT # In[1] import numpy as np # 2×2の2次元配列 x = np.array([[1, 2], [3, 4]]) # xを2個の配列に水平分割 y1, y2 = np.hsplit(x, 2) print(y1, "\n") print(y2) # [[1] # [3]] # [[2] # [4]
numpy.dsplit()
numpy.dsplit() は受け取った配列を深さ(axis=2)方向に分割します。
numpy.dsplit(ary, indices_or_sections)
第 2 引数に整数 k を渡すと、配列を深さ方向に k 等分します (割り切れなければエラーとなります)。準備として、まずは 2 次元配列を numpy.dstack() で 3 つ重ねてみます。
# NUMPY_DSPLIT # In[1] import numpy as np a = np.zeros((3, 3)) b = np.ones((3, 3)) c = np.full((3, 3), 2) # 配列a,b,cを深さ方向に連結 stack_abc = np.dstack((a, b, c)) print(stack_abc)
今度は逆に numpy.dsplit() を使って、stack_abc を深さ方向に 3 等分してみます。
# In[2] # stack_abcを深さ方向に3等分 d, e, f = np.dsplit(stack_abc, 3)
変数 d に格納された配列を確認してみます。
# In[3] print("配列d:\n{}\n".format(d)) print("配列dの形状:\n{}".format(d.shape)) ''' 配列d: [[[0.] [0.] [0.]] [[0.] [0.] [0.]] [[0.] [0.] [0.]]] 配列dの形状: (3, 3, 1) '''
numpy.dsplit() によって、3×3×1 の 3 次元配列にスライスされていることがわかります (配列 e, f についても確認してみてください)。重ね合わせる前の配列 a より次元が 1 つ増えていますが、numpy.dsplit() の戻り値は必ず 3 次元配列となります。
第 2 引数には配列を切り分ける位置をシーケンスで指定することもできます。stack_abc を 2 つ重ねて深さ 6 の 3 次元配列を作ってみます。
# In[4] # stack_abcを2つ重ねる stack_abcabc = np.dstack((stack_abc, stack_abc)) print(stack_abcabc) ''' [[[0. 1. 2. 0. 1. 2.] [0. 1. 2. 0. 1. 2.] [0. 1. 2. 0. 1. 2.]] [[0. 1. 2. 0. 1. 2.] [0. 1. 2. 0. 1. 2.] [0. 1. 2. 0. 1. 2.]] [[0. 1. 2. 0. 1. 2.] [0. 1. 2. 0. 1. 2.] [0. 1. 2. 0. 1. 2.]]] '''
深さ方向に [1, 3] の位置で切り分けてみます (インデクス 0 と 1 の間、および 3 と 4 の間に切れ目を入れるイメージです)。
# In[5] # stack_abcabcを深さの方向にインデクス1,3の位置でスライス g, h, i = np.dsplit(stack_abcabc, [1, 3])
配列 g の形状を確かめてみます。
# In[6] print("配列g:\n{}\n".format(g)) print("配列gの形状:\n{}".format(g.shape)) ''' 配列g: [[[0.] [0.] [0.]] [[0.] [0.] [0.]] [[0.] [0.] [0.]]] 配列gの形状: (3, 3, 1) '''
g は深さ 1 の 3 次元配列ですね。同様に h についても調べてみましょう。
# In[7] print("配列h:\n{}\n".format(h)) print("配列hの形状:\n{}".format(h.shape)) ''' 配列h: [[[1. 2.] [1. 2.] [1. 2.]] [[1. 2.] [1. 2.] [1. 2.]] [[1. 2.] [1. 2.] [1. 2.]]] 配列hの形状: (3, 3, 2) '''
h は深さ方向にインデクス 0 と 3 の間にある配列なので厚みは 2 となっています。
コメント
下記は誤植と思われますので、ご確認ください。
NUMPY_CONCATENATEプログラムのコメントで、
13行目:1次元配列同士を第1軸(縦軸)方向に連結
→ 2次元配列同士を第0軸(縦軸)方向に連結
16行目:1次元配列同士を第1軸(縦軸)方向に連結
→ 2次元配列同士を第1軸(横軸)方向に連結
ありがとうございます。
直しておきました。m(_ _)m
私は axis=0(縦軸) は 第 0 軸、axis=1(横軸) は 第 1 軸と言ったほうがいいと思ったのですが、以降の説明文の中でも、縦軸は第 1 軸、横軸は第 2 軸、深さは第 3 軸というコメントや説明が出てきますので、図中に第1~3軸の方向を明示して元に戻したほうがいいと思います。第1~3軸という表現をなくして、縦、横、深さという表現だけでもいいかもしれません。
確かに第1軸、第2軸 … という表現は混乱しやすいですね。私も時々混乱します。これはネストの外側から数えて一番目の軸、二番目の軸 … という意味なのですが、引数で指定するときは axis=0, axis=1, … となって、番号がずれているのもややこしいですね。少し悩みましたが、第1軸、第2軸 … のような表現はやめて、縦軸、横軸、あるいは axis=0, axis=1, .. で説明することにしました。色々考えてくださってありがとうございますm(_ _)m
今まで、2次元配列の縦方向は axis = 0、横方向は axix = 1、(2次元配列が2つ重なったイメージの) 3次元配列の深さ方向は axis = 0、縦方向は axis = 1,横方向は axis = 2 と形式的に憶えていたので、だいぶ混乱しました。
下記の基本に戻って、もう一度冷静に記事を読み直してdstack( )を理解し、dsplit( ) でしっかりと3次元配列のイメージが獲得できました。
・配列の形状は ndarray.shape で確認できる。
・配列の次元数は ndarray.shape の要素数である。
・axis は ndarray.shape のインデックスに対応する。
記事を役立てていただけたら嬉しいです。(^_^)
私も ndarray.shape を重宝しています。
新たに配列が生成されたときは常に形状、次元、axis を確認する、その基本姿勢は配列のイメージを描くためにとても大切だと思います。