{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":96164,"databundleVersionId":12993472,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# DRW Crypto — Ridge (L2) baseline, GroupKfold, (no PCA)\n","metadata":{}},{"cell_type":"markdown","source":"## Libraries: Import all useful libraries","metadata":{}},{"cell_type":"code","source":"%%time\n# === Cell 1: imports / paths / seed / utils ===\nimport os, gc, sys, random, warnings\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nimport polars as pl\n\nfrom sklearn.model_selection import GroupKFold, KFold, GridSearchCV\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.linear_model import Ridge\nfrom sklearn.metrics import make_scorer\n\nwarnings.filterwarnings(\"ignore\")\n\n# ---- paths (adjust base_dir if needed) ----\n# Works on Kaggle & Paperspace; edit base_dir if your data elsewhere\nbase_dir = Path(\"/kaggle/input/drw-crypto-market-prediction\")\nif not base_dir.exists():\n    base_dir = Path(\"/notebooks/Kaggle Competitions/data/drw-crypto-market-prediction\")\nif not base_dir.exists():\n    base_dir = Path(\"data/drw-crypto-market-prediction\")  # last fallback\nDATA_DIR = base_dir\nWORK_DIR = Path(os.getenv(\"KAGGLE_WORKING_DIR\", \"/kaggle/working\"))\nWORK_DIR.mkdir(parents=True, exist_ok=True)\n\nprint(\"DATA_DIR:\", DATA_DIR)\nprint(\"WORK_DIR:\", WORK_DIR)\n\n# ---- seed everywhere ----\nSEED = 42\ndef set_seed(s=SEED):\n    random.seed(s); np.random.seed(s)\nset_seed()\n\n# ---- Pearson (numpy) + scorer for sklearn ----\ndef pearson_r_np(y_true, y_pred):\n    y_true = np.asarray(y_true).ravel()\n    y_pred = np.asarray(y_pred).ravel()\n    if y_true.std() == 0 or y_pred.std() == 0: \n        return 0.0\n    return float(np.corrcoef(y_true, y_pred)[0, 1])\n\npearson_scorer = make_scorer(pearson_r_np, greater_is_better=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T17:25:38.274158Z","iopub.execute_input":"2025-09-22T17:25:38.274472Z","iopub.status.idle":"2025-09-22T17:25:39.423617Z","shell.execute_reply.started":"2025-09-22T17:25:38.274444Z","shell.execute_reply":"2025-09-22T17:25:39.422816Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Load Data: Fast IO with Polars","metadata":{}},{"cell_type":"code","source":"# === Reload/clean with Polars & align columns ===\n\ntrain_pl = pl.read_parquet(DATA_DIR / \"train.parquet\")\ntest_pl  = pl.read_parquet(DATA_DIR / \"test.parquet\")\n\nassert \"label\" in train_pl.columns, \"train must contain 'label'\"\n\n# strip index-like columns that can appear when parquet was saved from pandas\ndef clean_cols(cols):\n    return [c for c in cols if c != \"label\" and not c.startswith(\"__index_level_\") and c != \"index\"]\n\ntrain_feats = clean_cols(train_pl.columns)\ntest_feats  = clean_cols(test_pl.columns)\n\n# keep only features present in BOTH train and test\nfeat_cols = [c for c in train_feats if c in set(test_feats)]\n\n# cast to float32; leave NAs (Ridge can work after scaling)\ntrain_pl = train_pl.select(feat_cols + [\"label\"]).with_columns([\n    pl.col(feat_cols).cast(pl.Float32),\n    pl.col(\"label\").cast(pl.Float32),\n])\ntest_pl = test_pl.select(feat_cols).with_columns([\n    pl.col(feat_cols).cast(pl.Float32),\n])\n\n# hand off to sklearn as pandas/NumPy\nX_df    = train_pl.select(feat_cols).to_pandas()\ny_vec   = train_pl.select(\"label\").to_numpy().ravel()\ntest_df = test_pl.select(feat_cols).to_pandas()\n\nprint(\"n_features:\", len(feat_cols))\nprint(\"Shapes:\", X_df.shape, y_vec.shape, test_df.shape)\nprint(\"Example features:\", feat_cols[:8])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T17:25:39.424833Z","iopub.execute_input":"2025-09-22T17:25:39.425226Z","iopub.status.idle":"2025-09-22T17:26:05.734555Z","shell.execute_reply.started":"2025-09-22T17:25:39.425194Z","shell.execute_reply":"2025-09-22T17:26:05.733716Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## GroupKfold: ","metadata":{}},{"cell_type":"code","source":"# === Cell 3: GroupKFold via Polars row-hash (fallback to pandas if needed) ===\ntry:\n    # Polars row-hash (UInt64), stable and fast\n    groups = train_pl.select(pl.struct(feat_cols).hash_rows()).to_numpy().ravel().astype(\"uint64\")\nexcept Exception as e:\n    print(\"Polars hash_rows() failed; falling back to pandas hashing. Reason:\", e)\n    groups = pd.util.hash_pandas_object(X_df, index=False).astype(\"uint64\").to_numpy()\n\nn_splits = 5\ngkf = GroupKFold(n_splits=n_splits)\n\nfolds = []\nfor tr_idx, va_idx in gkf.split(X_df, y_vec, groups=groups):\n    folds.append((tr_idx, va_idx))\n\n# diagnostics\nvals, counts = np.unique(groups, return_counts=True)\ndup_groups = int((counts > 1).sum())\ndup_rows   = int(counts[counts > 1].sum())\ndup_rate   = 100.0 * dup_rows / len(groups)\nprint(f\"duplicate groups (>=2 rows): {dup_groups} | rows in duplicates: {dup_rows} ({dup_rate:.2f}%)\")\nprint(\"fold sizes (train/valid):\", [(len(a), len(b)) for a,b in folds])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T17:26:05.735421Z","iopub.execute_input":"2025-09-22T17:26:05.735641Z","iopub.status.idle":"2025-09-22T17:26:10.920843Z","shell.execute_reply.started":"2025-09-22T17:26:05.735625Z","shell.execute_reply":"2025-09-22T17:26:10.919940Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# One-fold Ridge trainer (inner CV tunes alpha)","metadata":{}},{"cell_type":"code","source":"# === Cell 4: ridge fold trainer ===\nRIDGE_ALPHAS = np.logspace(-4, 3, 20)  # 1e-4 .. 1e3 (tweak as needed)\n\ndef train_ridge_one_fold(tr_idx, va_idx, alphas=RIDGE_ALPHAS, seed=SEED):\n    X_tr, X_va = X_df.iloc[tr_idx], X_df.iloc[va_idx]\n    y_tr, y_va = y_vec[tr_idx], y_vec[va_idx]\n\n    # scaler + ridge in one pipeline (correct scaling inside CV)\n    pipe = Pipeline([\n        (\"sc\", StandardScaler(with_mean=True, with_std=True)),\n        (\"ridge\", Ridge(random_state=seed))\n    ])\n\n    # inner CV for hyperparam search\n    inner_cv = KFold(n_splits=3, shuffle=True, random_state=seed)\n    search = GridSearchCV(\n        estimator=pipe,\n        param_grid={\"ridge__alpha\": alphas},\n        scoring=pearson_scorer,\n        cv=inner_cv,\n        n_jobs=-1,\n        refit=True,\n        verbose=0\n    )\n    search.fit(X_tr, y_tr)\n\n    best = search.best_estimator_\n    # (best already refit on inner train folds; refit on full outer train for stability)\n    best.fit(X_tr, y_tr)\n\n    va_pred = best.predict(X_va).astype(\"float32\")\n    r = pearson_r_np(y_va, va_pred)\n\n    te_pred = best.predict(test_df).astype(\"float32\")\n\n    return va_idx, va_pred, te_pred, r, search.best_params_\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T17:26:10.922255Z","iopub.execute_input":"2025-09-22T17:26:10.922578Z","iopub.status.idle":"2025-09-22T17:26:10.930202Z","shell.execute_reply.started":"2025-09-22T17:26:10.922557Z","shell.execute_reply":"2025-09-22T17:26:10.929349Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cross-validate Ridge, collect OOF / per-fold / test","metadata":{}},{"cell_type":"code","source":"# === Cell 5: run CV ===\noof = np.zeros(len(y_vec), dtype=np.float32)\ntest_preds, fold_scores, best_params_per_fold = [], [], []\n\nfor f, (tr_idx, va_idx) in enumerate(folds, 1):\n    print(f\"\\n=== Ridge Fold {f}/{len(folds)} | train={len(tr_idx)} valid={len(va_idx)} ===\")\n    va_i, va_p, te_p, r, bp = train_ridge_one_fold(tr_idx, va_idx)\n    oof[va_i] = va_p\n    test_preds.append(te_p)\n    fold_scores.append(r)\n    best_params_per_fold.append(bp)\n    print(f\"Fold {f} Pearson r: {r:.6f} | best params: {bp}\")\n    gc.collect();\n\noof_r = pearson_r_np(y_vec, oof)\nprint(f\"\\nRidge OOF Pearson r: {oof_r:.6f} | per-fold: {np.round(fold_scores, 6)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-22T17:26:10.930905Z","iopub.execute_input":"2025-09-22T17:26:10.931201Z","execution_failed":"2025-09-22T17:26:41.026Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Save OOF & create submission","metadata":{}},{"cell_type":"code","source":"# === Cell 6: save OOF + submission ===\noof_path = WORK_DIR / \"oof_ridge.csv\"\npd.DataFrame({\"oof_pred\": oof}).to_csv(oof_path, index=False)\n\nsub = pd.read_csv(DATA_DIR / \"sample_submission.csv\")\nsub[\"prediction\"] = np.mean(np.stack(test_preds, axis=0), axis=0).astype(\"float32\")\nsub_path = WORK_DIR / \"submission_ridge.csv\"\nsub.to_csv(sub_path, index=False)\n\nprint(\"Saved:\")\nprint(\"  OOF ->\", oof_path)\nprint(\"  SUB ->\", sub_path)\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-09-22T17:26:41.026Z"}},"outputs":[],"execution_count":null}]}