はじめに
Pythonでデータ処理をしていると「条件を満たす要素の抽出」や「要素番号(インデックス)の取得」が必要になることがあります。このような操作では ブールマスク が非常に便利です。
本記事では、numpy
と標準 list
における ブールマスクの用法、および 要素番号の取得方法 について解説します。
よくある間違いやすい点 np.where
を使う場合は、条件を満たす配列の要素番号が取得できるが、pixel==3
のようにブールマスクを使う場合は、True/False
の numpy.array が返ってくるので、要素数は前者はカット後の要素数となり、後者は要素数は変わらないことに注意が必要。ただし、pixel[(pixel==3)]
のようにブールカットをかけたらカット後の要素数になる。
1. ブールマスクとは?
ブールマスクは、条件に合う要素を True、それ以外を False とする ブール型配列 です。データ処理のフィルタリングや要素抽出に使います。
2. numpy
におけるブールマスク
2.1 ブールマスクで要素を抽出する
numpy
配列では、ブールマスクを使って条件に合う要素を直接抽出できます。
import numpy as np
# サンプルデータ
data = np.array([0, 1, 2, 1, 3, 1, 4])
# 条件: 値が1に等しい要素を抽出
mask = (data == 1) # ブールマスクの生成
filtered_data = data[mask] # マスクを使って抽出
print("Boolean Mask:", mask)
print("Filtered Data:", filtered_data)
出力:
Boolean Mask: [False True False True False True False]
Filtered Data: [1 1 1]
2.2 True の要素番号(インデックス)を取得する
np.nonzero()
または np.where()
を使います。
# True の要素番号を取得
indices = np.nonzero(mask)[0]
# または np.where を使う
indices_alt = np.where(mask)[0]
print("True Indices (nonzero):", indices)
print("True Indices (where):", indices_alt)
出力:
True Indices (nonzero): [1 3 5]
True Indices (where): [1 3 5]
2.3 多次元配列でのブールマスク
numpy
では多次元配列にもブールマスクを適用できます。
data = np.array([[0, 1], [1, 0]])
# 条件: 値が1の要素
mask = (data == 1)
# True の位置を取得
indices = np.nonzero(mask)
print("True Indices (Row):", indices[0]) # 行インデックス
print("True Indices (Col):", indices[1]) # 列インデックス
出力:
True Indices (Row): [0 1]
True Indices (Col): [1 0]
3. Pythonリストにおけるブールマスクの用法
3.1 ブールマスクを使った要素抽出
Python標準 list
では、numpy
のようにブールマスクを直接適用することは できません。
data = [0, 1, 2, 1, 3, 1, 4]
mask = [False, True, False, True, False, True, False]
# これを実行するとエラーになる
# filtered_data = data[mask] # TypeError
3.2 リスト内包表記を使った抽出
リストとブールマスクの要素を対応させるには、zip
やリスト内包表記を使います。
# zip を使って条件に合う要素を抽出
filtered_data = [value for value, flag in zip(data, mask) if flag]
print("Filtered Data:", filtered_data)
出力:
Filtered Data: [1, 1, 1]
3.3 True の要素番号(インデックス)を取得する
リストでは enumerate()
を使ってインデックスを取得します。
# True の要素番号を取得
indices = [i for i, flag in enumerate(mask) if flag]
print("True Indices:", indices)
出力:
True Indices: [1, 3, 5]
4. itertools.compress
を使った簡潔なリスト処理
Python標準ライブラリの itertools.compress
を使うと、ブールマスクをリストに適用できます。
from itertools import compress
data = [0, 1, 2, 1, 3, 1, 4]
mask = [False, True, False, True, False, True, False]
# compress を使って抽出
filtered_data = list(compress(data, mask))
print("Filtered Data:", filtered_data)
出力:
Filtered Data: [1, 1, 1]
5. 要素数の取得
pixel == 1
でブールマスクを作成した場合、np.sum(pixel == 1)
で要素数が求められます。その理由は、ブール値 (True
/ False
) が数値として扱われる ためです。
ブール値と数値の関係
Pythonでは、True
と False
は 整数 として扱われることがあります:
-
True
は 1 として扱われる -
False
は 0 として扱われる
したがって、numpy
配列のブールマスクに対して np.sum()
を適用すると、True
の数を合計する動作になります。
具体例
以下の例で確認してみましょう:
import numpy as np
# サンプルデータ
pixel = np.array([0, 1, 2, 1, 3, 1, 4])
# ブールマスクの生成 (True: pixel == 1, False: otherwise)
mask = (pixel == 1)
print("Boolean Mask:", mask)
# True の要素数を計算
count = np.sum(mask)
print("Number of True Elements:", count)
出力:
Boolean Mask: [False True False True False True False]
Number of True Elements: 3
動作の仕組み
-
pixel == 1
各要素に対して条件を評価し、True
またはFalse
のブール型配列 (mask
) を生成します。[False, True, False, True, False, True, False]
-
np.sum(mask)
True
が 1、False
が 0 として数値扱いされるため、np.sum()
はTrue
の個数を合計します。0 + 1 + 0 + 1 + 0 + 1 + 0 = 3
この性質を利用すれば、条件を満たす要素数を簡単に求めることができます。
6. numpy
と list
の比較まとめ
機能 | numpy |
Python list
|
---|---|---|
ブールマスク適用 | data[mask] |
zip or compress
|
True の要素番号取得 |
np.nonzero(mask) / np.where(mask)
|
enumerate() を使う |
多次元配列の対応 | 可能 | 非対応 |
処理速度 | ベクトル化で高速 | ループベースのため遅い(?) |
まとめ
-
numpy
ではブールマスクを直接使って要素の 抽出 や インデックス取得 が簡単にできます。 - Pythonの標準
list
ではブールマスクが直接適用できないため、zip
やenumerate
を活用する必要があります。 -
多次元配列 の場合は
numpy
一択です。
データ処理の規模や用途に応じて、numpy
と list
を使い分けましょう!