σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 2 · 市場データのためのNumPyとpandas›トピック 2.16·~40 分中級

外れ値の検出

ロードマップ上の位置モジュール 2/11 · トピック 16/20
2.16 外れ値の検出
—ラボスコア— / 100合計XP—連続日数—

正しいはずのない値

大型株の日次リターンが +30% なら、買収か、株式分割の反映漏れや小数点の位置ミスなどのデータ誤りです。外れ値とは、他の値から十分に離れていて判断が必要な値のことで、残す・直す・除くのどれかを選びます。邪魔だからという理由だけで消してはいけません。本物のショックこそリスクモデルが見るべきものだからです。最初の一歩は、明確で再現できるルールで外れ値を見える化することです。

Zスコアのルールと、それが失敗する理由

古典的なルールは、平均から標準偏差の2〜3倍以上離れた値に印を付けます:z = (x - mean) / std。単純で、きれいなデータではうまく働きます。しかし外れ値自身が平均、特に標準偏差を膨らませるため、ルールから隠れてしまいます。下の例では +30% の日のZスコアは2.85にとどまり、しきい値3なら検出されません。誤りが極端なほど、自分を守ってしまうのです。

import pandas as pd

returns = pd.Series([0.004, -0.002, 0.001, 0.003, -0.004, 0.002, 0.30, -0.001, 0.0, 0.002])

z = (returns - returns.mean()) / returns.std()
print(z.round(2).tolist())
# [-0.28, -0.34, -0.31, -0.29, -0.36, -0.3, 2.85, -0.33, -0.32, -0.3]

print(returns.mean().round(4), returns.std().round(4))   # 0.0305 0.0947   one day moved the mean tenfold
print(returns.drop(6).std().round(4))                    # 0.0026          the std without that day

頑健なルール:IQR と MAD

頑健な統計量は、探している外れ値にほとんど動かされません。四分位範囲 IQR = Q3 - Q1 はデータの中央半分の広がりを測り、Q1 - 1.5 * IQR より下または Q3 + 1.5 * IQR より上の値は箱ひげ図の柵の外です。MAD(絶対偏差の中央値)は標準偏差を中央値に置き換えたもので、修正Zスコア 0.6745 * (x - median) / MAD は通常のZスコアと同じように読みます(3.5がよく使われる基準)。どちらも +30% の日を難なく検出します。

# The fences of a box plot
q1, q3 = returns.quantile(0.25), returns.quantile(0.75)
spread = q3 - q1
lower, upper = q1 - 1.5 * spread, q3 + 1.5 * spread
print(round(lower, 4), round(upper, 4))                    # -0.006 0.008
print(returns[(returns < lower) | (returns > upper)].to_dict())   # {6: 0.3}

# The modified z-score, built on the median
median = returns.median()
mad = (returns - median).abs().median()
modified_z = 0.6745 * (returns - median) / mad
print(modified_z.round(1).tolist())   # [0.8, -1.2, -0.2, 0.5, -1.9, 0.2, 100.7, -0.8, -0.5, 0.2]

ウィンザー処理:削除せず上限・下限で抑える

行を削除すると、他の列の情報も失われ、日付の間隔も崩れます。ウィンザー処理は全行を残し、極端な値を選んだ分位点(たとえば1%と99%)まで引き戻します。1%点より下の値は1%点に、99%点より上の値は99%点になります。pandas では quantile から求めた上限・下限を使った clip(lower, upper) です。系列の形は保たれ、極端な値が平均や回帰に及ぼす影響は弱まります。

low, high = returns.quantile(0.1), returns.quantile(0.9)
print(round(low, 4), round(high, 4))             # -0.0022 0.0336

clipped = returns.clip(lower=low, upper=high)
print(clipped.round(4).tolist())
# [0.004, -0.002, 0.001, 0.003, -0.0022, 0.002, 0.0336, -0.001, 0.0, 0.002]
#                                 ^ raised                  ^ the 30% day pulled back to the 90% quantile
# Flag with a rule, then look at what is flagged before deciding anything
mask = z.abs() > 2
print(returns[mask].to_dict())                 # {6: 0.3}        the +30% day
print(returns[~mask].std().round(4))           # 0.0026          the volatility of the remaining days
ルールで決め、記録に残す外れ値のルールは事実ではなくモデリング上の選択です。何かを変える前に、印の付いた日が何なのか(ニュース、分割、誤ったティック)を確認し、元の列を残し、使ったしきい値を記録しましょう。しきい値が違えばバックテストの結果も変わります。
  • Zスコアのルール abs((x - mean) / std) > k は単純だが、外れ値が平均と標準偏差を膨らませ、隠れることがある
  • 頑健なルールは分位点と中央値を使う:IQRの柵(Q1 - 1.5 IQR、Q3 + 1.5 IQR)と MAD に基づく修正Zスコア
  • 分位点の上下限による clip(lower, upper)(ウィンザー処理)は行を残す。中身を確認せずにショックを消さない

次はコーポレートアクションです。過去の価格を今日の価格と比較できなくする、分割と配当を扱います。

ラボ:採点される内容

  • zscore_outliers で平均から遠いリターンに印を付ける25点
  • iqr_bounds で箱ひげ図の境界を計算する25点
  • mad_outliers で頑健なスコアにより外れ値に印を付ける25点
  • winsorize で極端な値を抑える25点
エクスプローラー · lab-49-1
▾ lab-49-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-49-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-49-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける