{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":131653,"databundleVersionId":15802687}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# AI-EMT Türkiye — CatBoost Solution\n\nThis notebook trains a CatBoost model to predict:\n- adverse_outcome\n- readmission_30d\n\nDataset: train + supplemental_data  \nValidation: 5-fold stratified CV","metadata":{}},{"cell_type":"markdown","source":"## Navigation\n\n- [1. Setup](#setup)\n- [2. Data Loading](#loading)\n- [3. Feature Engineering](#features)\n- [4. Dataset Preparation](#dataset)\n- [5. Training (GPU)](#training)\n- [6. Submission](#submission)","metadata":{}},{"cell_type":"markdown","source":"<a id=\"setup\"></a>\n## 1. Setup","metadata":{}},{"cell_type":"code","source":"import os, gc, warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport numpy as np\nimport pandas as pd\n\nSEED = 42\nnp.random.seed(SEED)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id=\"loading\"></a>\n## 2. Data Loading","metadata":{}},{"cell_type":"code","source":"INPUT = \"/kaggle/input/ai-emergency-medicine-turkiye\"\nif not os.path.exists(INPUT):\n    INPUT = \".\"\n\ntrain_path = os.path.join(INPUT, \"train.csv\")\ntest_path  = os.path.join(INPUT, \"test.csv\")\nsupp_path  = os.path.join(INPUT, \"supplemental_data.csv\")\nsub_path   = os.path.join(INPUT, \"sample_submission.csv\")\n\nprint(\"Loading data...\")\ntrain = pd.read_csv(train_path)\ntest  = pd.read_csv(test_path)\nsample_sub = pd.read_csv(sub_path)\n\nif os.path.exists(supp_path):\n    supp = pd.read_csv(supp_path)\n    if set(supp.columns) == set(train.columns):\n        train = pd.concat([train, supp], axis=0, ignore_index=True)\n        print(f\"Added supplemental_data: train -> {train.shape}\")\n    del supp\n    gc.collect()\n\nTARGETS = [\"adverse_outcome\", \"readmission_30d\"]\nID_COL = \"encounter_id\"\n\nassert ID_COL in train.columns and ID_COL in test.columns, \"encounter_id missing\"\nfor t in TARGETS:\n    assert t in train.columns, f\"Target {t} missing in train\"\n\nfor t in TARGETS:\n    train[t] = train[t].astype(np.int8)\n\nprint(\"Shapes:\")\nprint(\"  train:\", train.shape)\nprint(\"  test :\", test.shape)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id=\"features\"></a>\n## 3. Feature Engineering","metadata":{}},{"cell_type":"code","source":"LAB_MNAR = [\n    \"troponin_i_ngml\", \"bnp_pgl\", \"d_dimer_mgfl\", \"lactate_mmoll\",\n    \"lipase_ul\", \"procalcitonin_ngl\", \"albumin_gdl\",\n]\n\nEXTRA_NUM = [\n    \"crp_mgl\", \"creatinine_mgdl\", \"wbc_k_ul\", \"platelets_k_ul\", \"hemoglobin_gdl\",\n    \"glucose_mgdl\", \"sbp_mmhg\", \"dbp_mmhg\", \"heart_rate_bpm\",\n    \"respiratory_rate_rpm\", \"spo2_pct\", \"temperature_celsius\",\n    \"sofa_score_approx\", \"mews_score\", \"news2_score\", \"gcs_total\",\n    \"charlson_comorbidity_index\", \"ed_los_minutes\", \"age\", \"bmi\",\n]\n\ndef add_features(df: pd.DataFrame) -> pd.DataFrame:\n    df = df.copy()\n\n    for c in LAB_MNAR:\n        if c in df.columns:\n            df[f\"{c}_isna\"] = df[c].isna().astype(np.int8)\n\n    if \"heart_rate_bpm\" in df.columns and \"sbp_mmhg\" in df.columns:\n        df[\"shock_index\"] = df[\"heart_rate_bpm\"] / df[\"sbp_mmhg\"].clip(lower=1)\n    if \"sbp_mmhg\" in df.columns and \"dbp_mmhg\" in df.columns:\n        df[\"map_calc\"] = (df[\"sbp_mmhg\"] + 2 * df[\"dbp_mmhg\"]) / 3.0\n        if \"heart_rate_bpm\" in df.columns:\n            df[\"modified_shock_index\"] = df[\"heart_rate_bpm\"] / df[\"map_calc\"].clip(lower=1)\n\n    if \"age\" in df.columns and \"charlson_comorbidity_index\" in df.columns:\n        df[\"age_x_charlson\"] = df[\"age\"] * df[\"charlson_comorbidity_index\"]\n\n    if \"sofa_score_approx\" in df.columns and \"triage_level\" in df.columns:\n        df[\"sofa_x_triage\"] = df[\"sofa_score_approx\"] * df[\"triage_level\"]\n\n    if \"mews_score\" in df.columns and \"triage_level\" in df.columns:\n        df[\"mews_x_triage\"] = df[\"mews_score\"] * df[\"triage_level\"]\n\n    if \"ed_los_minutes\" in df.columns:\n        df[\"ed_los_hours\"] = df[\"ed_los_minutes\"] / 60.0\n\n    for c in [\"troponin_i_ngml\", \"bnp_pgl\", \"d_dimer_mgfl\", \"crp_mgl\", \"procalcitonin_ngl\", \"lactate_mmoll\"]:\n        if c in df.columns:\n            df[f\"{c}_log1p\"] = np.log1p(df[c].fillna(0))\n\n    com_cols = [c for c in df.columns if c.startswith(\"comorbidity_\")]\n    med_cols = [c for c in df.columns if c.startswith(\"med_\")]\n    proc_cols = [c for c in df.columns if c.startswith(\"proc_\")]\n    if com_cols:\n        df[\"num_comorbidities\"] = df[com_cols].sum(axis=1)\n    if med_cols:\n        df[\"num_medications\"] = df[med_cols].sum(axis=1)\n    if proc_cols:\n        df[\"num_procedures\"] = df[proc_cols].sum(axis=1)\n\n    present_labs = [c for c in LAB_MNAR if c in df.columns]\n    if present_labs:\n        df[\"lab_completeness\"] = df[present_labs].notna().sum(axis=1) / float(len(present_labs))\n\n    return df\n\nprint(\"Engineering features...\")\ntrain_fe = add_features(train)\ntest_fe  = add_features(test)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id=\"dataset\"></a>\n## 4. Dataset Preparation","metadata":{}},{"cell_type":"code","source":"DROP_COLS = [\n    ID_COL,\n    \"disposition\", \"triage_label\", \"age_group\", \"arrival_day_of_week\",\n    \"latitude\", \"longitude\", \"pandemic_period\",\n    \"primary_diagnosis\",\n]\nDROP_COLS = [c for c in DROP_COLS if c in train_fe.columns]\n\ny = train_fe[TARGETS].copy()\nX = train_fe.drop(columns=TARGETS + DROP_COLS, errors=\"ignore\")\nX_test = test_fe.drop(columns=DROP_COLS, errors=\"ignore\")\n\ncat_cols = X.select_dtypes(include=[\"object\"]).columns.tolist()\nKNOWN_CAT = [\n    \"city\", \"nuts2_region\", \"urban_rural\", \"hospital_type\", \"bed_count_category\",\n    \"arrival_mode\", \"arrival_season\", \"chief_complaint\", \"insurance_type\",\n    \"primary_icd10_code\", \"consult_specialty\",\n]\nfor c in KNOWN_CAT:\n    if c in X.columns and c not in cat_cols:\n        cat_cols.append(c)\n\nfor c in cat_cols:\n    X[c] = X[c].astype(str)\n    X_test[c] = X_test[c].astype(str)\n\ncat_features = [X.columns.get_loc(c) for c in cat_cols if c in X.columns]\nprint(f\"Total features: {X.shape[1]}\")\nprint(f\"Categorical features: {len(cat_features)}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id=\"training\"></a>\n## 5. Training (GPU)","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import roc_auc_score\nfrom catboost import CatBoostClassifier, Pool\n\nstrat = (y[\"adverse_outcome\"].astype(int) * 2 + y[\"readmission_30d\"].astype(int)).astype(int)\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=SEED)\n\nCB_PARAMS = dict(\n    loss_function=\"Logloss\",\n    eval_metric=\"AUC\",\n    iterations=6000,\n    learning_rate=0.03,\n    depth=8,\n    l2_leaf_reg=6.0,\n    random_strength=0.8,\n    bagging_temperature=0.7,\n    border_count=128,\n    od_type=\"Iter\",\n    od_wait=300,\n    random_seed=SEED,\n    verbose=300,\n    task_type=\"GPU\",\n    devices=\"0\",\n    max_ctr_complexity=4\n)\n\noof = {t: np.zeros(len(X), dtype=np.float32) for t in TARGETS}\npred_test = {t: np.zeros(len(X_test), dtype=np.float32) for t in TARGETS}\n\nfor fold, (tr_idx, va_idx) in enumerate(skf.split(X, strat), 1):\n    print(f\"\\n========== Fold {fold}/5 ==========\")\n\n    X_tr, X_va = X.iloc[tr_idx], X.iloc[va_idx]\n    y_tr, y_va = y.iloc[tr_idx], y.iloc[va_idx]\n\n    test_pool = Pool(X_test, cat_features=cat_features)\n\n    for t in TARGETS:\n        train_pool = Pool(X_tr, label=y_tr[t].values, cat_features=cat_features)\n        valid_pool = Pool(X_va, label=y_va[t].values, cat_features=cat_features)\n\n        model = CatBoostClassifier(**CB_PARAMS)\n        model.fit(train_pool, eval_set=valid_pool, use_best_model=True)\n\n        oof[t][va_idx] = model.predict_proba(valid_pool)[:, 1].astype(np.float32)\n        pred_test[t] += (model.predict_proba(test_pool)[:, 1].astype(np.float32) / skf.n_splits)\n\n        auc = roc_auc_score(y_va[t].values, oof[t][va_idx])\n        print(f\"[Fold {fold}] AUC {t}: {auc:.5f}\")\n\n        del model\n        gc.collect()\n\nauc1 = roc_auc_score(y[\"adverse_outcome\"].values, oof[\"adverse_outcome\"])\nauc2 = roc_auc_score(y[\"readmission_30d\"].values, oof[\"readmission_30d\"])\nprint(\"\\n========== OOF Results ==========\")\nprint(f\"AUC adverse_outcome : {auc1:.6f}\")\nprint(f\"AUC readmission_30d : {auc2:.6f}\")\nprint(f\"Macro AUC           : {(auc1 + auc2) / 2:.6f}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id=\"submission\"></a>\n## 6. Submission","metadata":{}},{"cell_type":"code","source":"sub = sample_sub.copy()\nsub[\"adverse_outcome\"] = np.clip(pred_test[\"adverse_outcome\"], 0.0, 1.0)\nsub[\"readmission_30d\"] = np.clip(pred_test[\"readmission_30d\"], 0.0, 1.0)\n\nassert len(sub) == len(sample_sub) == len(test), \"Row count mismatch\"\nassert sub[ID_COL].equals(sample_sub[ID_COL]), \"encounter_id order mismatch vs sample_submission\"\nassert np.isfinite(sub[\"adverse_outcome\"]).all() and np.isfinite(sub[\"readmission_30d\"]).all(), \"NaN/Inf in predictions\"\n\nsub.to_csv(\"submission.csv\", index=False)\nprint(\"\\nSaved: submission.csv\")\nprint(sub.head())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"---\n\n## Conclusion\n\n**Possible improvements**\n- Time-based or city-based validation instead of random CV  \n- Feature interactions and hospital/city-level aggregations  \n- Model ensembling (CatBoost + LightGBM / XGBoost)  \n- Probability calibration and threshold tuning  \n\n**Good luck to everyone competing! :)**","metadata":{}}]}