静かなる名辞

pythonとプログラミングのこと


【python】numpy配列の結合方法まとめ

はじめに

 複数のnumpy配列を一つにまとめたい、連結したい、結合したいというシチュエーションはよくあると思います。numpyには配列を結合してまとめるための、様々な方法が存在します。

 色々あるのは嬉しいことですが、「多すぎて覚えきれんわ」と思ったので備忘録としてまとめておきます。

 なお、公式ドキュメントにおける配列の結合関係の記述は、

Array manipulation routines — NumPy v1.17 Manual

 に一覧があり、この記事の最終編集日(2019/09/04)時点で7種類の関数が存在しています。

 また、この記事の内容を理解するにはnumpyのaxisについて知っている必要があります。ご存知でない方は下のリンクなどを参考にしてください。

NumPyの軸(axis)と次元数(ndim)とは何を意味するのか - DeepAge

NumPyでのaxis指定 - Qiita

 目次

スポンサーリンク


結合する方法の一覧

listなどに入れてnumpy配列に変換する

 listなど(tupleとかでも可)に入れてnumpy配列に変換することができます。普通に想像した通りの動作になります。

>>> import numpy as np  # この記事の以下の記述では同様にnumpyをimportしていることを前提とします
>>> a = np.array([1,2])
>>> b = np.array([3,4])
>>> np.array([a,b])
array([[1, 2],
       [3, 4]])
>>> np.array([[a],[b]])
array([[[1, 2]],

       [[3, 4]]])
>>> c = np.array([[1,2],[3,4]])
>>> d = np.array([[5,6],[7,8]])
>>> np.array([c,d])
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

 たとえば画像のlistをnumpy配列として扱いたい……というときなどはこの方法で構わないと思います。

 なお、ndimが揃わないとエラーになります。

>>> np.array([a,c])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not broadcast input array from shape (2,2) into shape (2)

 ndimが同じでshapeが揃わない場合、エラーにこそならないもののobject型の配列という扱いになり、うまく変換できません。

>>> np.array([np.array([1,2]), np.array([1,2,3])])
array([array([1, 2]), array([1, 2, 3])], dtype=object)

numpy.concatenate

 numpy.concatenateはすでに存在するaxis上で配列を結合します。画像を横や縦に並べたりするイメージです。

 第一引数に配列を格納したシーケンス(listやtupleなど)を渡します。第二引数axisはオプションで、デフォルトは0です。第三引数outはdestinationを指定できますが、通常の用途では必要ないでしょう。

numpy.stack — NumPy v1.17 Manual

>>> a = np.array([[1,2],[3,4]])
>>> b = np.array([[5,6],[7,8]])
>>> np.concatenate([a,b])
array([[1, 2],
       [3, 4],
       [5, 6],
       [7, 8]])
>>> np.concatenate([a,b], axis=1)
array([[1, 2, 5, 6],
       [3, 4, 7, 8]])

numpy.stack

 numpy.stackは新しいaxisを作成し、そのaxisで配列を連結します。画像を積み重ねて立体にする感じ……でしょうか。

 引数はnumpy.concatenateと同じです。axisの扱いが異なっており、新しい次元をどの方向に作るかを指定できます。うまく言葉で説明できないので、結果を見てもらった方がわかりやすいと思います。

numpy.stack — NumPy v1.17 Manual

>>> np.stack([a,b], axis=0)
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])
>>> np.stack([a,b], axis=1)
array([[[1, 2],
        [5, 6]],

       [[3, 4],
        [7, 8]]])
>>> np.stack([a,b], axis=2)
array([[[1, 5],
        [2, 6]],

       [[3, 7],
        [4, 8]]])

 ……なんとなくわかるような、わからないようなって感じかな。なお、axis=0の場合は、listに入れてarrayに変換するのと同じです。

numpy.column_stack

 numpy.column_stackは配列を列方向に積み重ねます。引数は一次元ないし二次元の配列のシーケンスです。concatenate, stackと比べると汎用性の低い関数です。

numpy.column_stack — NumPy v1.17 Manual

 一次元配列の場合はこのような動作になります。

>>> a = np.array([1,2,3])
>>> b = np.array([4,5,6])
>>> np.column_stack([a,b])
array([[1, 4],
       [2, 5],
       [3, 6]])

 二次元配列では、また挙動が異なります。

>>> a = np.array([[1,2,3]])
>>> b = np.array([[4,5,6]])
>>> np.column_stack([a,b])
array([[1, 2, 3, 4, 5, 6]])

 このようにあくまでも二次元配列の列方向に向かってスタックします。また、三次元以上の配列に対して適用することはできません。

