σμ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.8·~40 分初級

groupbyと集計

ロードマップ上の位置モジュール 2/11 · トピック 8/20
2.8 groupbyと集計
—ラボスコア— / 100合計XP—連続日数—

分割・適用・結合

多くの問いはグループごとに1つの数を求めます。セクターごとのエクスポージャー、戦略ごとの平均リターン、日ごとの約定数などです。パターンは常に同じ分割・適用・結合(split-apply-combine)です。df.groupby("sector") は列の値で行をグループに分割し、.sum() などの集計を各グループに適用し、pandas がその結果をグループのキーでインデックスされた1つの表に結合します。これがなければ合計を溜める辞書を使ったループを書くことになりますが、これなら問い全体が1行で済みます:df.groupby("sector")["value"].sum()。

import pandas as pd

df = pd.DataFrame({
    "symbol": ["AAPL", "MSFT", "GOOG", "TSLA", "F"],
    "sector": ["tech", "tech", "tech", "auto", "auto"],
    "value":  [36850.0, 20510.0, 51300.0, 30120.0, 9000.0],
    "ret":    [0.012, -0.004, 0.021, 0.03, -0.01],
})

print(df.groupby("sector")["value"].sum())
# sector
# auto      39120.0
# tech     108660.0
# Name: value, dtype: float64

print(df.groupby("sector").size().to_dict())             # {'auto': 2, 'tech': 3}  rows per group
print(df.groupby("sector")["ret"].mean().round(4).to_dict())   # {'auto': 0.01, 'tech': 0.0097}

agg で複数の要約を一度に

.agg() は複数の要約を1回で計算し、結果の列に名前を付けられます:df.groupby("sector").agg(count=("value", "size"), mean_return=("ret", "mean"), max_value=("value", "max"))。各キーワードが新しい列名で、タプルは読む列と適用する関数("sum"・"mean"・"min"・"max"・"std"・"size"・"first" など)を表します。groupby(["sector", "day"]) のように複数列で同時にグループ化すると、2階層のインデックスの結果になります。グループのキーは既定でソートされ、キーが欠損(NaN)の行は除かれます。

import pandas as pd

df = pd.DataFrame({
    "symbol": ["AAPL", "MSFT", "GOOG", "TSLA", "F"],
    "sector": ["tech", "tech", "tech", "auto", "auto"],
    "value":  [36850.0, 20510.0, 51300.0, 30120.0, 9000.0],
    "ret":    [0.012, -0.004, 0.021, 0.03, -0.01],
})

summary = df.groupby("sector").agg(
    count=("value", "size"),
    mean_return=("ret", "mean"),
    max_value=("value", "max"),
)
print(summary)
#         count  mean_return  max_value
# sector
# auto        2     0.010000    30120.0
# tech        3     0.009667    51300.0

print(df.groupby("sector")["ret"].agg(["min", "max"]))   # a list of function names
#           min    max
# sector
# auto   -0.010  0.030
# tech   -0.004  0.021

transform:各行にグループの統計量を付ける

集計は各グループを1行に縮めます。transform は元の形を保ちます。グループの統計量を計算し、グループの各行に繰り返して、元のインデックスに揃えます。「行の値をそのグループと比べる」ための道具で、ポジションのセクター内シェアは df["value"] / df.groupby("sector")["value"].transform("sum")、グループ内の平均除去は x - x.groupby(g).transform("mean") です。各セクターで最も成績の良い銘柄のように、グループごとに行全体を選ぶには、idxmax() で各グループの最大値のインデックスラベルを得て、.loc でその行を選びます。

import pandas as pd

df = pd.DataFrame({
    "symbol": ["AAPL", "MSFT", "GOOG", "TSLA", "F"],
    "sector": ["tech", "tech", "tech", "auto", "auto"],
    "value":  [36850.0, 20510.0, 51300.0, 30120.0, 9000.0],
    "ret":    [0.012, -0.004, 0.021, 0.03, -0.01],
})

totals = df.groupby("sector")["value"].transform("sum")      # one value per original row
print(totals.tolist())                       # [108660.0, 108660.0, 108660.0, 39120.0, 39120.0]
print((df["value"] / totals).round(3).tolist())   # [0.339, 0.189, 0.472, 0.77, 0.23]

best = df.loc[df.groupby("sector")["ret"].idxmax()].set_index("sector")[["symbol", "ret"]]
print(best)
#        symbol    ret
# sector
# auto     TSLA  0.030
# tech     GOOG  0.021
import pandas as pd

t = pd.DataFrame({
    "day":    ["mon", "mon", "tue", "tue"],
    "sector": ["tech", "auto", "tech", "tech"],
    "value":  [1.0, 2.0, 3.0, 4.0],
})

# Two keys give a two-level index
print(t.groupby(["day", "sector"])["value"].sum())
# day  sector
# mon  auto      2.0
#      tech      1.0
# tue  tech      7.0
# Name: value, dtype: float64

# reset_index() turns the keys back into ordinary columns
print(t.groupby(["day", "sector"])["value"].sum().reset_index())
#    day sector  value
# 0  mon   auto    2.0
# 1  mon   tech    1.0
# 2  tue   tech    7.0
メソッドがあるならグループをループしないfor name, group in df.groupby("sector"): でループもできますが、これは少数のグループを表示・描画するのに向いており、数値計算では組み込みの集計と transform のほうが Python のループや関数を渡す apply より短く、はるかに高速です。結果のインデックス(グループのキー、通常はソート済み)と dtype を確認し、size は行数を、count は欠損でない値の数を数えることを覚えておきましょう。
  • df.groupby(キー)[col].sum()(mean・max・size なども):グループごとに1つの値で、キーがインデックス
  • .agg(名前=(列, 関数), ...) で名前付きの複数の要約を一度に作る
  • transform は行数を保つ(シェア・平均除去)。idxmax + .loc でグループごとの行全体を選ぶ

次は表の結合です。価格と参照データのように、キーを共有する表を組み合わせます。

ラボ:採点される内容

  • total_by_sector でセクターごとのエクスポージャーを合計する25点
  • summary_by_sector で全セクターを1つの表に要約する25点
  • share_of_sector で各ポジションをセクター内で比較する25点
  • best_per_sector でセクターごとの最良銘柄を選ぶ25点
エクスプローラー · lab-41-1
▾ lab-41-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-41-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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