Python | ファイル・OS 操作:NumPy の条件抽出

Python Python
スポンサーリンク
  1. 概要(NumPyの条件抽出は「ブール配列で一発フィルタ」する最短ルート)
  2. 基本の使い方(ここが重要)
    1. 単一条件で抽出(ブール配列をそのままインデックスに使う)
    2. 二次元配列の抽出(同形状マスクで値を取り出す)
  3. 複数条件の組み合わせ(& | ~ と括弧が最重要)
    1. かつ・または・否定を組み合わせて柔軟に絞る
  4. インデックスの取得と置換(np.where・nonzero・argwhere)
    1. Trueの位置(インデックス)を取りたいとき
    2. 二次元で座標の一覧が欲しいとき(行・列をペアで)
    3. 条件に応じた値の置換(if-elseのベクトル化)
  5. NaN・特別値の扱い(比較では拾えないものを正しく処理)
    1. NaN検出と除外・置換
    2. 値の集合で抽出(isinで「含まれるか」を判定)
  6. 二次元・多次元の条件抽出(軸と形を意識して使う)
    1. 列条件・行条件での抽出
    2. 形を保ったまま「該当だけ残し、他は埋める」
  7. 安全な代入と更新(マスクで一括更新、ループ不要)
    1. 条件を満たす要素へまとめて代入
    2. 二次元で部分的な更新
  8. よくある落とし穴(括弧忘れ・形不一致・比較の勘違い)
    1. 括弧を忘れるとエラー・意図せぬ結果
    2. 形が合わないマスクは使えない
    3. NaNは比較では拾えない
  9. 例題で身につける(定番から一歩先まで)
    1. 例題1:閾値と奇数・偶数で抽出
    2. 例題2:二次元で行・列条件を組み合わせる
    3. 例題3:np.whereで条件置換とインデックス取得
    4. 例題4:NaN除外して統計、欠損は0で補完
  10. まとめ

概要(NumPyの条件抽出は「ブール配列で一発フィルタ」する最短ルート)

NumPyの条件抽出(Boolean indexing)は、配列に対する比較結果(True/Falseの配列)をそのままインデックスとして使い、条件に合う要素だけを高速に取り出す仕組みです。重要なのは「条件式が配列全体に同時適用される」「複数条件は&(かつ)・|(または)・~(否定)で組み合わせる」「括弧が必須」「np.where・nonzeroでインデックスを取得できる」「NaNは特別扱い(np.isnan)」の5点です。まずは一次元で感覚を掴み、二次元・三次元で形(shape)を意識しながら使うと、ループ無しで自然に書けるようになります。


基本の使い方(ここが重要)

単一条件で抽出(ブール配列をそのままインデックスに使う)

import numpy as np

a = np.array([10, 20, 15, 8, 30])
mask = a > 15
print(mask)       # [False  True False False  True]
print(a[mask])    # [20 30]
Python

条件式は配列の各要素に一度に評価され、同じ長さのTrue/False配列が返ります。これをインデックスに入れると、Trueの要素だけが取り出されます。

二次元配列の抽出(同形状マスクで値を取り出す)

M = np.array([[1, 2, 3],
              [4, 5, 6]])
mask = M % 2 == 0          # 偶数をTrueに
print(M[mask])             # [2 4 6]
Python

同形状のマスクを使うと、Trueの場所の値が一次元に平坦化されて返ります。形を保ったまま残したい場合は、後述の手法を使います。


複数条件の組み合わせ(& | ~ と括弧が最重要)

かつ・または・否定を組み合わせて柔軟に絞る

import numpy as np

a = np.array([10, 20, 15, 8, 30])
# 15より大きく、かつ偶数
mask = (a > 15) & (a % 2 == 0)
print(a[mask])  # [20]

# 10未満、または30以上
mask = (a < 10) | (a >= 30)
print(a[mask])  # [8 30]

# 否定(15超ではない)
mask = ~(a > 15)
print(a[mask])  # [10 15 8]
Python

&や|を使うときは、各条件を必ず括弧で囲みます。括弧なしだと演算子の優先順位でエラーや意図しない結果になります。


インデックスの取得と置換(np.where・nonzero・argwhere)

Trueの位置(インデックス)を取りたいとき

import numpy as np

a = np.array([10, 20, 15, 8, 30])
idx = np.where(a > 15)     # (行)インデックスのタプル
print(idx)                 # (array([1, 4]),)
print(a[idx])              # [20 30]
Python

一次元ではnp.whereは「該当位置の配列」を返します。二次元以上では軸ごとのインデックスをタプルで返すため、そのまま使えば位置抽出も可能です。

二次元で座標の一覧が欲しいとき(行・列をペアで)

import numpy as np

M = np.array([[1, 2, 3],
              [4, 5, 6]])
rows, cols = np.where(M > 3)  # 各Trueの行・列
coords = list(zip(rows, cols))
print(coords)                 # [(1, 0), (1, 1), (1, 2)]
Python

行配列・列配列が得られるので、座標ペアにして回す処理にも使えます。全座標を一つの配列で欲しいならnp.argwhereが便利です。

条件に応じた値の置換(if-elseのベクトル化)

import numpy as np

a = np.array([10, 20, 15, 8, 30])
b = np.where(a >= 20, a, -1)   # 20以上なら元値、それ以外は-1
print(b)                       # [10 -1 15 -1 30] ← 10は20未満では?
Python

上の例は「20以上を残し、それ以外を-1」とする想定なら、実装は次のようにします。

b = np.where(a >= 20, a, -1)   # [ -1 20 -1 -1 30 ]
Python

条件・真値・偽値の対応関係を明確に書くと、配列全体に一度で適用されます。


NaN・特別値の扱い(比較では拾えないものを正しく処理)

NaN検出と除外・置換

