note.nkmk.me

NumPy配列ndarrayを分割(split, array_split, hsplit, vsplit, dsplit)

Date: 2018-12-13 / tags: Python, NumPy

NumPy配列ndarrayを分割するためには以下の関数を使う。

  • numpy.split(): 等分割、または、任意の位置で分割
  • numpy.array_split(): できるだけ等分割で分割
  • numpy.vsplit(): 縦に分割
  • numpy.hsplit(): 横に分割
  • numpy.dsplit(): 深さ方向に分割

numpy.split()が基本で、あとのものはnumpy.split()の引数設定を省略して便利に使えるようにしたもの。numpy.split()の使い方を理解しておけば他のものも理解しやすい。

ここでは以下の内容について説明する。

  • numpy.split()の基本的な使い方
    • 戻り値は配列ndarrayのリスト
    • 分割個数または分割位置を指定: 引数indices_or_sections
    • 分割する軸(次元)を指定: 引数axis
    • 3次元以上の多次元配列の場合の例
  • numpy.array_split()でできるだけ等分割で分割
  • numpy.vsplit()で縦に分割
  • numpy.hsplit()で横に分割
  • numpy.dsplit()で深さ方向に分割

「縦に分割」「横に分割」という表現は曖昧だが、ここではNumPyの公式ドキュメントに則り、上下に分割することを縦に分割、左右に分割することを横に分割と表す。

分割ではなく結合(連結)については以下の記事を参照。

スポンサーリンク

numpy.split()の基本的な使い方

配列ndarrayを分割する基本的な関数がnumpy.split()

以下の2次元配列を例とする。

import numpy as np

a = np.arange(16).reshape(4, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

戻り値は配列ndarrayのリスト

第一引数に分割する配列ndarray、第二引数・第三引数に分割方法を指定する。

縦に2等分で分割する例は以下の通り。第二引数を2とし、第三引数を省略する(詳細は後述)。

戻り値は配列ndarrayのリストとなる。元の配列は変更されない。

a_split = np.split(a, 2)

print(type(a_split))
# <class 'list'>

print(len(a_split))
# 2

print(a_split[0])
# [[0 1 2 3]
#  [4 5 6 7]]

print(a_split[1])
# [[ 8  9 10 11]
#  [12 13 14 15]]

print(type(a_split[0]))
# <class 'numpy.ndarray'>

print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

アンパックを利用して以下のように別々の変数に格納することも可能。

a0, a1 = np.split(a, 2)

print(a0)
# [[0 1 2 3]
#  [4 5 6 7]]

print(a1)
# [[ 8  9 10 11]
#  [12 13 14 15]]

分割個数または分割位置を指定: 引数indices_or_sections

分割個数を整数で指定

上の例のように、第二引数indices_or_sectionsに整数intを指定するとその個数で等分割される。割り切れない値を指定するとエラーとなる。

a0, a1 = np.split(a, 2)

print(a0)
# [[0 1 2 3]
#  [4 5 6 7]]

print(a1)
# [[ 8  9 10 11]
#  [12 13 14 15]]

# np.split(a, 3)
# ValueError: array split does not result in an equal division

後述のnumpy.array_split()を使うと、割り切れない場合に適当に行数や列数を調整してくれる。

位置をリスト(配列)で指定

第二引数indices_or_sectionsにリスト(配列)を指定すると、その位置で分割される。位置は0始まりで指定。

例えば[1, 3]とすると、1行目の前(0行目と1行目の間)と3行目の前(2行目と3行目の間)で分割される。

a0, a1, a2 = np.split(a, [1, 3])

print(a0)
# [[0 1 2 3]]

print(a1)
# [[ 4  5  6  7]
#  [ 8  9 10 11]]

print(a2)
# [[12 13 14 15]]

任意の位置で2分割したい場合は要素数が1のリストで指定すればよい。[]を忘れないように注意。

a0, a1 = np.split(a, [1])

print(a0)
# [[0 1 2 3]]

print(a1)
# [[ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

分割する軸(次元)を指定: 引数axis

分割する軸(次元)は第三引数axisで指定する。

これまでの例のように第三引数を省略するとデフォルトのaxis=0となる(もちろんaxis=0と明示的に指定してもOK)。この場合、0次元目の軸に沿って、すなわち、行ごとに分割される。

a0, a1 = np.split(a, 2, 0)

print(a0)
# [[0 1 2 3]
#  [4 5 6 7]]

print(a1)
# [[ 8  9 10 11]
#  [12 13 14 15]]

axis=1とすると、1次元目の軸に沿って、つまり、列ごとに分割される。

a0, a1 = np.split(a, 2, 1)

print(a0)
# [[ 0  1]
#  [ 4  5]
#  [ 8  9]
#  [12 13]]

print(a1)
# [[ 2  3]
#  [ 6  7]
#  [10 11]
#  [14 15]]

存在しない次元を指定するとエラーとなる。

# np.split(a, 2, 2)
# IndexError: tuple index out of range

3次元以上の多次元配列の場合の例

ここまでは、便宜上、行・列といった文言で説明したが、3次元以上の場合も考え方は同じ。以下の配列を例とする。

a_3d = np.arange(24).reshape(2, 3, 4)
print(a_3d)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]
# 
#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

