前回の記事でndarrayの要素の参照方法について解説したが、ndarrayでは今回紹介するブールインデックス、ファンシーインデックスという参照方法も用意されている。

ブールインデックス参照

早速、例を見ていこう。

まず、6行3列の2次元配列dataを定義し、各行に対応する配列nameを用意する。

import numpy as np

data = np.random.randn(6, 3)
name = np.array(['taro', 'jiro', 'saburo', 'taro', 'jiro', 'saburo'])

name == 'taro'
# array([ True, False, False,  True, False, False])

ndarrayは比較演算子(>=や==など)をベクトル演算に用いることもでき、上記のように各要素がtaroかどうか真偽値(True・False)として結果を取得することができる。

この真偽値配列をインデックスとして渡すことで、Trueの位置の要素だけを参照することができる。

import numpy as np

data = np.random.randn(6, 3)
print(data)
name = np.array(['taro', 'jiro', 'saburo', 'taro', 'jiro', 'saburo'])

print(data[name == 'taro'])

# 以下出力結果
[[-0.53302874 -0.53127488  0.64156317]
 [-0.81398381 -0.28940456  0.09640812]
 [ 0.19935173  0.02286339 -1.55135963]
 [-0.07720318 -0.61034682 -0.90544993]
 [ 0.53191529 -0.3342422   1.82631684]
 [-0.48109535  0.97156044  0.8633786 ]]

array([[-0.53302874, -0.53127488,  0.64156317],
       [-0.07720318, -0.61034682, -0.90544993]])

なお、ブールインデックス参照は、インデックスに渡す真偽値配列と元の配列の要素数が一致していなければならないので注意する必要がある。

また、複数の条件を指定することもでき、複雑な条件指定にも対応することができる。
※真偽値配列の場合、Pythonの「and」や「or」キーワードは使えないので「&」と「|」を使う!

import numpy as np

data = np.random.randn(6, 3)
name = np.array(['taro', 'jiro', 'saburo', 'taro', 'jiro', 'saburo'])

mask = (name == 'taro') | (name == 'saburo')
mask
# array([ True, False,  True,  True, False,  True])

data[mask]

真偽値配列を使った値の代入

ブールインデックス参照と組み合わせて値を代入することもできる。

以下は、負の値を持つ要素全てに0を設定する例だ。

import numpy as np

data = np.random.randn(6, 3)
name = np.array(['taro', 'jiro', 'saburo', 'taro', 'jiro', 'saburo'])

data[data < 0] = 0
data

# 以下出力結果
array([[0.34446745, 0.        , 1.55046266],
       [0.        , 0.        , 0.        ],
       [1.60214805, 0.        , 1.32304427],
       [0.        , 0.83257511, 0.        ],
       [0.        , 0.        , 0.        ],
       [1.0677772 , 0.12984518, 0.        ]])

ファンシーインデックス参照

ブールインデックス参照は真偽値配列を使ったのに対し、ファンシーインデックス参照では整数配列を使う。

import numpy as np

arr = np.empty((6, 3))

for i in range(6):
    arr[i] = i
    
idx_arr = [5, 3, 1, 0]

arr[idx_arr]

# 以下出力結果
array([[5., 5., 5.],
       [3., 3., 3.],
       [1., 1., 1.],
       [0., 0., 0.]])

負数の配列をインデックスへ渡すと次のとおり、最終行から指定することができる。

import numpy as np

arr = np.empty((6, 3))

for i in range(6):
    arr[i] = i
    
idx_arr = [-5, -3, -1]

arr[idx_arr]

#以下出力結果
array([[1., 1., 1.],
       [3., 3., 3.],
       [5., 5., 5.]])

複数の配列を指定する

複数の配列をインデックスに指定することも可能だ。

今回は、8行4列の二次元配列に複数のインデックス配列を指定し、結果として一次元配列を取得する例を見てみよう。

import numpy as np

arr = np.arange(32).reshape((8, 4))

idx_arr = [
    [1, 5, 7, 2],
    [0, 3, 1, 2]
]

print(arr)
arr[idx_arr]

# 以下出力結果
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]
 [24 25 26 27]
 [28 29 30 31]]

array([ 4, 23, 29, 10])

インデックス配列の1行目が元の配列の何行目を取得するかを、インデックス配列の2行目が元の配列の何番目の要素を取得するか、といった内容となっている。

まとめ

前回の記事と今回の2回に渡り、ndarrayの参照方法について見てきた。

今回取り上げたブールインデックス、ファンシーインデックスによる参照については、スライシングと違い、元データのコピーを取得するということを覚えておいてほしい。