AIの異常検知をデータ分析で検算:GA4の外れ日を3つの基準で比較

AI 異常検知をデータ分析に使う前の確かめ方です。GA4 公開サンプルの日別セッション数 92 日を AI に 3 通りの頼み方で見せ、挙げた外れ日を標準偏差・四分位・曜日別の 3 基準の SQL と突き合わせました。

21分で読めます Data
日別の折れ線と上下の帯の図で、帯をはみ出た青い点と帯の内側にある橙の白抜きの点を虫眼鏡で見比べ、AI が挙げた外れた日を統計の基準で確かめるイメージ
この記事は AI が下書きを補助し、運営者が内容を確認して公開しています。

日別のセッション数の表を AI に貼り、「おかしな日はあるか」と聞いているマーケターや事業担当の方向けの記事です。AI の異常検知をデータ分析に使う前に、AI が挙げた日が統計の基準でも外れているかを、公開データと SQL で確かめた結果を紹介します。読み終わると、根拠つきで外れた日を挙げてもらう頼み方と、その答えを検算する順番が分かります。

この記事で分かること

  • 「おかしな日を挙げて」とだけ頼むと、AI が挙げた 10 日のうち 7 日は、どの統計の基準でも外れていなかったこと
  • 基準を「全体の平均」から「同じ曜日どうし」に変えるだけで、外れた日そのものが入れ替わること
  • AI に基準を言わせ、同じ基準を SQL で計算して突き合わせる、検算の順番とひな形

今回確かめたのはセッション数(サイトへの訪問のまとまりの数)だけです。売上と購入数の外れた日は計算していません。理由は「使うデータ」の節で説明します。

AI の異常検知とは:外れ値検出を表で頼むということ

異常検知(いつもと違うデータを見つけること)には、ふだんの範囲から飛び出た値を探す外れ値検出や、傾向が切り替わった時点を探す変化点検出などの種類があります。この記事で扱うのは、日別の表の中で「この日だけ飛び出ている」を探す外れ値検出です。

表を AI に貼って「おかしな日は?」と聞くと、AI はもっともらしい理由を添えて日付を挙げてくれます。ただ、AI がどんな物差しで「おかしい」と判断したのかは、頼み方によっては答えに書かれません。物差しが分からないと、挙がった日が本当に外れているのかを確かめられないため、まず物差しを 3 つ決めました。

「外れた日」の基準を 3 つ決める:標準偏差・四分位・曜日別

ここでは統計的手法(データの散らばりから「ふつうの範囲」を数字で決めるやり方)を 3 つ使います。機械学習のモデルは使いません。どれも SQL の関数だけで計算でき、読者が同じ結果を出せるためです。

基準ふつうの範囲の決め方特徴
(1) 標準偏差全体の平均 ± 2 標準偏差大きな値に引っ張られやすい
(2) 四分位Q1 − 1.5×IQR 〜 Q3 + 1.5×IQR極端な値に引っ張られにくい
(3) 曜日別同じ曜日の平均 ± 2 標準偏差週末の落ち込みを除ける

標準偏差は、値が平均からどれだけ散らばっているかを表す数です。BigQuery の STDDEV は STDDEV_SAMP の別名で、標本標準偏差(データの件数から 1 を引いた数で割る計算)を返します(BigQuery の統計集計関数)。件数で割る母集団標準偏差が欲しいときは STDDEV_POP を使います。

四分位は、値を小さい順に並べて 4 等分する区切りです。下から 25% の位置を Q1、75% の位置を Q3 と呼び、その差を IQR(四分位範囲)と呼びます。

四分位を計算する関数には、近似の APPROX_QUANTILES もあります。この関数は厳密な値ではなく近似の値を返し、統計的な不確かさが入ると公式に書かれています(BigQuery の近似集計関数)。境目の日を判定するには近似のずれが邪魔になるため、今回は厳密な PERCENTILE_CONT で計算しました。

使うデータ:GA4 公開サンプルの日別の表 92 日分

使ったのは、BigQuery の公開データ ga4_obfuscated_sample_ecommerce です。Google Merchandise Store の 2020 年 11 月 1 日から 2021 年 1 月 31 日までの、3 か月分の GA4 イベントデータです(Google アナリティクス 4 の BigQuery サンプル データセット)。