print(a_3d.shape)
# (2, 3, 4)

第三引数axisに分割する対象の軸(次元)を指定し、第二引数indices_or_sectionsに整数またはリストで分割個数や位置を指定する。

a0, a1 = np.split(a_3d, 2, 0)

print(a0)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]]

print(a1)
# [[[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

a0, a1 = np.split(a_3d, [1], 2)

print(a0)
# [[[ 0]
#   [ 4]
#   [ 8]]
# 
#  [[12]
#   [16]
#   [20]]]

print(a1)
# [[[ 1  2  3]
#   [ 5  6  7]
#   [ 9 10 11]]
# 
#  [[13 14 15]
#   [17 18 19]
#   [21 22 23]]]

numpy.array_split()でできるだけ等分割で分割

numpy.split()で第二引数indices_or_sectionsに整数で分割個数を指定する場合は割り切れないとエラーになるが、numpy.array_split()だと割り切れないときに適当に行数や列数を調整してくれる。それ以外はnumpy.split()と同じ動作。引数の設定なども同じ。

以下の配列を例とする。

a = np.arange(15).reshape(3, 5)
print(a)
# [[ 0  1  2  3  4]
#  [ 5  6  7  8  9]
#  [10 11 12 13 14]]

numpy.split()だと割り切れない(等分割できない)場合にエラーとなる。

# np.split(a, 2, 0)
# ValueError: array split does not result in an equal division

numpy.array_split()だとエラーにならずに適当に調整してくれる。この例だと先頭の配列の行数を一つ増やして分割される。

a0, a1 = np.array_split(a, 2, 0)

print(a0)
# [[0 1 2 3 4]
#  [5 6 7 8 9]]

print(a1)
# [[10 11 12 13 14]]

行数・列数は以下のルールに従って調整される。

For an array of length l that should be split into n sections, it returns l % n sub-arrays of size l//n + 1 and the rest of size l//n.
numpy.array_split — NumPy v1.15 Manual

余り分を分割後の配列の先頭から振り分けていくイメージ。以下の例では先頭2つの配列の列数を一つ増やして分割される。

a0, a1, a2 = np.array_split(a, 3, 1)

print(a0)
# [[ 0  1]
#  [ 5  6]
#  [10 11]]

print(a1)
# [[ 2  3]
#  [ 7  8]
#  [12 13]]

print(a2)
# [[ 4]
#  [ 9]
#  [14]]

numpy.vsplit()で縦に分割

numpy.vsplit()は配列を縦(vertical)に分割する関数。

動作は第三引数axis=0としたnumpy.split()と同じ。

