NumPy配列ndarrayのスライスによる部分配列の選択と代入

Pythonではコロンを使って表すスライス[start:stop:step]によって、リストや文字列、タプルなどのシーケンスオブジェクトの一部分を選択して取得したり別の値を代入したりできます。 NumPy配列ndarrayに対してもスライスで部分配列を選択して抽出したり別の値を代入したりすることが可能。 以下の内容について説明します。

スライスの基本 一次元のNumPy配列ndarrayにおけるスライス 多次元のNumPy配列ndarrayにおけるスライス ビュー(参照)とコピー ファンシーインデックス(リストによる選択)との組み合わせ

NumPy配列ndarrayの部分配列を選択する方法としてはリストで指定するファンシーインデックスもあります。以下の記事を参照。

また、条件を満たす行・列を抽出したい場合は以下の記事を参照。

スライスの基本

Pythonではコロンを使って表すスライス[start:stop:step]によって、リストや文字列、タプルなどのシーケンスオブジェクトの一部分を選択して取得したり別の値を代入したりできます。

import numpy as np

l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(l[4:8])
# [4, 5, 6, 7]

print(l[-5:-2])
# [5, 6, 7]

print(l[::-1])
# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

スライスはPython標準の機能。詳細は以下の記事を参照。

一次元のNumPy配列ndarrayにおけるスライス

選択 一次元のNumPy配列numpy.ndarrayをスライスで選択する場合は、上述のPythonの基本的なスライスと同じ。

a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]

print(a[4:8])
# [4 5 6 7]

print(a[-5:-2])
# [5 6 7]

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

代入 スライスを使った代入の振る舞いはPythonのリスト(list型)とNumPy配列numpy.ndarrayで異なる。 Pythonのリストに対するスライスでの代入は以下の記事を参照。スライスで選択した要素数と代入する要素数は一致していなくても問題ありません。

numpy.ndarrayでは右辺の値がブロードキャストされて代入される。 すなわち、右辺がスカラー値であればスライスで選択された要素がすべてそのスカラー値で置き換えられ、一次元の配列であればそのまま代入される。

a[3:6] = 100
print(a)
# [  0   1   2 100 100 100   6   7   8   9]

a[3:6] = [100, 200, 300]
print(a)
# [  0   1   2 100 200 300   6   7   8   9]

配列を代入する場合は代入する配列の要素数とスライスで選択された要素数が一致していないとエラーValueErrorになるので注意。

# a[3:6] = [100, 200, 300, 400]
# ValueError: cannot copy sequence with size 4 to array axis with dimension 3

stepを指定したスライスによる飛び飛びの値への代入でも同様。

a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]

print(a[2:8:2])
# [2 4 6]

a[2:8:2] = 100
print(a)
# [  0   1 100   3 100   5 100   7   8   9]

a[2:8:2] = [100, 200, 300]
print(a)
# [  0   1 100   3 200   5 300   7   8   9]

多次元のNumPy配列ndarrayにおけるスライス

多次元のNumPy配列ndarrayに対しては各次元のスライスをカンマで区切って指定できます。 以下の二次元配列を例とします。

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

選択 各次元のスライスをカンマで区切って指定します。

print(a[1:, 1:3])
# [[ 5  6]
#  [ 9 10]]

行の選択 全体を表すスライス:を使うと行を選択できます。この場合、後ろの, :は省略できます。

print(a[1:, :])
# [[ 4  5  6  7]
#  [ 8  9 10 11]]

print(a[1:])
# [[ 4  5  6  7]
#  [ 8  9 10 11]]

1行を選択する場合、スライスではなくスカラー値でインデックスを指定すると一次元配列となるが、スライスで1行分を選択すると元の配列の形状と同じ二次元配列となります。

print(a[1])
# [4 5 6 7]

print(a[1].shape)
# (4,)

print(a[1:2])
# [[4 5 6 7]]

print(a[1:2].shape)
# (1, 4)

行列演算など形状が重要な場合は注意。 列の選択 列の選択も同様。この場合、はじめの:は省略できない。

print(a[:, 1:3])
# [[ 1  2]
#  [ 5  6]
#  [ 9 10]]

行と同様に、1列を選択する場合、スライスではなくスカラー値でインデックスを指定すると一次元配列となるが、スライスで1列分を選択すると元の配列の形状と同じ二次元配列となります。

print(a[:, 1])
# [1 5 9]

print(a[:, 1].shape)
# (3,)

print(a[:, 1:2])
# [[1]
#  [5]
#  [9]]

print(a[:, 1:2].shape)
# (3, 1)

複数の:が繰り返す場合は...を使うことができます。以下の記事を参照。

