NumPy配列ndarrayを結合(concatenate, stack, blockなど)
複数のNumPy配列ndarray
を結合(連結)するためには様々な関数がある。ここでは以下の内容について説明する。
numpy.concatenate()
の基本的な使い方- 結合する配列
ndarray
のリストを指定 - 結合する軸(次元)を指定: 引数
axis
- 結合する配列
numpy.stack()
で新たな軸(次元)に沿って結合numpy.block()
で配置を指定して結合numpy.vstack()
で縦に結合numpy.hstack()
で横に結合numpy.dstack()
で深さ方向に結合
既存の軸(次元)に沿って結合するのがnumpy.concatenate()
で、新たな軸に沿って結合するのがnumpy.stack()
。例えば、2次元配列を縦横に結合するのがnumpy.concatenate()
で、2次元配列を重ねて3次元配列を生成するのがnumpy.stack()
となる。
基本的にはnumpy.concatenate()
とnumpy.stack()
で対応できるが、特に2次元配列に対してはnumpy.block()
やnumpy.vstack()
, numpy.hstack()
を覚えておくと便利。
そのほかnumpy.r_[]
やnumpy.c_[]
といった方法もあるが、癖があるのでここでは触れない。詳細は以下の公式ドキュメントを参照。
結合ではなく分割については以下の記事を参照。
また、ndarray
の任意の位置に追加(挿入)する場合は以下の記事を参照。
numpy.concatenate()の基本的な使い方
複数の配列ndarray
を結合する基本的な関数がnumpy.concatenate()
。既存の軸(次元)に沿って結合する。
結合する配列ndarrayのリストを指定
以下の配列を例とする。
import numpy as np
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full((2, 3), 2)
print(a2)
# [[2 2 2]
# [2 2 2]]
numpy.ones()
やnumpy.full()
については以下の記事を参照。
第一引数に結合したいndarray
のリストを指定する。タプルでもOK。
print(np.concatenate([a1, a2]))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]]
3つ以上でも考え方は同じ。第一引数に指定するリストやタプルの要素数を増やせばよい。
a3 = np.full((2, 3), 3)
print(a3)
# [[3 3 3]
# [3 3 3]]
print(np.concatenate([a1, a2, a3]))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]
# [3 3 3]
# [3 3 3]]
結合する軸(次元)を指定: 引数axis
第二引数axis
に結合する軸(次元)を0始まりで指定する。
デフォルトはaxis=0
。2次元配列の場合は最初の軸(0次元目)が行なので縦に結合される。
print(np.concatenate([a1, a2], 0))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]]
axis=1
とすると2番目の軸(1次元目)が列なので横に結合される。
print(np.concatenate([a1, a2], 1))
# [[1 1 1 2 2 2]
# [1 1 1 2 2 2]]
存在しない軸(次元)を指定するとエラーとなる。新たな軸に対して結合したい、例えば2次元配列を重ねて3次元配列を生成したいというような場合は後述のnumpy.stack()
などを使う。
# print(np.concatenate([a1, a2], 2))
# AxisError: axis 2 is out of bounds for array of dimension 2
結合軸以外の軸のサイズが一致していないとエラーとなる。足りない部分がNaN
で埋められたりはしない。
a2_ = np.full((3, 3), 2)
print(a2_)
# [[2 2 2]
# [2 2 2]
# [2 2 2]]
print(np.concatenate([a1, a2_], 0))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]
# [2 2 2]]
# print(np.concatenate([a1, a2_], 1))
# ValueError: all the input array dimensions except for the concatenation axis must match exactly
2次元以外の配列でも同様。第二引数axis
に軸(次元)を指定する。元の配列に存在しない軸では結合できない。
a1 = np.ones(3, int)
print(a1)
# [1 1 1]
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
print(np.concatenate([a1, a2], 0))
# [1 1 1 2 2 2]
# print(np.concatenate([a1, a2], 1))
# AxisError: axis 1 is out of bounds for array of dimension 1
また、元の配列の次元数が異なっている場合はエラーとなる。後述のnumpy.block()
やnumpy.vstack()
を使うと、1次元配列と2次元配列の縦結合が可能。
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
# print(np.concatenate([a1, a2], 0))
# ValueError: all the input arrays must have same number of dimensions
numpy.stack()で新たな軸(次元)に沿って結合
既存の軸(次元)に沿って結合するnumpy.concatenate()
に対して、新たな軸に沿って結合する関数がnumpy.stack()
。新たな軸に沿って配列を積み重ねる(=stackする)イメージ。
第一引数に連結する配列ndarray
のリスト、第二引数axis
に結合する軸(次元)を0始まりで指定するのはnumpy.concatenate()
と同じ。ただし、axis
は結合された配列にとっての軸を指定する。
1次元配列の例
1次元配列を例とする。
a1 = np.ones(3, int)
print(a1)
# [1 1 1]
print(a1.shape)
# (3,)
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
print(a2.shape)
# (3,)
numpy.stack()
は新たな軸で結合するので、結果の配列は1次元多い2次元配列となる。
第二引数axis
のデフォルトはaxis=0
。結果の配列の0次元目(行)に沿って結合される。
print(np.stack([a1, a2]))
# [[1 1 1]
# [2 2 2]]
print(np.stack([a1, a2], 0))
# [[1 1 1]
# [2 2 2]]
print(np.stack([a1, a2], 0).shape)
# (2, 3)
axis=1
とすると以下の通り。
print(np.stack([a1, a2], 1))
# [[1 2]
# [1 2]
# [1 2]]
print(np.stack([a1, a2], 1).shape)
# (3, 2)
結果の配列に存在しない軸を指定するとエラーとなる。
# print(np.stack([a1, a2], 2))
# AxisError: axis 2 is out of bounds for array of dimension 2
axis=-1
とすると最後の軸が指定される。この場合はaxis=1
と等価。
print(np.stack([a1, a2], -1))
# [[1 2]
# [1 2]
# [1 2]]
print(np.stack([a1, a2], -1).shape)
# (3, 2)
次元数ndim
が同じでも形状shape
が異なっているとエラーとなる。
a2_ = np.full(4, 2)
print(a2_)
# [2 2 2 2]
print(a2_.shape)
# (4,)
# print(np.stack([a1, a2]))
# ValueError: all input arrays must have the same shape
2次元配列の例
続いて2次元配列を例とする。
a1 = np.ones((3, 4), int)
print(a1)
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
print(a1.shape)
# (3, 4)
a2 = np.full((3, 4), 2)
print(a2)
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]
print(a2.shape)
# (3, 4)
考え方は1次元配列と同様。結果は2次元 + 1次元で3次元配列となる。結果の形状shape
を意識すると理解しやすい。
print(np.stack([a1, a2]))
# [[[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
#
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]]
print(np.stack([a1, a2], 0))
# [[[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
#
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]]
print(np.stack([a1, a2], 0).shape)
# (2, 3, 4)
print(np.stack([a1, a2], 1))
# [[[1 1 1 1]
# [2 2 2 2]]
#
# [[1 1 1 1]
# [2 2 2 2]]
#
# [[1 1 1 1]
# [2 2 2 2]]]
print(np.stack([a1, a2], 1).shape)
# (3, 2, 4)
print(np.stack([a1, a2], 1)[:, 0, :])
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
print(np.stack([a1, a2], 1)[:, 1, :])
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]
print(np.stack([a1, a2], 2))
# [[[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]]
print(np.stack([a1, a2], 2).shape)
# (3, 4, 2)
print(np.stack([a1, a2], 2)[:, :, 0])
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
print(np.stack([a1, a2], 2)[:, :, 1])
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]
結果の配列に存在しない軸を指定するとエラーとなる。
# print(np.stack([a1, a2], 3))
# AxisError: axis 3 is out of bounds for array of dimension 3
axis=-1
とすると最後の軸が指定される。この場合はaxis=2
と等価。
print(np.stack([a1, a2], -1))
# [[[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]]
print(np.stack([a1, a2], -1).shape)
# (3, 4, 2)
次元数ndim
が同じでも形状shape
が異なっているとエラーとなる。
a2_ = np.full((2, 3), 2)
print(a2_)
# [[2 2 2]
# [2 2 2]]
# print(np.stack([a1, a2_]))
# ValueError: all input arrays must have the same shape
3次元以上のさらに多次元の場合も同様の考え方。
numpy.block()で配置を指定して結合
より直感的に複数の配列を結合できるのがnumpy.block()
。
元の配列をどのように配置するかを示したリストを引数に指定する。
配列を横に並べたリストを指定するとその順番で横に結合される。
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full((2, 3), 2)
print(a2)
# [[2 2 2]
# [2 2 2]]
print(np.block([a1, a2]))
# [[1 1 1 2 2 2]
# [1 1 1 2 2 2]]
2次元のリスト(ネストしたリスト)で縦方向の配置を指定すればそのとおりに結合される。
print(np.block([[a1], [a2]]))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]]
print(np.block([[a1, a2], [a2, a1]]))
# [[1 1 1 2 2 2]
# [1 1 1 2 2 2]
# [2 2 2 1 1 1]
# [2 2 2 1 1 1]]
引数を3次元にすれば結果も3次元になる。
print(np.block([[[a1]], [[a2]]]))
# [[[1 1 1]
# [1 1 1]]
#
# [[2 2 2]
# [2 2 2]]]
print(np.block([[[a1]], [[a2]]]).shape)
# (2, 2, 3)
以下のように1次元配列と2次元配列を結合することも可能。
a3 = np.full(6, 3)
print(a3)
# [3 3 3 3 3 3]
print(np.block([[a1, a2], [a3]]))
# [[1 1 1 2 2 2]
# [1 1 1 2 2 2]
# [3 3 3 3 3 3]]
[]
で次元を揃えないとエラーとなるので注意。
# print(np.block([[a1, a2], a3]))
# ValueError: List depths are mismatched. First element was at depth 2, but there is an element at depth 1 (arrays[1])
また、要素数に過不足がある場合もエラーとなる。
# print(np.block([[a1, a2, a3]]))
# ValueError: all the input array dimensions except for the concatenation axis must match exactly
numpy.vstack()で縦に結合
numpy.vstack()
は配列を縦(vertical)に結合する関数。
基本的にはaxis=0
としたnumpy.concatenate()
と同等(numpy/shape_base.py at v1.15.4 · numpy/numpy)。
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full((2, 3), 2)
print(a2)
# [[2 2 2]
# [2 2 2]]
print(np.vstack([a1, a2]))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]]
print(np.concatenate([a1, a2], 0))
# [[1 1 1]
# [1 1 1]
# [2 2 2]
# [2 2 2]]
1次元配列に対しては各配列を2次元に拡張してからaxis=0
のnumpy.concatenate()
が呼ばれるため、結果は2次元配列となる。動作としてはaxis=0
としたnumpy.stack()
と同等。
a1 = np.ones(3, int)
print(a1)
# [1 1 1]
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
print(np.vstack([a1, a2]))
# [[1 1 1]
# [2 2 2]]
print(np.stack([a1, a2], 0))
# [[1 1 1]
# [2 2 2]]
1次元配列が2次元に拡張されるので、2次元配列と1次元配列も結合できる。
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
print(np.vstack([a1, a2]))
# [[1 1 1]
# [1 1 1]
# [2 2 2]]
numpy.hstack()で横に結合
numpy.hstack()
は配列を横(horizontal)に結合する関数。
基本的にはaxis=1
としたnumpy.concatenate()
と同等(numpy/shape_base.py at v1.15.4 · numpy/numpy)。
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full((2, 3), 2)
print(a2)
# [[2 2 2]
# [2 2 2]]
print(np.hstack([a1, a2]))
# [[1 1 1 2 2 2]
# [1 1 1 2 2 2]]
print(np.concatenate([a1, a2], 1))
# [[1 1 1 2 2 2]
# [1 1 1 2 2 2]]
1次元配列に対してはaxis=0
のnumpy.concatenate()
が呼ばれる。
a1 = np.ones(3, int)
print(a1)
# [1 1 1]
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
print(np.hstack([a1, a2]))
# [1 1 1 2 2 2]
print(np.concatenate([a1, a2], 0))
# [1 1 1 2 2 2]
numpy.hstack()
ではnumpy.vstack()
のように1次元配列が2次元配列に拡張されたりしないので、2次元配列と1次元配列の組み合わせは結合できない。
a1 = np.ones((2, 3), int)
print(a1)
# [[1 1 1]
# [1 1 1]]
a2 = np.full(2, 2)
print(a2)
# [2 2]
# print(np.hstack([a1, a2]))
# ValueError: all the input arrays must have same number of dimensions
numpy.dstack()で深さ方向に結合
numpy.dstack()
は配列を深さ(depth)方向に結合する関数。
基本的にはaxis=2
としたnumpy.concatenate()
と同等(numpy/shape_base.py at v1.15.4 · numpy/numpy)。
2次元以下の配列は3次元に拡張されてから結合される((M, N) -> (M, N, 1)
)。
a1 = np.ones((3, 4), int)
print(a1)
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
a2 = np.full((3, 4), 2)
print(a2)
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]
print(np.dstack([a1, a2]))
# [[[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]]
print(np.dstack([a1, a2]).shape)
# (3, 4, 2)
print(np.dstack([a1, a2])[:, :, 0])
# [[1 1 1 1]
# [1 1 1 1]
# [1 1 1 1]]
print(np.dstack([a1, a2])[:, :, 1])
# [[2 2 2 2]
# [2 2 2 2]
# [2 2 2 2]]
以下のnumpy.concatenate()
と同等。
print(np.concatenate([a1.reshape(3, 4, 1), a2.reshape(3, 4, 1)], 2))
# [[[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]
#
# [[1 2]
# [1 2]
# [1 2]
# [1 2]]]
1次元配列も3次元に拡張される((N, ) -> (1, N, 1
)。
a1 = np.ones(3, int)
print(a1)
# [1 1 1]
a2 = np.full(3, 2)
print(a2)
# [2 2 2]
print(np.dstack([a1, a2]))
# [[[1 2]
# [1 2]
# [1 2]]]
print(np.dstack([a1, a2]).shape)
# (1, 3, 2)
print(np.dstack([a1, a2])[:, :, 0])
# [[1 1 1]]
print(np.dstack([a1, a2])[:, :, 1])
# [[2 2 2]]