AIで異常値の候補を調べる方法|四分位数の計算法と元行を残す

架空の処理時間8件でexclusiveとinclusiveの四分位数・境界・候補の違いを計算。元行を保持し、候補理由と原因調査を分けます。

AIものさし編集部

AIへ異常値分析を頼むときは、対象列、比較する集団、計算方法、候補にする条件を先に決めます。「他より大きい」という説明だけでは、入力ミスとも業務上の異常とも決まりません。候補の元行を残し、計算と原因の調査を分けて進めます。

この記事では、架空の処理時間8件で、四分位範囲を使った候補抽出を確かめます。同じ値でも四分位数の計算法によって候補が変わる例です。Pythonの計算はローカルで実行しましたが、AIサービスへの投入、実業務の異常判定、監視通知、画面表示は検証していません。

分析前に、同じ条件の値かを確認する

今回の値は、同じ種類の作業を同じ計測方法で測ったと仮定する架空の処理時間です。実際に計測したデータではありません。

表は横にスクロールできます

行ID処理時間(秒)
R0110
R0211
R0312
R0413
R0514
R0615
R0716
R0822

実データなら、秒とミリ秒が混在していないか、完了した作業だけを集めていないか、再試行を含むか、重複記録や欠損がないかを先に確認します。大きな値の入力ミスを調べることと、遅い処理を除外して平均を良く見せることを混同しません。

集団の定義も必要です。別の処理、データ量、時間帯が混ざっているなら、全体の分布と条件別の分布を見ます。少数の集団へ細かく分ければよいとは限らず、件数も併記します。この8件だけでは季節性や曜日差を判断できません。

四分位範囲と候補の条件を明記する

第1四分位数をQ1、第3四分位数をQ3とし、四分位範囲をIQR = Q3 − Q1とします。今回の説明用ルールは、下限Q1 − 1.5 × IQRより小さい値、または上限Q3 + 1.5 × IQRより大きい値を調査候補にするものです。境界と等しい値は候補にしません。

NISTは箱ひげ図の内側の境界としてこの式を説明しています。また、外れた値は情報を含む可能性があり、除去を考える前に調べる必要があるとしています。この式を、全業務共通の異常判定や削除基準にはしません。NIST:外れ値と境界(新しいタブで開く)

式だけでなく、Q1とQ3の求め方も固定します。Pythonのstatistics.quantilesにはexclusiveとinclusiveがあり、端の値を百分位のどこへ置くかが異なります。別のツールへ移すときも、単に「四分位数」と指定するだけでは同じ値になるとは限りません。Python公式:statistics(新しいタブで開く)

同じ8件で、方法による違いを計算する

今回の主計算はexclusiveです。比較用にinclusiveも計算し、差を隠さず残します。

表は横にスクロールできます

項目exclusive(主計算)inclusive(比較)
Q111.25秒11.75秒
中央値13.5秒13.5秒
Q315.75秒15.25秒
IQR4.5秒3.5秒
下限4.5秒6.5秒
上限22.5秒20.5秒
候補0件R08の22秒、1件

exclusiveでは22 < 22.5なのでR08は候補になりません。inclusiveでは22 > 20.5なので候補になります。どちらも、ここで指定した計算法と条件に沿った結果です。R08が候補にならない結果を「問題なし」、候補になった結果を「入力ミス」と置き換えてはいけません。

全8件の合計は113秒、平均は14.125秒、中央値は13.5秒です。これらは同じデータの要約で、異なる問いへ答えています。平均と中央値の差だけで、特定の行の原因を説明することもできません。

候補を見た後に方法を都合よく選び直すと、結果に合わせた基準になります。変更が必要なら、変更理由、適用するデータ、旧条件と新条件の両方の結果を記録します。

元行を削除せず、候補を別に返す

次のコードは、掲載した8件へラベルを付けます。主計算と比較のそれぞれで境界を求め直し、元の行IDを保持します。処理時間はこの例では有限の正の数、行IDは空でなく一意という条件に限定しています。

from math import isfinite
from statistics import quantiles

rows = [
    ("R01", 10), ("R02", 11), ("R03", 12), ("R04", 13),
    ("R05", 14), ("R06", 15), ("R07", 16), ("R08", 22),
]

