note.nkmk.me

NumPy配列ndarrayを結合(concatenate, stack, blockなど)

Date: 2018-12-13 / Modified: 2019-06-19 / tags: Python, NumPy

複数の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=0numpy.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=0numpy.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]

nnumpy.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]]
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事