好的,我把“路線A:分類建模擇時”的代碼按功能分段給出,并為每段配上簡明解釋。你可以將這些段落依次粘貼到已完成清洗后的 df
變量之后直接運行。
0. 依賴導入(一次即可)
作用:導入所需庫;后續各段使用。
import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold, GridSearchCV
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import roc_auc_score, brier_score_loss
import matplotlib.pyplot as plt
1. 構造標簽與選擇特征
作用:
- 將濃度閾值0.04轉為“是否達標”的二分類標簽。
- 選擇用于建模的特征列(存在才會使用)。
df_cls = df.copy()
df_cls['達標'] = (df_cls['Y染色體濃度'] >= 0.04).astype(int)
feat_all = ['檢測孕周','孕婦BMI','年齡','身高','體重']
features = [c for c in feat_all if c in df_cls.columns]
X = df_cls[features]
y = df_cls['達標']
2. 模型訓練:多項式特征 + 標準化 + 邏輯回歸 + 概率校準
作用:
- 用多項式/交互項捕捉非線性。
- 標準化提升邏輯回歸穩定性。
- 網格搜索選擇最優多項式階數與C。
- 概率校準(isotonic)提升預測概率的可信度。
pipe = Pipeline([('poly', PolynomialFeatures(degree=2, include_bias=False)),('scaler', StandardScaler()),('clf', LogisticRegression(max_iter=1000, solver='lbfgs', class_weight='balanced'))
])
param = {'poly__degree': [1, 2], 'clf__C': [0.5, 1.0, 5.0]}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
grid = GridSearchCV(pipe, param, cv=cv, scoring='roc_auc', n_jobs=-1)
grid.fit(X, y)calib = CalibratedClassifierCV(grid.best_estimator_, cv=5, method='isotonic')
calib.fit(X, y)p_hat = calib.predict_proba(X)[:,1]
auc = roc_auc_score(y, p_hat)
brier = brier_score_loss(y, p_hat)
print(f'AUC={auc:.3f}, Brier={brier:.4f}, BestParams={grid.best_params_}')
3. BMI 分組
作用:
- 以醫學常用區間對 BMI 分組,便于組內擇時與解釋。
bins = [0,28,32,36,40,np.inf]
labels = ['[0,28)','[28,32)','[32,36)','[36,40)','40+']
df_cls['BMI組'] = pd.cut(df_cls['孕婦BMI'], bins=bins, labels=labels, right=False)
4. 概率閾值法擇時(核心)
作用:
- 在每個 BMI 組,用“組內中位特征”構造代表個體。
- 在孕周網格 t∈[10,25] 上預測達標概率曲線 P(t)。
- 取最早使 P(t)≥q(如0.9)的孕周為推薦時點。
weeks = np.arange(10, 25.01, 0.1)
q = 0.9rows_q = []
for g in df_cls['BMI組'].dropna().unique():sub = df_cls[df_cls['BMI組']==g]if sub.empty: continuemed = sub[features].median(numeric_only=True)gridX = pd.DataFrame({'檢測孕周': weeks})for col in features:if col != '檢測孕周':gridX[col] = med.get(col, sub[col].median())p_curve = calib.predict_proba(gridX[features])[:,1]idx = np.where(p_curve >= q)[0]best_week = weeks[idx[0]] if len(idx)>0 else np.nanrows_q.append({'BMI組': str(g), f'推薦孕周_閾值q={q}': best_week})res_q = pd.DataFrame(rows_q).sort_values('BMI組')
print('\n按BMI組的推薦孕周(基于概率閾值法)')
print(res_q)
5. 風險最小化擇時(可與閾值法交叉驗證)
作用:
- 定義簡單風險 R(t)=w1·(1?P(t)) + w2·早檢懲罰(max(12?t,0))。
- 找每組使風險最小的孕周,作為備選/對照的推薦時點。
w1, w2 = 1.0, 0.1
rows_risk = []
for g in df_cls['BMI組'].dropna().unique():sub = df_cls[df_cls['BMI組']==g]if sub.empty:continuemed = sub[features].median(numeric_only=True)gridX = pd.DataFrame({'檢測孕周': weeks})for col in features:if col != '檢測孕周':gridX[col] = med.get(col, sub[col].median())p_curve = calib.predict_proba(gridX[features])[:,1]early_pen = np.maximum(12 - weeks, 0)risk = w1*(1 - p_curve) + w2*early_pent_star = weeks[np.argmin(risk)]rows_risk.append({'BMI組': str(g), '最小風險孕周': t_star})res_risk = pd.DataFrame(rows_risk).sort_values('BMI組')
print('\n按BMI組的最小風險孕周(簡單風險函數)')
print(res_risk)
6. 概率曲線可視化
作用:
- 展示各 BMI 組的 P(t) 曲線與閾值線,便于說明推薦時點的依據。
plt.figure(figsize=(10,6))
for g in df_cls['BMI組'].dropna().unique():sub = df_cls[df_cls['BMI組']==g]if sub.empty:continuemed = sub[features].median(numeric_only=True)gridX = pd.DataFrame({'檢測孕周': weeks})for col in features:if col != '檢測孕周':gridX[col] = med.get(col, sub[col].median())p_curve = calib.predict_proba(gridX[features])[:,1]plt.plot(weeks, p_curve, label=str(g))
plt.axhline(q, color='gray', linestyle='--', label=f'閾值q={q}')
plt.xlabel('檢測孕周'); plt.ylabel('達標概率P'); plt.title('各BMI組的達標概率曲線P(t)')
plt.legend(); plt.tight_layout(); plt.show()
7. 簡易穩健性(誤差敏感性)分析
作用:
- 對閾值0.04做隨機抖動,重復計算推薦孕周。
- 給出區間估計,體現方案對誤差的穩健性。
np.random.seed(42)
B = 200
sigma_thr = 0.004 # 閾值抖動(可用重復檢測估計更精確的σ)
rows_ci = []for g in df_cls['BMI組'].dropna().unique():sub = df_cls[df_cls['BMI組']==g]if sub.empty:continuemed = sub[features].median(numeric_only=True)t_list = []for _ in range(B):gridX = pd.DataFrame({'檢測孕周': weeks})for col in features:if col != '檢測孕周':gridX[col] = med.get(col, sub[col].median())p_curve = calib.predict_proba(gridX[features])[:,1]thr = np.random.normal(0.04, sigma_thr)adj = np.clip(p_curve - 0.5*((max(thr-0.04,0))/0.04), 0, 1) # 保守調整idx = np.where(adj >= q)[0]t_list.append(weeks[idx[0]] if len(idx)>0 else np.nan)arr = np.array([t for t in t_list if np.isfinite(t)])if len(arr)>0:rows_ci.append({'BMI組': str(g),'推薦孕周_中位數': np.median(arr),'2.5%': np.percentile(arr, 2.5),'97.5%': np.percentile(arr, 97.5),'有效樣本': len(arr)})res_ci = pd.DataFrame(rows_ci).sort_values('BMI組')
print('\n穩健性分析:推薦孕周的模擬區間')
print(res_ci)
小結
- 第1–2段得到“可信的達標概率模型”。
- 第3–4段按 BMI 分組并給出“最早 P≥q 的推薦孕周”。
- 第5段提供“最小風險孕周”作為對照,增強決策穩健性。
- 第6–7段用圖與區間說明“為什么選這個時點”和“對誤差是否穩健”。
需要的話,我可以把以上分段自動合并進你當前的 game.py
并一次性運行,輸出結果表與圖。