第5回:Numpyで条件抽出と検索
はじめに
本記事は「Pythonで数値計算を行うためのNumPy入門」シリーズの第5回です.
このシリーズでは, "NumPyのよく使われる機能を分野ごとに理解すること" を目標としています.
Pythonで数値計算を行う際に欠かせないライブラリ「NumPy」について,基礎から応用まで体系的に学べます.
シリーズ構成予定(全10記事)
- NumPyのインストール 〜 arrayの基本
- 配列の追加・挿入・コピー
- 配列の作成(
ones/zeros/arange/linspace/eye) - 配列の結合(
hstack/vstack/concatenate) - 条件抽出と検索(
where/clip/unique/isclose)(本記事) - 統計処理(
mean/std/medianほか) - 線形代数(
dot/inv/det/norm)(公開後にリンクを追加する予定) - 乱数生成(
rand/randint/normal)(公開後にリンクを追加する予定) - 三角関数と角度変換(
sin/arctan2ほか)(公開後にリンクを追加する予定) - ファイル入出力(
savetxt/loadtxt)(公開後にリンクを追加する予定)
私のNumpyライブラリのバージョンは "1.26.4" です.
本記事のゴール
本記事では, 配列の条件抽出と検索 を学んでいきます.配列内で条件に一致するデータの抽出や検索の方法を説明します.
今回扱う関数は以下の4つです.
| 関数 | 内容 |
|---|---|
np.where() |
配列内で条件に一致する要素または値を取得 |
np.clip() |
配列の値を指定した範囲に収める |
np.unique() |
配列の重複を除いて,ユニーク(唯一)な値を抽出 |
np.isclose() |
配列の各要素が特定の値に近似しているかを判定 |
上記の関数を理解することで,配列内の条件抽出および検索する ことができるようになります.
NumPyライブラリの読み込み
NumPyを使うためには,まずライブラリの読み込みから始めます.
import numpy as np
numpyライブラリを "np" という名前として,扱えるようにライブラリを読み込んでいます.
以降では "np" という名前を使っていきます.
np.where() (配列内で条件に一致する要素または値を取得)
配列内で条件に一致する要素または値を取得する np.where() 関数に関して説明します.
# 適当なベクトルを作成
vec1 = np.array([1, 2, 3, 4, 5, 6])
# 適当な行列を作成
mat1 = np.array([[1, 9, 2],
[8, 3, 7],
[4, 6, 5]])
# np.where() による奇数と偶数を文字列に置き換え
vec1_where = np.where(vec1 % 2 == 0, "even", "odd")
print(f"vec1_where = {vec1_where}")
# 出力:vec1_where = ["odd" "even" "odd" "even" "odd" "even"]
# np.where() による条件に一致する要素を取得 (ベクトルに対して実施)
vec1_where_idx = np.where(vec1 > 3)
print(f"vec1_where_idx = {vec1_where_idx}")
# 出力:vec1_where_idx = (array([3, 4, 5]), )
# np.where() による条件に一致する要素を取得 (行列に対して実施)
mat1_where_idx = np.where(mat1 > 5)
print(f"mat1_where_idx = {mat1_where_idx}")
# 出力:mat1_where_idx = [array([0, 1, 1, 2]), array([1, 0, 2, 1])]
# np.where() による複数条件で値を変更 (行列に対して実施)
mat1_where_multi = np.where(mat1 > 6, "高", np.where(mat1 > 3, "中", "低"))
print(f"mat1_where_multi = {mat1_where_multi}")
# 出力:mat1_where_multi = [['低' '高' '低'],
# ['高' '低' '高'],
# ['中' '中' '中']]
# np.where() による複数条件を満たす要素を抽出 (行列に対して実施)
# 複数の条件を満たすためには,条件間に "&"" を使用
mat1_where_multi_cond = np.where((mat1 > 3) & (mat1 < 8))
print(f"mat1_where_multi_cond = {mat1_where_multi_cond}")
# 出力:mat1_where_multi_cond = [array([1, 2, 2, 2]), array([2, 0, 1, 2])]
np.where() の引数に関して,下表にまとめました.
| 引数番号 | 引数名 | 内容 | 備考 |
| 1 | condition | 条件 | 複数条件も可能 |
| 2 | x | 条件が True の時の処理 | 値を入れなくても良い |
| 3 | y | 条件が False の時の処理 | 値を入れなくても良い |
np.where() は配列内で条件に一致する要素または値を取得することができます.また,条件に関しては複数の条件を使用できます.
条件に一致した値を別の値に変更することもできます.
np.where() 関数の用途としては,以下が挙げられます.
- センサデータの閾値処理 (特定の距離内のデータより,干渉物の位置を計算)
- 画像処理での明るい部分だけの抽出 (特定の明度の画素を抽出)
- ロボットの経路生成時の干渉物判定
np.where() は 処理が高速である ため,様々な用途で使用されます.
np.clip() (配列の値を指定した範囲に収める)
配列の値を指定した範囲に収める np.clip() 関数に関して説明します.
# 適当なベクトルを作成
vec1 = np.array([1, 2, 3])
vec2 = np.array([2, 3, 4])
vec3 = np.array([1, 5, 10, 20])
# np.clip() による配列の値を指定した範囲に収める
vec1_clip = np.clip(vec1, 1.5, 3)
print(f"vec1_clip = {vec1_clip}")
# 出力:vec1_clip = [1.5 2. 3.]
vec2_clip = np.clip(vec2, 1.5, 3)
print(f"vec2_clip = {vec2_clip}")
# 出力:vec2_clip = [2. 3. 3.]
vec3_clip = np.clip(vec3, 3, 15)
print(f"vec3_clip = {vec3_clip}")
# 出力:vec3_clip = [3 5 10 15]
# np.clip() による配列の値に対して上限を与える
vec1_clip_upper = np.clip(vec1, None, 2)
print(f"vec1_clip_upper = {vec1_clip_upper}")
# 出力:vec1_clip_upper = [1 2 2]
# np.clip() による配列の値に対して下限を与える
vec3_clip_lower = np.clip(vec3, 8, None)
print(f"vec3_clip_lower = {vec3_clip_lower}")
# 出力:vec3_clip_lower = [8 8 10 20]
np.clip() の引数に関して,下表にまとめました.
| 引数番号 | 引数名 | 内容 | 備考 |
| 1 | a | 値を指定範囲内に収めたい配列 | - |
| 2 | a_min | 範囲の最小値 | a_min よりも小さい要素は a_min に置き換えられる (Noneも可能) |
| 3 | a_max | 範囲の最大値 | a_max よりも大きい要素は a_max に置き換えられる (Noneも可能) |
'np.clip()` は配列の値を指定した範囲に収めることができます.上限だけを設定したり,加減だけを設定したりすることができます.
np.clip() 関数の用途としては,以下が挙げられます.
- 三角関数 (
sin,cos) の誤差への対処 (-1 ~ 1 の範囲に抑える) - ロボットの関節角度を関節限界が超えないように抑える
- 画像処理で,画素の範囲制限 (0 ~ 255 の範囲に制限)
- 機械学習での,勾配爆発や勾配損失への対策
np.clip() は 値を一定範囲内に収めるため,安全にする ことができる関数です.
np.unique() (配列の重複を除いて,ユニーク(唯一)な値を抽出)
配列の重複を除いて,ユニーク(唯一)な要素を抽出する np.unique() 関数に関して説明します.
# 適当なベクトルを作成
vec1 = np.array([1, 2, 3, 4, 5, 6, 7])
vec2 = np.array([1, 2, 2, 2, 3, 3, 4])
# np.unique() によるユニーク(唯一)な要素を抽出
vec1_unique = np.unique(vec1)
print(f"vec1_unique = {vec1_unique}")
# 出力:vec1_unique = [1 2 3 4 5 6 7]
vec2_unique = np.unique(vec2)
print(f"vec2_unique = {vec2_unique}")
# 出力:vec2_unique = [1 2 3 4]
# np.unique() のオプション機能
# return_index:元配列の位置
# return_counts:各値が何回出現したか
vec1_values, vec1_index, vec1_counts = np.unique(vec1, return_index=True, return_counts=True)
print(f"vec1_values = {vec1_values}")
# 出力:vec1_values = [1 2 3 4 5 6 7]
print(f"vec1_index = {vec1_index}")
# 出力:vec1_index = [0 1 2 3 4 5 6]
print(f"vec1_counts = {vec1_counts}")
# 出力:vec1_counts = [1 1 1 1 1 1 1]
vec2_values, vec2_index, vec2_counts = np.unique(vec2, return_index=True, return_counts=True)
print(f"vec2_values = {vec2_values}")
# 出力:vec2_values = [1 2 3 4]
print(f"vec2_index = {vec2_index}")
# 出力:vec2_index = [0 1 4 6]
print(f"vec2_counts = {vec2_counts}")
# 出力:vec2_counts = [1 3 2 1]
np.unique() の引数に関して,下表にまとめました.
| 引数番号 | 引数名 | 内容 | 備考 |
| 1 | arr | ユニークな値を取得したい配列 | - |
| 2 | return_index | ユニークな値が保存されているインデックスが必要かどうかの有無 | return_index のデフォルト値(初期値)は False |
| 3 | return_counts | 同じ値が配列内に何個入っているかの有無 | return_counts のデフォルト値(初期値)は False |
np.unique() は配列の重複を除いて,ユニーク(唯一)な値を抽出することができます.また,オプション引数を使用することで,様々な情報を取得することが可能となります.
np.unique() 関数の用途としては,以下が挙げられます.
- センサデータで,同じ値の出現頻度の把握
- センサデータのヒストグラム作成
- 重複削除後のデータを可視化
np.unique() はグラフに可視化するための重複ないデータを取得することができる関数です.
np.isclose() (配列の各要素が特定の値に近似しているかを判定)
配列の各要素が特定の値に近似しているかを判定する np.isclose() 関数に関して説明します.
# 適当なベクトルを作成
vec1 = np.array([1, 2, 3, 4, 5, 6, 7])
vec2 = np.array([1, 2, 2, 2, 3, 3, 4])
# np.isclose() に各要素が近似しているかを判定
vec1_isclose = np.isclose(vec1, 2)
print(f"vec1_isclose = {vec1_isclose}")
# 出力:vec1_isclose = [False True False False False False False]
vec2_isclose = np.isclose(vec2, 2)
print(f"vec2_isclose = {vec2_isclose}")
# 出力:vec2_isclose = [False True True True False False False]
# np.isclose() で近似の閾値を変更して,各要素が近似しているかを判定
# rtol:相対交差 (absolute tolerance)
# atol:絶対交差 (absolute tolerance)
# 以下条件が成立したら True 判定となる.不成立なら False 判定となる.
# ∣第一引数 − 第二引数∣ <= atol + rtol * ∣第二引数∣
vec1_isclose_atol = np.isclose(vec1, 2, atol=2)
print(f"vec1_isclose_atol = {vec1_isclose_atol}")
# 出力:vec1_isclose_atol = [True True True True False False False]
vec1_isclose_rtol = np.isclose(vec1, 2, rtol=2)
print(f"vec1_isclose_rtol = {vec1_isclose_rtol}")
# 出力:vec1_isclose_rtol = [True True True True True True False]
vec2_isclose_atol = np.isclose(vec2, 2, atol=1)
print(f"vec2_isclose_atol = {vec2_isclose_atol}")
# 出力:vec2_isclose_atol = [True True True True True True False]
vec2_isclose_rtol = np.isclose(vec2, 2, rtol=2)
print(f"vec2_isclose_rtol = {vec2_isclose_rtol}")
# 出力:vec2_isclose_rtol = [True True True True True True True ]
np.isclose() の引数に関して,下表にまとめました.
| 引数番号 | 引数名 | 内容 | 備考 |
| 1 | a | 比較元データ | - |
| 2 | b | 比較先データ | - |
| 3 | rtol | 相対公差 (relative tolerance) | 引数 b との影響が高いので,注意 |
| 4 | atol | 絶対公差 (absolute tolerance) | 他の引数との影響はない |
近似しているかどうかの判定方式は下式の通りです.下式の文字は上表の引数名を使用しています.
上記の判定式が成立したら True 判定となり,不成立なら False 判定となります.
判定方式において, rtol は |b| との掛け合わせのため,注意しないと想定外の判定結果になります.
np.isclose() は配列の各要素が特定の値に近似しているかを判定することができます.引数 rtol と atol を変更することで,近似しているかどうかの判定方式を変えることができます.
np.isclose() 関数の用途としては,以下が挙げられます.
- ロボットの関節角度が目標角度に「ほぼ到達したか」の判定
- ロボットの手先位置が目標位置に「ほぼ到達したか」の判定
- 機械学習での「学習完了したか」の判定
np.isclose() は 特定の値に近似しているかにより,処理を終了させるかの判定として利用する ことができる関数です.
まとめ
NumPyライブラリの where / clip / unique / isclose を比較します.
| 操作 | 関数 | 備考 |
|---|---|---|
| 配列内で条件に一致する要素または値を取得 | np.where() |
処理が高速なため,様々な用途で使用 |
| 配列の値を指定した範囲に収める | np.clip() |
範囲内に収めるため,安全を確保 |
| 配列の重複を除いて,ユニーク(唯一)な値を抽出 | np.unique() |
グラフに可視化するための重複ないデータを取得 |
| 配列の各要素が特定の値に近似しているかを判定 | np.isclose() |
処理を終了させるかどうかの判定として利用 |
配列の条件抽出と検索を理解することで,配列内の条件に合ったデータやデータの範囲を制限させる ことができるようになります.
次回予告
第6回:「統計処理(mean / std / median ほか)」
-
np.mean()(配列の平均値を計算) -
np.std()(配列の標準偏差を計算) -
np.median()(配列の中央値を計算)
「統計処理に関する方法」を説明します.
参考文献
本記事を作成するに当たって参考にしたサイトをまとめました.
Discussion