代入 多次元のNumPy配列ndarrayに対する代入も一次元の場合と同様に右辺の値がブロードキャストされて代入される。 配列を代入する場合は代入する配列の要素数とスライスで選択された領域の対応する要素数が一致していないとエラーValueErrorになるので注意。

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

print(a[1:, 1:3])
# [[ 5  6]
#  [ 9 10]]

a[1:, 1:3] = 100
print(a)
# [[  0   1   2   3]
#  [  4 100 100   7]
#  [  8 100 100  11]]

a[1:, 1:3] = [100, 200]
print(a)
# [[  0   1   2   3]
#  [  4 100 200   7]
#  [  8 100 200  11]]

a[1:, 1:3] = [[100, 200], [300, 400]]
print(a)
# [[  0   1   2   3]
#  [  4 100 200   7]
#  [  8 300 400  11]]

stepを指定したスライスによる飛び飛びの値への代入でも同様。

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

print(a[1:, ::2])
# [[ 4  6]
#  [ 8 10]]

a[1:, ::2] = 100
print(a)
# [[  0   1   2   3]
#  [100   5 100   7]
#  [100   9 100  11]]

a[1:, ::2] = [100, 200]
print(a)
# [[  0   1   2   3]
#  [100   5 200   7]
#  [100   9 200  11]]

a[1:, ::2] = [[100, 200], [300, 400]]
print(a)
# [[  0   1   2   3]
#  [100   5 200   7]
#  [300   9 400  11]]

ビュー(参照)とコピー

スライスで抽出した部分配列は元の配列のビュー(参照)であり、部分配列の要素を変更すると元の配列の要素も変更される。

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

a_slice = a[1:, 1:3]
print(a_slice)
# [[ 5  6]
#  [ 9 10]]

a_slice[0, 0] = 100
print(a_slice)
# [[100   6]
#  [  9  10]]

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

スライスで抽出した部分配列のコピーを作成したい場合はcopy()メソッドを使います。コピーの要素を変更しても元の配列の要素は変更されない。

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

a_slice_copy = a[1:, 1:3].copy()
print(a_slice_copy)
# [[ 5  6]
#  [ 9 10]]

a_slice_copy[0, 0] = 100
print(a_slice_copy)
# [[100   6]
#  [  9  10]]

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

ファンシーインデックス(リストによる選択)との組み合わせ

NumPyにはインデックスのリストによってnumpy.ndarrayから部分配列を選択するファンシーインデックスという仕組みがあります。

これとスライスを組み合わせて部分配列を選択し取得することができます。

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

print(a[[0, 2], 1:3])
# [[ 1  2]
#  [ 9 10]]

代入も同様。

a[[0, 2], 1:3] = 100
print(a)
# [[  0 100 100   3]
#  [  4   5   6   7]
#  [  8 100 100  11]]

a[[0, 2], 1:3] = [100, 200]
print(a)
# [[  0 100 200   3]
#  [  4   5   6   7]
#  [  8 100 200  11]]

a[[0, 2], 1:3] = [[100, 200], [300, 400]]
print(a)
# [[  0 100 200   3]
#  [  4   5   6   7]
#  [  8 300 400  11]]

ファンシーインデックスで抽出した部分配列はビューではなくコピーとなるので注意。

a_subset = a[[0, 2], 1:3]
print(a_subset)
# [[100 200]
#  [300 400]]

a_subset[0, 0] = -1
print(a_subset)
# [[ -1 200]
#  [300 400]]

print(a)
# [[  0 100 200   3]
#  [  4   5   6   7]
#  [  8 300 400  11]]

シェア

関連カテゴリー

Python NumPy

NumPy配列ndarrayの条件を満たす要素数をカウント NumPy配列ndarrayをシフト(スクロール)させるnp.roll NumPyでRGB画像の色チャンネルを分離して単色化、白黒化、色交換 NumPyのブロードキャスト(形状の自動変換) pandasからNumPyの関数などを使う方法(pd.np) Python, NumPyでグラデーション画像を生成 NumPyのファンシーインデックス(リストによる選択と代入) NumPyで欠損値np.nanを含む配列ndarrayの合計や平均を算出 NumPy配列ndarrayをprint表示で省略するかしないか設定 NumPy配列ndarrayの形状を変換するreshapeの使い方と-1の意味 NumPyのデータ型dtype一覧とastypeによる変換(キャスト) NumPy配列ndarrayの次元数、形状、サイズ(全要素数)を取得 NumPy配列ndarrayをタイル状に繰り返し並べるnp.tile Python, NumPy(OpenCV)で画像を二値化処理 NumPy配列の行・列ごとの合計、平均、最大、最小などを算出

Last Updated: 6/26/2019, 10:34:03 PM