a = np.arange(16).reshape(4, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

a0, a1 = np.vsplit(a, 2)

print(a0)
# [[0 1 2 3]
#  [4 5 6 7]]

print(a1)
# [[ 8  9 10 11]
#  [12 13 14 15]]

内部ではnumpy.split()を呼んでいるので、第二引数indices_or_sectionsに整数で分割個数を指定する場合、割り切れないとエラーになる。

第二引数indices_or_sectionsにリスト(配列)を指定して位置で分割することも可能。

a0, a1 = np.split(a, [1])

print(a0)
# [[0 1 2 3]]

print(a1)
# [[ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

numpy.hsplit()で横に分割

numpy.hsplit()は配列を横(horizontal)に分割する関数。

動作は第三引数axis=1としたnumpy.split()と同じ。

a = np.arange(16).reshape(4, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

a0, a1 = np.hsplit(a, 2)

print(a0)
# [[ 0  1]
#  [ 4  5]
#  [ 8  9]
#  [12 13]]

print(a1)
# [[ 2  3]
#  [ 6  7]
#  [10 11]
#  [14 15]]

内部ではnumpy.split()を呼んでいるので、第二引数indices_or_sectionsに整数で分割個数を指定する場合、割り切れないとエラーになる。

第二引数indices_or_sectionsにリスト(配列)を指定して位置で分割することも可能。

a0, a1 = np.hsplit(a, [1])

print(a0)
# [[ 0]
#  [ 4]
#  [ 8]
#  [12]]

print(a1)
# [[ 1  2  3]
#  [ 5  6  7]
#  [ 9 10 11]
#  [13 14 15]]

axis=1としたnumpy.split()と違い、一次元配列に対してもエラーにならない。

a_1d = np.arange(6)
print(a_1d)
# [0 1 2 3 4 5]

# np.split(a_1d, 2, 1)
# IndexError: tuple index out of range

a0, a1 = np.hsplit(a_1d, 2)

print(a0)
# [0 1 2]

print(a1)
# [3 4 5]

これは一次元配列に対してはaxis=0としてnumpy.split()を呼んでいるから。以下のソースを参照。

numpy.dsplit()で深さ方向に分割

numpy.dsplit()は配列を深さ(depth)方向に分割する関数。

動作は第三引数axis=2としたnumpy.split()と同じ。print()の出力を見るとイメージしにくいかもしれないが、形状 shapeを見ると2次元目で分割されていることが確認できる。

import numpy as np

a_3d = np.arange(24).reshape(2, 3, 4)
print(a_3d)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]
# 
#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

print(a_3d.shape)
# (2, 3, 4)

a0, a1 = np.dsplit(a_3d, 2)

print(a0)
# [[[ 0  1]
#   [ 4  5]
#   [ 8  9]]
# 
#  [[12 13]
#   [16 17]
#   [20 21]]]

print(a0.shape)
# (2, 3, 2)

print(a1)
# [[[ 2  3]
#   [ 6  7]
#   [10 11]]
# 
#  [[14 15]
#   [18 19]
#   [22 23]]]

print(a1.shape)
# (2, 3, 2)

内部ではnumpy.split()を呼んでいるので、第二引数indices_or_sectionsに整数で分割個数を指定する場合、割り切れないとエラーになる。

第二引数indices_or_sectionsにリスト(配列)を指定することも可能。

a0, a1 = np.dsplit(a_3d, [1])

print(a0)
# [[[ 0]
#   [ 4]
#   [ 8]]
# 
#  [[12]
#   [16]
#   [20]]]

print(a1)
# [[[ 1  2  3]
#   [ 5  6  7]
#   [ 9 10 11]]
# 
#  [[13 14 15]
#   [17 18 19]
#   [21 22 23]]]

numpy.dsplit()は3次元以上の配列が対象。2次元以下の配列に対してはエラーとなる。

a = np.arange(16).reshape(4, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

# np.dsplit(a, 2)
# ValueError: dsplit only works on arrays of 3 or more dimensions
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事