def label_candidates(rows, method):
    if method not in ("exclusive", "inclusive"):
        raise ValueError("unsupported method")
    if len(rows) < 5:
        raise ValueError("this example requires at least 5 rows")
    ids = [row_id for row_id, _ in rows]
    if any(not isinstance(x, str) or not x.strip() for x in ids):
        raise ValueError("empty or invalid row ID")
    if len(set(ids)) != len(ids):
        raise ValueError("duplicate row ID")
    values = [value for _, value in rows]
    if any(type(x) not in (int, float) or not isfinite(x) or x <= 0
           for x in values):
        raise ValueError("expected finite positive seconds")
    q1, _, q3 = quantiles(values, n=4, method=method)
    iqr = q3 - q1
    if iqr == 0:
        raise ValueError("zero IQR: review the rule separately")
    lower, upper = q1 - 1.5 * iqr, q3 + 1.5 * iqr
    labeled = [
        {"row_id": row_id, "seconds": value,
         "candidate": value < lower or value > upper}
        for row_id, value in rows
    ]
    return {"method": method, "q1": q1, "q3": q3,
            "iqr": iqr, "lower": lower, "upper": upper,
            "rows": labeled}

for method in ("exclusive", "inclusive"):
    result = label_candidates(rows, method)
    print(method, result["lower"], result["upper"],
          [r["row_id"] for r in result["rows"] if r["candidate"]])

出力の候補IDはexclusiveが空配列、inclusiveがR08です。どちらも出力行は8件で、元の順序と値を保持します。これは監視システムや業務データの自動修正を実装したコードではありません。

5件以上、正の秒数、IQRが0なら保留するという条件は、この例の契約です。すべての統計分析の必須条件ではありません。実際の測定で0秒が意味を持つ場合や、同値が多くIQRが0になる場合には、測定単位や別の比較基準を検討します。

AIへの依頼では、候補理由と原因を分ける

次の行ID付きデータについて、調査候補を整理してください。
対象:同じ種類・計測方法の処理時間(秒)。
主計算:Python statistics.quantiles、n=4、method="exclusive"。
IQR=Q3-Q1。下限Q1-1.5*IQR未満、上限Q3+1.5*IQR超を候補。
境界と等しい値は候補にしません。
欠損、単位、行IDの重複を先に確認し、勝手に削除・補完しないでください。
出力:件数、Q1、Q3、IQR、上下限、全行の候補ラベル、候補の理由。
原因と次の確認先は別欄にし、資料にない原因は「未確認」としてください。
方法を変更する場合は主計算を上書きせず比較として示してください。
実行していない計算を、実行済みと書かないでください。
入力:[必要な行と計測条件]。

AIには式の説明、候補表、調査項目の整理を頼めます。提示された数値は手元の計算と照合し、候補が0件でもデータ全体が正しいという保証にはしません。入力にないキャンペーン、障害、担当者の行動などを、もっともらしい原因として補わせないようにします。

R08を調査するなら、元の計測ログ、処理量、再試行、計測開始・終了条件を確認します。22秒の理由が未確認の段階では、元行を残して調査中とします。異常を確定する基準、除外の判断、通知や業務上の対応は別に決めます。

計算の再現と、実業務への適用を分ける

今回、掲載コードの2方式、表の数値、候補ID、元8行の保持をローカルで確認しました。境界と等しい値を候補にしない比較、欠損に相当する値や重複IDなどを拒否する条件も確認しています。

実業務では、データ版、期間、対象列、単位、集団、計算法、境界の比較記号、候補ID、調査結果、対応者を残します。時系列なら過去のどの期間を基準にしたかも必要です。今回の8件の分布内判定は、将来の遅延予測や同曜日比較の検証ではありません。

候補抽出が再現したことと、業務上の判定が妥当なことは別に確かめます。データを消して結果を整える前に、元行と計算条件へ戻れる状態を保ってください。

参考資料

公式資料の確認日:2026年10月20日。8件は説明用の架空値です。掲載Pythonと計算条件をローカルで検査しました。AIサービス、実データの原因、業務基準の妥当性、通知、画面表示は未検証です。

次に読む・使う

この記事の次に必要な情報へ進めます。