import numpy as np

x = np.array([1.0, np.nan, 3.0, np.nan])
mask = ~np.isnan(x)           # NaN以外
print(x[mask])                # [1. 3.]

filled = np.where(np.isnan(x), 0.0, x)  # NaNを0に置換
print(filled)                           # [1. 0. 3. 0.]
Python

NaNは通常の比較では拾えないため、np.isnanを使うのが安全です。

値の集合で抽出(isinで「含まれるか」を判定)

import numpy as np

names = np.array(["a", "b", "c", "d"])
mask = np.isin(names, ["b", "d"])
print(names[mask])   # ['b' 'd']
Python

複数候補との一致はnp.isinが最短です。


二次元・多次元の条件抽出(軸と形を意識して使う)

列条件・行条件での抽出

import numpy as np

M = np.array([[10, 20, 30],
              [ 5, 25, 35],
              [15,  8, 40]])

# 1列目が10以上の行を丸ごと抽出
row_mask = M[:, 0] >= 10
print(M[row_mask])       # [[10 20 30] [15  8 40]]

# 2列目が20以上の列を丸ごと抽出
col_mask = M[:, 1] >= 20
print(M[:, col_mask])    # [[20 30] [25 35] [ 8 40]]
Python

行と列でマスクの掛け方が違うため、M[行マスク]またはM[:, 列マスク]の形を意識します。

形を保ったまま「該当だけ残し、他は埋める」

import numpy as np

M = np.array([[1, 2, 3],
              [4, 5, 6]])
mask = M % 2 == 0
out = np.where(mask, M, 0)   # 偶数は値、奇数は0
print(out)                   # [[0 2 0] [4 0 6]]
Python

同形状で戻したい場合はnp.whereで置換するのが簡単です。


安全な代入と更新(マスクで一括更新、ループ不要)

条件を満たす要素へまとめて代入

import numpy as np

a = np.array([10, 20, 15, 8, 30])
a[a < 15] = 0
print(a)  # [10 20 15  0 30]
Python

条件式を左辺のインデックスに使えば、対象要素を一括更新できます。二次元でも同様に機能します。

二次元で部分的な更新

import numpy as np

M = np.array([[1, 2, 3],
              [4, 5, 6]])
M[M % 2 == 1] = -M[M % 2 == 1]   # 奇数だけ符号反転
print(M)                         # [[-1  2 -3] [ 4 -5  6]]
Python

右辺にも同じマスクを使うと、対象の値を取り出して加工し、戻す流れが自然に書けます。


よくある落とし穴(括弧忘れ・形不一致・比較の勘違い)

括弧を忘れるとエラー・意図せぬ結果

import numpy as np

a = np.array([10, 20, 30])
# 悪い例:a > 10 & a < 30   ← 演算子優先順位のせいでエラー・誤動作
mask = (a > 10) & (a < 30)   # 良い例
print(a[mask])               # [20]
Python

& と | は「ビット演算子」で、Pythonのand/orとは仕様が違います。必ず各条件を括弧で囲みます。

形が合わないマスクは使えない

import numpy as np

M = np.arange(6).reshape(2, 3)   # (2,3)
mask = np.array([True, False])   # (2,)
# M[mask] は行選択なのでOK
print(M[mask])

col_mask = np.array([True, False])  # (2,) ← 列数と不一致
# M[:, col_mask] はエラーになる(列は3本なのに2長のマスク)
Python

行を選ぶマスクは行数と、列を選ぶマスクは列数と一致している必要があります。

NaNは比較では拾えない

import numpy as np

x = np.array([np.nan, 1.0])
print(x == np.nan)       # [False False]
print(np.isnan(x))       # [ True False]
Python

NaNの検出はnp.isnanを使います。比較では見つかりません。


例題で身につける(定番から一歩先まで)

例題1:閾値と奇数・偶数で抽出

import numpy as np

a = np.array([3, 10, 21, 14, 7, 30])
print(a[(a >= 10) & (a % 2 == 0)])  # 10以上かつ偶数 → [10 14 30]
Python

例題2:二次元で行・列条件を組み合わせる

import numpy as np

M = np.array([[10, 20, 30],
              [ 5, 25, 35],
              [15,  8, 40]])

row_mask = M[:, 0] >= 10       # 1列目が10以上の行
col_mask = M[0, :] >= 25       # 1行目が25以上の列
print(M[row_mask][:, col_mask])  # 行→列の順で絞る → [[30] [40]]
Python

例題3:np.whereで条件置換とインデックス取得

import numpy as np

a = np.array([10, 20, 15, 8, 30])
print(np.where(a >= 20, a, -1))   # 条件置換 → [-1 20 -1 -1 30]
print(np.where(a < 10))           # インデックス取得 → (array([3]),)
Python

例題4:NaN除外して統計、欠損は0で補完

import numpy as np

x = np.array([1.2, np.nan, 0.8, np.nan, 1.5])
clean = x[~np.isnan(x)]
print(clean.mean())                     # 欠損除外の平均
filled = np.where(np.isnan(x), 0.0, x)  # 欠損を0に
print(filled)
Python

まとめ

NumPyの条件抽出は「比較で作るブール配列をインデックスに使う」だけで、配列全体を一発でフィルタできます。複数条件は&・|・~で組み合わせ、必ず括弧を付ける。インデックスが必要ならnp.where・nonzero・argwhere、値の置換はnp.whereが最短。NaN検出はnp.isnan、集合一致はnp.isin。二次元・多次元では「どの軸に対するマスクか」をshapeで確認し、行・列の取り扱いを意識する。更新はマスク代入で安全に一括。これらを型にすれば、初心者でもループなしで短く、速く、壊れない抽出・加工が自然に書けるようになります。

タイトルとURLをコピーしました