numpy.vstack

 numpy.vstackはnumpy配列を垂直方向に結合します。使いやすいので、使った記憶がある人も多いと思います。

numpy.vstack — NumPy v1.17 Manual

 一次元配列は(1, N)にreshapeしてから結合するような挙動になります。

>>> a = np.array([1,2,3])
>>> b = np.array([4,5,6])
>>> np.vstack([a,b])
array([[1, 2, 3],
       [4, 5, 6]])

 二次元以上であればaxis=0での結合です。

>>> a = np.array([[1,2,3]])
>>> b = np.array([[4,5,6]])
>>> np.vstack([a,b])
array([[1, 2, 3],
       [4, 5, 6]])

numpy.hstack

 numpy.hstackはnumpy配列を水平方向に結合します。これもvstackと並んでよく使われる関数だと思います。

numpy.hstack — NumPy v1.17 Manual

 一次元配列の場合はaxis=0で結合されます。二次元以上であれば、axis=1で結合されます。何次元の配列でも使えるようですが、あまりndimが深い配列をこれで操作することもないでしょう。

>>> a = np.array([1,2,3])
>>> b = np.array([4,5,6])
>>> np.hstack([a,b])
array([1, 2, 3, 4, 5, 6])
>>> a = np.array([[1],[2],[3]])
>>> b = np.array([[4],[5],[6]])
>>> np.hstack([a,b])
array([[1, 4],
       [2, 5],
       [3, 6]])

numpy.dstack

 numpy.dstackはnumpy配列を深さ方向に結合します。要はaxis=2の結合であり、axis=0で結合するvstack、axis=1で結合するhstackに続いています。

numpy.dstack — NumPy v1.17 Manual

 直感的にわかりづらいと思うので、実例を示します。

 一次元配列のlistを渡した場合。

>>> a = np.array([1,2,3])
>>> b = np.array([4,5,6])
>>> np.dstack([a,b])
array([[[1, 4],
        [2, 5],
        [3, 6]]])

 なんだかよくわからないのでそのまま二次元配列にしてみます。

>>> a = np.array([[1,2],[3,4]])
>>> b = np.array([[5,6],[7,8]])
>>> np.dstack([a,b])
array([[[1, 5],
        [2, 6]],

       [[3, 7],
        [4, 8]]])

 まだわからないので3次元に。

>>> a = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>>> b = np.array([[[9,10],[11,12]],[[13,14],[15,16]]])
>>> np.dstack([a,b])
array([[[ 1,  2,  9, 10],
        [ 3,  4, 11, 12]],

       [[ 5,  6, 13, 14],
        [ 7,  8, 15, 16]]])

 あー、なるほど、なんとなくはわかった。

 dstackはvstack, hstackと比較して、使う機会は少ないと思います。

numpy.block

 これは今までの関数とは少し毛色が違います。これを使うことで、垂直に結合してから水平に結合……といった複雑な操作を一発で簡単に書くことができます(たとえば四枚の画像を縦横に並べるときなど)。挙動としては、numpy配列を入れた多重リストを結合します。

numpy.block — NumPy v1.17 Manual

 最も内側のlistはaxis=-1で結合され、その外側はaxis=-2で……と再帰的に結合される仕様になっています。

>>> a = np.array([[1,2],[3,4]])
>>> b = np.array([[5,6],[7,8]])
>>> c = np.array([[9,10],[11,12]])
>>> d = np.array([[13,14],[15,16]])
>>> np.block([[a,b],[c,d]])
array([[ 1,  2,  5,  6],
       [ 3,  4,  7,  8],
       [ 9, 10, 13, 14],
       [11, 12, 15, 16]])

どれを覚えれば良いのか

 特に覚えておく必要があるのは、

  • listに入れて配列に変換する方法
  • すでに存在するaxisで結合するnumpy.concatenate
  • 新しくaxisを作って結合するnumpy.stack
  • ネストされた配列のlistを結合するnumpy.block

 あたりで、他は簡略記法としてvstack, hstackを覚えておけば大抵の状況には対応できると思います。

 column_stackとdstackは使っているコードをほとんど見たことがないので、忘れても構いませんが、そういうものがあるということは頭の片隅に入れておくと良いと思います。

まとめ

 numpy配列の結合方法についてまとめてみました。

 一見すると関数がたくさんあって複雑なようにも思いますが、存在するaxisで結合するconcatenateとaxisを増やして結合するstackの違いさえ覚えてしまえば、それほど不思議なことはないようにも思います。

 これでnumpy配列を自由自在に結合できるようになると思います。