{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ad31c402",
   "metadata": {},
   "source": [
    "# 欺诈检测 — 不平衡学习实战\n",
    "\n",
    "对应案例: [欺诈检测](/docs/case-studies/finance/fraud-detection-case/)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3eda2100",
   "metadata": {},
   "source": [
    "## 1. 环境准备"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff8552db",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np, pandas as pd, matplotlib.pyplot as plt\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import classification_report, roc_auc_score, average_precision_score, precision_recall_curve\n",
    "from lightgbm import LGBMClassifier\n",
    "from imblearn.over_sampling import SMOTE\n",
    "np.random.seed(42)\n",
    "print('Ready')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b30e1ac0",
   "metadata": {},
   "source": [
    "## 2. 模拟不平衡数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "629f37ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "n=50000; fraud_rate=0.01\n",
    "n_f=int(n*fraud_rate); n_n=n-n_f\n",
    "normal=pd.DataFrame({'amount':np.random.lognormal(4,0.8,n_n).clip(1,50000),\n",
    "'hour':np.random.randint(0,24,n_n),'dist':np.random.exponential(5,n_n),\n",
    "'device_days':np.random.exponential(365,n_n),'fails':np.random.poisson(0.5,n_n),'fraud':0})\n",
    "fraud=pd.DataFrame({'amount':np.random.lognormal(5.5,0.6,n_f).clip(10,100000),\n",
    "'hour':np.random.choice(24,n_f,p=[0.02]*8+[0.06]*8+[0.04]*8),\n",
    "'dist':np.random.exponential(50,n_f),'device_days':np.random.exponential(30,n_f),\n",
    "'fails':np.random.poisson(2,n_f),'fraud':1})\n",
    "df=pd.concat([normal,fraud]).sample(frac=1,random_state=42).reset_index(drop=True)\n",
    "print(f'N={len(df)}, fraud={df.fraud.mean():.2%}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "066c0c3e",
   "metadata": {},
   "source": [
    "## 3. 建模"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff2cd4bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "X=df[['amount','hour','dist','device_days','fails']]; y=df['fraud']\n",
    "X_tr,X_te,y_tr,y_te=train_test_split(X,y,test_size=0.2,stratify=y,random_state=42)\n",
    "sm=SMOTE(random_state=42)\n",
    "X_sm,y_sm=sm.fit_resample(X_tr,y_tr)\n",
    "model=LGBMClassifier(n_estimators=100,random_state=42,verbose=-1)\n",
    "model.fit(X_sm,y_sm)\n",
    "y_pred=model.predict(X_te); y_proba=model.predict_proba(X_te)[:,1]\n",
    "print(classification_report(y_te,y_pred,target_names=['Normal','Fraud']))\n",
    "print(f'ROC-AUC:{roc_auc_score(y_te,y_proba):.4f}  PR-AUC:{average_precision_score(y_te,y_proba):.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a969e6a3",
   "metadata": {},
   "source": [
    "## 4. PR 曲线（不平衡场景更重要）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf981e5a",
   "metadata": {},
   "outputs": [],
   "source": [
    "prec,rec,_=precision_recall_curve(y_te,y_proba)\n",
    "plt.plot(rec,prec,label=f'PR-AUC={average_precision_score(y_te,y_proba):.3f}')\n",
    "plt.axhline(y_te.mean(),color='gray',ls='--',label=f'Baseline ({y_te.mean():.1%})')\n",
    "plt.xlabel('Recall'); plt.ylabel('Precision')\n",
    "plt.title('Precision-Recall Curve'); plt.legend(); plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa6eea38",
   "metadata": {},
   "source": [
    "---\n",
    "*更多分析见 [案例文档](/docs/case-studies/finance/fraud-detection-case/)*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
