NumPyを使用する際、特定の条件を満たすデータのみを効率的に取り出したい場面は非常に多くあります。
このようなニーズに応える強力な機能が、ブールインデックス(Boolean Indexing)です。
ブールインデックスを活用することで、Pythonの標準的なループ処理(for文など)を記述することなく、高速かつ直感的に配列の要素を抽出できます。
本記事では、NumPyにおけるブールインデックスの基本的な仕組みから、実務で役立つ応用テクニックまで詳しく紹介します。
ブールインデックスの基本概念
ブールインデックスとは、ブール値(TrueまたはFalse)で構成された配列を使用して、元の配列から要素を選択する手法です。
NumPy配列に対して比較演算子を適用すると、各要素が条件を満たすかどうかを示す「マスク配列」が生成されます。
このマスク配列を元の配列のインデックスとして渡すことで、Trueに対応する要素のみを抽出できる仕組みです。
大量のデータを扱うデータサイエンスの現場において、この手法はパフォーマンスを向上させるために不可欠な技術といえます。
比較演算子によるマスク配列の生成
まずは、数値が含まれる配列に対して比較演算を行い、どのような結果が得られるかを確認してみましょう。
import numpy as np
# 配列の作成
data = np.array([10, 25, 30, 45, 50])
# 30以上の要素を確認する条件式
mask = data >= 30
print(mask)
[False False True True True]
上記のコードでは、各要素が30以上であればTrue、そうでなければFalseを返す新しい配列が作成されます。
これがブールインデックスの基礎となるマスク配列です。
条件に合致する要素の抽出
次に、作成したマスクを使用して、実際に条件を満たす値だけを抜き出します。
# マスクを配列に適用して抽出
filtered_data = data[mask]
print(filtered_data)
[30 45 50]
このように、配列名に対して角括弧[]の中に条件式(またはマスク配列)を記述するだけで、特定の要素を抽出できます。
抽出された結果は新しい配列として返されるため、元の配列を書き換えることなく操作が可能です。
複数条件を組み合わせた高度なフィルタリング
実務では、「AかつB」や「AまたはB」といった、複数の条件を組み合わせてデータを抽出したいケースが頻繁に発生します。
NumPyでは、論理演算子を使用することで複雑な条件分岐を一行で記述できます。
AND演算子(&)とOR演算子(|)
複数の条件を結合する場合、Python標準のandやorではなく、NumPy専用の演算子を使用する必要があります。
以下の表に、主な論理演算子をまとめました。
| 演算子 | 意味 | 使用例 |
|---|---|---|
& | AND(かつ) | (arr > 10) & (arr < 50) |
| | OR(または) | (arr < 10) | (arr > 90) |
~ | NOT(否定) | ~(arr == 0) |
これらの演算子を使用する際は、各条件式を必ず丸括弧()で囲む必要がある点に注意してください。
実践的な複数条件の抽出例
具体的なコードで、範囲指定による抽出方法を見ていきましょう。
# 1から20までの配列を作成
arr = np.arange(1, 21)
# 5以上かつ15以下の要素を抽出
result = arr[(arr >= 5) & (arr <= 15)]
print(result)
[ 5 6 7 8 9 10 11 12 13 14 15]
この記述方法は、Pandasなどのデータ分析ライブラリでも共通して利用されるため、習得しておくと非常に汎用性が高いです。
ブールインデックスを用いたデータの加工と置換
ブールインデックスは、単にデータを抽出するだけでなく、特定の条件を満たす要素に対してのみ値を代入する場合にも極めて有効です。
これにより、データクリーニングにおける外れ値の補正などが容易になります。
条件に合致する要素の一括置換
例えば、配列内の負の値をすべて0に置き換えたい場合は、以下のように記述します。
# サンプル配列の作成
values = np.array([-10, 5, -2, 15, 0])
# 0未満の要素を0に置換
values[values < 0] = 0
print(values)
[ 0 5 0 15 0]
この手法を用いると、特定の条件に該当するデータだけをピンポイントで修正できるため、コードの可読性が大幅に向上します。
np.where関数との使い分け
抽出だけでなく、「条件を満たす場合はA、満たさない場合はB」という処理を行いたい場合は、np.where関数が適しています。
ブールインデックスによる直接的な代入は元の配列を変更しますが、np.whereは条件に応じた新しい配列を生成します。
x = np.array([1, 10, 5, 20])
# 10以上の場合は'High'、それ以外は'Low'とする新しい配列を作成
labels = np.where(x >= 10, 'High', 'Low')
print(labels)
['Low' 'High' 'Low' 'High']
パフォーマンスにおけるメリット
NumPyのブールインデックスが推奨される最大の理由は、その実行速度にあります。
Pythonのリストに対してリスト内包表記やフィルタリングを行うよりも、NumPyのベクトル演算(ユニバーサル関数)を利用した方が、内部的にC言語レベルで最適化されるため高速です。
数万件から数百万件といった大規模なデータを処理する場合、この速度差はシステムのパフォーマンスに決定的な影響を与えます。
「ループを回さず、配列のまま処理する」というNumPyの設計思想を体現した機能が、このブールインデックスなのです。
まとめ
NumPyのブールインデックスは、配列から特定の条件に基づいたデータを抽出・加工するための非常に強力なツールです。
比較演算子を組み合わせて直感的に記述でき、複雑な論理条件にも柔軟に対応可能です。
また、データの一括置換やフィルタリングを高速に行えるため、効率的なデータ分析には欠かせません。
本記事で紹介した基本操作と複数条件の組み合わせ、そして置換テクニックをマスターして、NumPyを最大限に活用しましょう。