このデータは実際のデータに似せて難読化(中身が分からないように値を加工すること)されています。公式の説明では、難読化によって内部の整合性が少し制限される可能性があり、GA4 のデモアカウントの数字とも比べられません。なので、ここで出る数字は「やり方を試すための数字」として読んでください。

筆者が実行した問い合わせでは、全体は 92 日・4,295,584 イベントでした。日付の列 event_date は、YYYYMMDD 形式の文字列です(BigQuery Export スキーマ)。そのため、PARSE_DATE で日付の型に直してから曜日を取り出します。

WITH daily AS (
  SELECT PARSE_DATE('%Y%m%d', event_date) AS d,
    COUNT(DISTINCT IF(event_name='session_start', CONCAT(user_pseudo_id, CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id') AS STRING)), NULL)) AS sessions,
    COUNTIF(event_name='purchase') AS purchases,
    ROUND(SUM(IF(event_name='purchase', IFNULL(ecommerce.purchase_revenue_in_usd,0), 0)),2) AS revenue_usd
  FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
  GROUP BY d
)
SELECT d, FORMAT_DATE('%a', d) AS dow, sessions, purchases, revenue_usd FROM daily ORDER BY d

この SQL は筆者が実行した原文です。セッション数は、session_start イベントのユーザー ID とセッション ID の組を重複なしで数えました。

売上の列 purchase_revenue_in_usd は、購入イベントにだけ入る米ドルの金額です(BigQuery Export スキーマ)。ところが今回の結果では、売上が 2021-01-26 から急に下がって 01-27 以降は 0 近くになり、購入数は残っていました。原因を確かめられなかったため、この記事はセッション数だけで検算します。

AI への頼み方 3 通りと、挙げられた日

92 日分の「日付・曜日・セッション数」の表を AI に渡し、頼み方だけを変えて 3 回答えさせました。使ったのは Claude Sonnet 5.5 で、各頼み方 1 回だけです。コード実行や電卓は使わせず、表を読んだ暗算で答えさせました。

答えたのは、この記事の調査を担当した AI 自身です。SQL の基準を計算する前に、同じ作業の中で表だけを見て答えたもので、頼み方ごとに新しい会話で聞いたわけではありません。

P1:「おかしな日を挙げて」だけ

次は、ある EC サイトの日別セッション数です(日付, 曜日, セッション数)。
おかしな日を挙げてください。(表を貼る)

AI は 10 日を挙げました。答えの一部を原文のまま載せます。

  • 2020-12-08(7,522。表の中で最大で、前後の日より突出しています)
  • 2020-12-25(2,623。クリスマスで低いです)
  • 2020-11-08(2,132。表の中で最小です)

12 月上旬はセールなどの施策、年末年始は休日の影響が考えられます。

挙げた 10 日は、12-08・12-09・01-06・11-03・12-25・12-26・12-27・01-01・11-08・11-15 です。原因の説明(セール・クリスマス)は AI の推測で、公開データからは確かめられません。

P2:「基準と計算の途中を書いて」

次は、ある EC サイトの日別セッション数です(日付, 曜日, セッション数)。
外れた日を挙げてください。どんな基準で外れたと判断したのか、
平均などの計算の途中も書いてください。(表を貼る)

答えの一部を原文のまま載せます。

計算の途中: 92 日のセッション数の平均はおよそ 3,900、標準偏差はおよそ 1,050 です(暗算による概算です)。 範囲: 3,900 ± 2 × 1,050 = およそ 1,800〜6,000 です。 2021-01-06(5,951)は上限にわずかに届かないため、外れた日には含めません。

AI は「平均 ± 2 標準偏差」を自分で選び、12-08 と 12-09 の 2 日を挙げました。

P3:「同じ曜日どうしで比べて」

次は、ある EC サイトの日別セッション数です(日付, 曜日, セッション数)。
週末は平日より少ないので、同じ曜日どうしで比べて外れた日を挙げてください。
曜日ごとの平均と、外れたと判断した基準も書いてください。(表を貼る)

答えの一部を原文のまま載せます。

  • 2020-12-07(月。5,409。月曜の平均 3,900 より 1,500 高く、標準偏差の 2 倍を超えます)
  • 2020-12-25(金。2,623。金曜の平均 3,900 を大きく下回ります) 土曜と日曜は、低い日(2,132〜2,700 台)もすべて土日の中の通常の幅に収まるため、外れた日には含めません。

AI は 12-08・12-07・12-25・01-01 の 4 日を挙げました。

検算:SQL の基準で外れた日と何日一致したか

SQL で 3 つの基準を計算する

正解の側は、3 つの基準を SQL で計算しました。下の SQL は、筆者が実行した問い合わせの式(STDDEV_SAMP、PERCENTILE_CONT、曜日ごとの PARTITION BY)を、読者が貼って動かせる形に書き直したものです。書き直した全文も実行し、次の節の 4 日と z 値が同じになることを確かめました。

WITH daily AS (
  SELECT PARSE_DATE('%Y%m%d', event_date) AS d,
    COUNT(DISTINCT IF(event_name='session_start', CONCAT(user_pseudo_id, CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id') AS STRING)), NULL)) AS sessions
  FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
  GROUP BY d
),
s AS (
  SELECT d, FORMAT_DATE('%a', d) AS dow, sessions,
    AVG(sessions) OVER () AS m,
    STDDEV_SAMP(sessions) OVER () AS sd,
    PERCENTILE_CONT(sessions, 0.25) OVER () AS q1,
    PERCENTILE_CONT(sessions, 0.75) OVER () AS q3,
    AVG(sessions) OVER (PARTITION BY EXTRACT(DAYOFWEEK FROM d)) AS wm,
    STDDEV_SAMP(sessions) OVER (PARTITION BY EXTRACT(DAYOFWEEK FROM d)) AS wsd
  FROM daily
)
SELECT d, dow, sessions,
  ABS(sessions - m) > 2 * sd AS c1_sd,
  (sessions < q1 - 1.5 * (q3 - q1) OR sessions > q3 + 1.5 * (q3 - q1)) AS c2_iqr,
  ABS(sessions - wm) > 2 * wsd AS c3_wk,
  ROUND((sessions - m) / sd, 2) AS z_global,
  ROUND((sessions - wm) / wsd, 2) AS z_wk
FROM s
WHERE ABS(sessions - m) > 2 * sd
  OR sessions < q1 - 1.5 * (q3 - q1)
  OR sessions > q3 + 1.5 * (q3 - q1)
  OR ABS(sessions - wm) > 2 * wsd
ORDER BY d

z_global と z_wk は z 値(平均から標準偏差何個分離れているか)です。絶対値が 2 を超えると、基準 (1) や (3) で外れた日になります。

基準の境界値と、外れた日

境界値は次のとおりです。筆者は同じ日別の表を Node.js で計算し直し、すべての値が SQL の結果と一致することも確かめました。

基準下限上限外れた日数
(1) 平均 3,857.1 ± 2 × 999.71,857.75,856.63 日
(2) Q1 3,200 / Q3 4,343.251,485.16,058.12 日
(3) 曜日別(例: 月曜)2,598.55,282.02 日(全曜日の合計)

92 日のセッション数の折れ線に平均±2 標準偏差の上限 5,856.6 と下限 1,857.7 の線を重ね、上限を超えた 12 月 8 日・9 日・1 月 6 日の 3 点を青で示したグラフ

図 1 の 92 日分の値は、前の節の SQL で再現できます。図 1 のとおり、外れた日はすべて上側でした。下限を下回った日はどの基準にもなく、92 日で最も少ない日は 2020-11-08 の 2,132 でした。

日付セッション数(1)(2)(3)全体の z曜日別の z
12-07(月)5,409外れない外れない外れた1.552.19
12-08(火)7,522外れた外れた外れた3.672.69
12-09(水)6,207外れた外れた外れない2.351.85
01-06(水)5,951外れた外れない外れない2.091.57

(1)〜(3) は上の表の基準の番号です。12-09 は全体で比べると外れますが、水曜どうしで比べると外れません。12-07 は逆に、全体では外れず、月曜どうしで比べると外れます。

同じ表でも、基準を全体の平均から曜日別に変えるだけで、外れた日が入れ替わります。AI の答えを確かめるときは、AI がどの基準を使ったかを先に知る必要があります。

頼み方 3 × 基準 3 の突き合わせ

AI が挙げた日と、基準で外れた日を突き合わせました。適合率は「AI が挙げた日のうち、基準でも外れていた日の割合」、再現率は「基準で外れた日のうち、AI が挙げた日の割合」です。

頼み方基準適合率再現率
P1 おかしな日(1) 標準偏差3/10 = 30%3/3 = 100%
P1 おかしな日(2) 四分位2/10 = 20%2/2 = 100%
P1 おかしな日(3) 曜日別1/10 = 10%1/2 = 50%
P2 基準と途中(1) 標準偏差2/2 = 100%2/3 = 67%
P2 基準と途中(2) 四分位2/2 = 100%2/2 = 100%
P2 基準と途中(3) 曜日別1/2 = 50%1/2 = 50%
P3 同じ曜日(1) 標準偏差1/4 = 25%1/3 = 33%
P3 同じ曜日(2) 四分位1/4 = 25%1/2 = 50%
P3 同じ曜日(3) 曜日別2/4 = 50%2/2 = 100%

筆者が、筆者の実行した問い合わせの結果と AI の答えから計算しました。分母は 2〜10 日と小さいので、割合だけでなく分数で読んでください。

頼み方 3 通りごとに、標準偏差・四分位・曜日別の 3 基準に対する再現率を並べた横棒グラフ。P2 は全体の 2 基準で高く、P3 は曜日別で 100%

図 4 と上の表のとおり、全体の平均で比べた P2 は、全体の基準 (1)(2) との一致が多くなりました。曜日どうしで比べた P3 は、曜日別の基準 (3) との一致が多くなりました。比べ方の範囲(全体か、同じ曜日か)が同じ基準と、答えがそろう傾向が見られました(各 1 回の結果です)。

曜日別の基準で見ると、何が変わったか

曜日別の基準に対する適合率と再現率を頼み方 3 通りで比べた横棒グラフ。適合率は P1 が 10%、P2 と P3 が 50%、再現率は P3 だけが 100%

図 2 の値は、上の 9 通りの表の「(3) 曜日別」の 3 行と同じです。図 2 のとおり、「同じ曜日どうしで」と頼むと、曜日別の基準で外れた 2 日(12-07・12-08)をどちらも拾えました。ただし適合率は P2 と同じ 50% で、上がったのは再現率だけです。

P3 が挙げた 12-25(2,623)と 01-01(2,289)は、金曜の下限 2,135.4 より上で、基準では外れていませんでした。

1 つのモデルで各 1 回だけ試した結果なので、「曜日で比べると精度が上がる」とまでは言えません。言えるのは、頼み方を変えると AI が採る基準が変わり、どの基準と一致するかも変わる、ということです。

暗算の丸めが、境目の日を動かした

P2 の AI は、平均を約 3,900、標準偏差を約 1,050 と概算し、上限を約 6,000 としました。実際の上限は 5,856.6 です。2021-01-06 の 5,951 は実際の上限を約 94 上回っていましたが、AI の概算の上限には届かず、外されました。

P2 が四分位の基準と 100% / 100% で一致したのは、偶然と考えられます。AI が名乗った基準は標準偏差で、丸めた上限 6,000 が、たまたま四分位の上限 6,058.1 に近かったためです。

つまずきやすい点:週末の落ち込みや祝日を異常と数える

「低い日」は、全体で見ても曜日で見ても外れていなかった

P1 が挙げた 10 日のうち、どの基準でも外れていない 7 日の内訳は、土日 4 日、金曜 2 日(12-25・01-01)、火曜 1 日(11-03)でした。低い側で挙げた 6 日の z 値は、全体で −1.21〜−1.73、曜日別で −0.50〜−1.82 で、どれも −2 に届きません(筆者が日別の表から計算)。

曜日ごとの平均は、火曜が 4,602.0 で最も高く、日曜が 2,857.6 で最も低い値でした。この差は、全体の標準偏差 999.7 の約 1.7 倍あります(筆者計算)。週末の少なさは毎週くり返す「ふつうの変動」なので、全体の平均と比べて「低い」と言うだけでは、外れた日の根拠になりません。

AI が祝日や週末を「おかしな日」に挙げたときは、同じ曜日の平均と標準偏差を出して、z 値が 2 を超えるかを確かめてください。今回は、AI が挙げた低い日のうち、この条件を満たす日は 1 日もありませんでした。

標本か母集団かで、境目の日が変わることがある

STDDEV は標本標準偏差を返し、STDDEV_POP は母集団標準偏差を返します(BigQuery の統計集計関数)。今回の 92 日では、母集団の標準偏差は 994.3 で、上限は 5,845.7 になりました(筆者計算)。外れた日は同じ 3 日で、結果は変わりませんでした。

ただし、日数が少ない表や境目ぎりぎりの日がある表では、どちらで割るかで判定が変わる可能性があります。AI に頼むときも、SQL を書くときも、どちらを使ったかを書いておくと検算が速くなります。

外れた理由は、データだけでは分からない

12 月上旬の急増について、AI は「セールなどの施策」と推測しました。しかし、公開データには施策の記録がないため、この推測は確かめられません。外れた日を見つけることと、その理由を説明することは、別の作業として扱ってください。

検算しやすい頼み方のひな形

ここまでの結果から、検算しやすい答えをもらうための頼み方をまとめます。ポイントは、AI に基準と境界値を数字で書かせることです。

次は、ある EC サイトの日別セッション数です(日付, 曜日, セッション数)。
外れた日を挙げてください。条件は次のとおりです。
1. 基準は「同じ曜日の平均 ± 2 標準偏差(標本標準偏差)」とする
2. 曜日ごとに、日数・平均・標準偏差・下限・上限を表で書く
3. 外れた日ごとに、その曜日の z 値を書く
4. 境界値の計算に自信がない日は「境目」と書いて分ける
5. 外れた理由の推測は書かない
(表を貼る)

AI が返した境界値の表は、上の SQL の s から曜日ごとの wm と wsd を取り出して、並べて比べます。曜日ごとの境界値が合えば、外れた日の判定もそろうはずです。

AI に外れた日を頼み、基準を言わせ、同じ基準を SQL で計算し、最後に曜日どうしで比べ直すまでの 4 段階を上から下へ並べたフロー図

図 3 の順番で確かめると、AI の答えのどこがずれたかを切り分けられます。

  1. AI に表を貼り、外れた日と理由を挙げてもらう
  2. 基準と境界値を数字で書かせる
  3. 同じ基準を SQL で計算し、境界値と日付を比べる
  4. 曜日どうしの基準でも計算し直し、入れ替わる日を確かめる

AI に外れた日を頼むときは、基準と境界値を数字で書かせ、同じ基準を SQL で計算して突き合わせてください。基準が書かれていない答えは、正しいか間違いかを判定できないためです。

SQL そのものを AI に書かせる場合の検算は、AI に書かせた GA4 の BigQuery SQL を検算した記事 で扱っています。表の合計や割合を AI に計算させるときの検算は、集計表の計算を行数別に検算したプロンプト集 が参考になります。

今回の条件と、確かめていないこと

  • AI は Claude Sonnet 5.5 の 1 モデルで、各頼み方 1 回だけです。他のモデルや、くり返したときのばらつきは確かめていません。
  • 答えは、記事の調査を担当した AI が、同じ作業の中で表だけを見て作ったものです。頼み方ごとに新しい会話で聞いたわけではなく、答えた AI は頼み方を比べる狙いを知っていました。読者がチャットに表を貼って同じ頼み方をしても、同じ日が挙がるとは限りません。
  • AI にはコード実行を許していません。コードを実行できる AI なら、P2 の丸めのずれは起きない可能性がありますが、試していません。
  • APPROX_QUANTILES の近似の誤差の大きさは、公式ページから数値で確認できませんでした。
  • 売上と購入数の外れた日は計算していません。

同じ日別の表を使って、AI の予測を実績で確かめた記録は、AI に来月のセッション数を予測させて実績で検算した記事 にまとめています。

まとめ

  • 「おかしな日を挙げて」だけでは、AI が挙げた 10 日のうち 7 日がどの基準でも外れていませんでした。低い日として挙げた日は、z 値がどれも −2 に届きませんでした。
  • 基準を全体から曜日別に変えると、外れた日が入れ替わります(12-09 と 12-07)。全体で比べた答えは全体の基準と、曜日で比べた答えは曜日別の基準と一致が多くなりました。
  • 検算しやすいのは、基準と境界値を数字で書かせる頼み方です。同じ基準を SQL で計算して、境界値と日付を突き合わせてください。

次にやること

外れた日を毎週の数字の中でどう見せるかは、ダッシュボードの一覧で、公開データから作った例をご覧ください。外れた日を判定する SQL が基準どおりか、第三者の目で確かめたい場合は、BigQuery SQL レビューパックもご利用いただけます。

参考にした一次情報