{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"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":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":13636070,"sourceType":"datasetVersion","datasetId":8667402},{"sourceId":4534,"sourceType":"modelInstanceVersion","modelInstanceId":3326,"modelId":986},{"sourceId":268942,"sourceType":"modelInstanceVersion","modelInstanceId":230141,"modelId":251887}],"dockerImageVersionId":31154,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"62eea2e7","cell_type":"markdown","source":"\n# CSIRO Biomass v9.5 — Exportador de OOF & TEST matrices  \n\n**Objetivo:**  \nEste notebook genera y guarda las matrices OOF y TEST de los modelos:\n- Two‑Stream ConvNeXt\n- DINOv2 (ViT‑Base)\n- SigLIP (SO400M)  \n\nEstas matrices serán utilizadas luego en la **v10 (LightGBM meta‑stacker)** para acelerar experimentación.  \n","metadata":{}},{"id":"f2bdf032","cell_type":"code","source":"\nimport os, gc, random\nfrom pathlib import Path\nimport numpy as np\nimport pandas as pd\n\nfrom PIL import Image\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as T\nimport timm\n\nfrom sklearn.model_selection import KFold\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.linear_model import ElasticNetCV, LinearRegression\n\nDATA_DIR = Path(\"/kaggle/input/csiro-biomass\")\nWORK_DIR = Path(\"/kaggle/working\"); WORK_DIR.mkdir(exist_ok=True, parents=True)\n\nOOF_CSV_CANDIDATES = []\nTEST_CSV_CANDIDATES = []\n\nDINO_DIR   = \"/kaggle/input/dinov2/pytorch/base/1\"\nSIGLIP_DIR = None\nfor cand in [\"/kaggle/input/google-siglip-so400m-patch14-384/transformers/default/1\",\n             \"/kaggle/input/siglip-so400m-384\",\n             \"/kaggle/input/siglip-vitb16-384\"]:\n    if os.path.exists(cand):\n        SIGLIP_DIR = cand\n        break\n\nTS_MODEL_DIR = Path(\"/kaggle/input/csiro-exp3/convnext_exp3\")\nTS_BACKBONE  = \"convnext_small\"\nTS_IMG_SIZE  = 1000\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nAMP = torch.cuda.is_available()\nif torch.cuda.is_available():\n    torch.set_float32_matmul_precision(\"high\")\n    torch.backends.cuda.matmul.allow_tf32 = True\n    torch.backends.cudnn.allow_tf32 = True\n\ndef set_seed(seed=42):\n    random.seed(seed); np.random.seed(seed)\n    torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)\nset_seed(42)\n\nprint(\"Device:\", device)\nprint(\"DINO path ok:\", os.path.exists(DINO_DIR))\nprint(\"SigLIP path:\", SIGLIP_DIR)\nprint(\"Two‑Stream dir ok:\", TS_MODEL_DIR.exists())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:32:20.701909Z","iopub.execute_input":"2025-11-17T15:32:20.702251Z","iopub.status.idle":"2025-11-17T15:32:33.027808Z","shell.execute_reply.started":"2025-11-17T15:32:20.702226Z","shell.execute_reply":"2025-11-17T15:32:33.027037Z"}},"outputs":[],"execution_count":null},{"id":"63296b6c","cell_type":"code","source":"\ntrain = pd.read_csv(DATA_DIR / \"train.csv\")\ntest  = pd.read_csv(DATA_DIR / \"test.csv\")\n\ntarget_names = sorted(train[\"target_name\"].unique())\ny_wide = train.pivot(index=\"image_path\", columns=\"target_name\", values=\"target\").loc[:, target_names]\n\nTARGET_WEIGHTS = {\"Dry_Green_g\":0.1,\"Dry_Dead_g\":0.1,\"Dry_Clover_g\":0.1,\"GDM_g\":0.2,\"Dry_Total_g\":0.5}\nW_VEC = np.array([TARGET_WEIGHTS[t] for t in target_names], dtype=np.float32)\n\ntrain_imgs = y_wide.index.tolist()\ntest_imgs  = test[\"image_path\"].drop_duplicates().tolist()\ny_matrix = y_wide[target_names].values\n\nprint(\"train images:\", len(train_imgs), \"test images:\", len(test_imgs))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:32:33.029238Z","iopub.execute_input":"2025-11-17T15:32:33.029606Z","iopub.status.idle":"2025-11-17T15:32:33.079228Z","shell.execute_reply.started":"2025-11-17T15:32:33.029589Z","shell.execute_reply":"2025-11-17T15:32:33.078638Z"}},"outputs":[],"execution_count":null},{"id":"7bb2ef43","cell_type":"code","source":"\ndef try_read_csv(path):\n    try:\n        if path and os.path.exists(path):\n            df = pd.read_csv(path)\n            if \"image_path\" in df.columns:\n                df = df.set_index(\"image_path\")\n            return df\n    except Exception as e:\n        print(\"No pude leer:\", path, \"->\", e)\n    return None\n\noof_external = []\ntest_external = []\n\nfor p in OOF_CSV_CANDIDATES:\n    df = try_read_csv(p)\n    if df is not None and all(t in df.columns for t in target_names):\n        print(\"OOF externo:\", p, df.shape)\n        oof_external.append(df[target_names].reindex(train_imgs))\n\nfor p in TEST_CSV_CANDIDATES:\n    df = try_read_csv(p)\n    if df is not None and all(t in df.columns for t in target_names):\n        print(\"TEST externo:\", p, df.shape)\n        test_external.append(df[target_names].reindex(test_imgs))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:32:33.079810Z","iopub.execute_input":"2025-11-17T15:32:33.080044Z","iopub.status.idle":"2025-11-17T15:32:33.085962Z","shell.execute_reply.started":"2025-11-17T15:32:33.080027Z","shell.execute_reply":"2025-11-17T15:32:33.085164Z"}},"outputs":[],"execution_count":null},{"id":"5c502ea6","cell_type":"code","source":"\nfrom dataclasses import dataclass\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport cv2, torch.nn as nn\n\n@dataclass\nclass TSConfig:\n    base_path: Path = Path(\"/kaggle/input/csiro-biomass\")\n    model_dir: Path = TS_MODEL_DIR\n    model_name: str = TS_BACKBONE\n    img_size: int = TS_IMG_SIZE\n    n_folds: int = 5\n    batch_size: int = 1\n    num_workers: int = 1\n    device: torch.device = device\n\nTS_CFG = TSConfig()\n\ndef ts_get_tta(img_size):\n    base = [A.Normalize(mean=(0.485,0.456,0.406), std=(0.229,0.224,0.225)), ToTensorV2()]\n    return [\n        A.Compose([A.Resize(img_size, img_size), *base]),\n        A.Compose([A.HorizontalFlip(p=1.0), A.Resize(img_size, img_size), *base]),\n        A.Compose([A.VerticalFlip(p=1.0),   A.Resize(img_size, img_size), *base]),\n    ]\n\nclass TSTestDS(Dataset):\n    def __init__(self, df_unique, tta_transform, image_dir):\n        self.paths = df_unique[\"image_path\"].values\n        self.tfm = tta_transform\n        self.img_dir = image_dir\n    def __len__(self): return len(self.paths)\n    def __getitem__(self, i):\n        p = Path(self.img_dir) / Path(self.paths[i]).name\n        im = cv2.imread(str(p))\n        if im is None:\n            im = np.zeros((TS_CFG.img_size, TS_CFG.img_size*2, 3), dtype=np.uint8)\n        im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)\n        h, w = im.shape[:2]; mid = w // 2\n        l  = self.tfm(image=im[:, :mid])[\"image\"]\n        r  = self.tfm(image=im[:, mid:])[\"image\"]\n        return l, r, self.paths[i]\n\nclass TSModel(nn.Module):\n    def __init__(self, model_name):\n        super().__init__()\n        self.backbone = timm.create_model(model_name, pretrained=False, num_classes=0, global_pool='avg')\n        nf = self.backbone.num_features\n        self.head_total = nn.Sequential(nn.Linear(nf*2, nf), nn.ReLU(), nn.Dropout(0.3), nn.Linear(nf, 1))\n        self.head_gdm   = nn.Sequential(nn.Linear(nf*2, nf), nn.ReLU(), nn.Dropout(0.3), nn.Linear(nf, 1))\n        self.head_green = nn.Sequential(nn.Linear(nf*2, nf), nn.ReLU(), nn.Dropout(0.3), nn.Linear(nf, 1))\n    def forward(self, l, r):\n        fl = self.backbone(l); fr = self.backbone(r)\n        z = torch.cat([fl, fr], dim=1)\n        return self.head_total(z), self.head_gdm(z), self.head_green(z)\n\ndef ts_load_fold_models():\n    assert TS_CFG.model_dir.exists(), f\"No existe {TS_CFG.model_dir}\"\n    models = []\n    for k in range(TS_CFG.n_folds):\n        mp = TS_CFG.model_dir / f\"best_model_fold{k}.pth\"\n        assert mp.exists(), f\"Falta {mp}\"\n        m = TSModel(TS_CFG.model_name).to(TS_CFG.device).eval()\n        sd = torch.load(mp, map_location=TS_CFG.device)\n        sd = {k.replace(\"module.\",\"\"): v for k,v in sd.items()}\n        m.load_state_dict(sd, strict=True)\n        models.append(m)\n    return models\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:32:33.087289Z","iopub.execute_input":"2025-11-17T15:32:33.087520Z","iopub.status.idle":"2025-11-17T15:33:05.703991Z","shell.execute_reply.started":"2025-11-17T15:32:33.087494Z","shell.execute_reply":"2025-11-17T15:33:05.703166Z"}},"outputs":[],"execution_count":null},{"id":"e897fdb0","cell_type":"code","source":"\ndef ts_oof_train(train_df):\n    df_u = train_df.drop_duplicates(subset=[\"image_path\"]).reset_index(drop=True)\n    img_dir = TS_CFG.base_path / \"train\"\n    tta = ts_get_tta(TS_CFG.img_size)\n    models = ts_load_fold_models()\n\n    kf = KFold(n_splits=TS_CFG.n_folds, shuffle=True, random_state=42)\n    indices = list(kf.split(df_u))\n\n    preds_total = np.zeros((len(df_u),), dtype=np.float32)\n    preds_gdm   = np.zeros((len(df_u),), dtype=np.float32)\n    preds_green = np.zeros((len(df_u),), dtype=np.float32)\n\n    for fold, (_, va_idx) in enumerate(indices):\n        m = models[fold]\n        fold_total = []; fold_gdm = []; fold_green = []\n        for view in tta:\n            ds = TSTestDS(df_u.iloc[va_idx], view, img_dir)\n            dl = DataLoader(ds, batch_size=TS_CFG.batch_size, shuffle=False,\n                            num_workers=TS_CFG.num_workers, pin_memory=True)\n            vt, vg, vg2 = [], [], []\n            with torch.no_grad(), torch.amp.autocast(\"cuda\", enabled=AMP):\n                for l, r, _ in dl:\n                    l = l.to(TS_CFG.device); r = r.to(TS_CFG.device)\n                    pt, pg, pgr = m(l, r)\n                    vt.append(pt.squeeze(1).cpu().float().numpy())\n                    vg.append(pg.squeeze(1).cpu().float().numpy())\n                    vg2.append(pgr.squeeze(1).cpu().float().numpy())\n            fold_total.append(np.concatenate(vt))\n            fold_gdm.append(np.concatenate(vg))\n            fold_green.append(np.concatenate(vg2))\n        preds_total[va_idx] = np.mean(np.stack(fold_total, 0), 0)\n        preds_gdm[va_idx]   = np.mean(np.stack(fold_gdm,   0), 0)\n        preds_green[va_idx] = np.mean(np.stack(fold_green, 0), 0)\n\n    clover = np.clip(preds_gdm - preds_green, 0, None)\n    dead   = np.clip(preds_total - preds_gdm, 0, None)\n\n    oof_df = pd.DataFrame({\n        \"image_path\": df_u[\"image_path\"].values,\n        \"Dry_Green_g\": preds_green,\n        \"Dry_Dead_g\":  dead,\n        \"Dry_Clover_g\": clover,\n        \"GDM_g\":        preds_gdm,\n        \"Dry_Total_g\":  preds_total,\n    }).set_index(\"image_path\").reindex(train_imgs)\n\n    print(\"Two‑Stream OOF shape:\", oof_df.shape)\n    return oof_df\n\nts_oof_df = ts_oof_train(train)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:33:05.704941Z","iopub.execute_input":"2025-11-17T15:33:05.705316Z","iopub.status.idle":"2025-11-17T15:35:24.239401Z","shell.execute_reply.started":"2025-11-17T15:33:05.705290Z","shell.execute_reply":"2025-11-17T15:35:24.238478Z"}},"outputs":[],"execution_count":null},{"id":"7a3f809f","cell_type":"code","source":"\ndef ts_test_preds(test_df):\n    df_u = test_df.drop_duplicates(subset=[\"image_path\"]).reset_index(drop=True)\n    img_dir = TS_CFG.base_path / \"test\"\n    tta = ts_get_tta(TS_CFG.img_size)\n    models = ts_load_fold_models()\n\n    all_total = []; all_gdm = []; all_green = []\n    for view in tta:\n        ds = TSTestDS(df_u, view, img_dir)\n        dl = DataLoader(ds, batch_size=TS_CFG.batch_size, shuffle=False,\n                        num_workers=TS_CFG.num_workers, pin_memory=True)\n        vt, vg, vg2 = [], [], []\n        with torch.no_grad(), torch.amp.autocast(\"cuda\", enabled=AMP):\n            for l, r, _ in dl:\n                l = l.to(TS_CFG.device); r = r.to(TS_CFG.device)\n                pt, pg, pgr = [], [], []\n                for m in models:\n                    a,b,c = m(l, r)\n                    pt.append(a); pg.append(b); pgr.append(c)\n                pt = torch.stack(pt).mean(0); pg = torch.stack(pg).mean(0); pgr = torch.stack(pgr).mean(0)\n                vt.append(pt.squeeze(1).cpu().float().numpy())\n                vg.append(pg.squeeze(1).cpu().float().numpy())\n                vg2.append(pgr.squeeze(1).cpu().float().numpy())\n        all_total.append(np.concatenate(vt))\n        all_gdm.append(np.concatenate(vg))\n        all_green.append(np.concatenate(vg2))\n\n    preds_total = np.mean(np.stack(all_total, 0), 0)\n    preds_gdm   = np.mean(np.stack(all_gdm,   0), 0)\n    preds_green = np.mean(np.stack(all_green, 0), 0)\n\n    clover = np.clip(preds_gdm - preds_green, 0, None)\n    dead   = np.clip(preds_total - preds_gdm, 0, None)\n\n    test_df2 = pd.DataFrame({\n        \"image_path\": df_u[\"image_path\"].values,\n        \"Dry_Green_g\": preds_green,\n        \"Dry_Dead_g\":  dead,\n        \"Dry_Clover_g\": clover,\n        \"GDM_g\":        preds_gdm,\n        \"Dry_Total_g\":  preds_total,\n    }).set_index(\"image_path\").reindex(test_imgs)\n\n    print(\"Two‑Stream TEST shape:\", test_df2.shape)\n    return test_df2\n\nts_test_df = ts_test_preds(test)\noof_external.append(ts_oof_df[target_names].reindex(train_imgs))\ntest_external.append(ts_test_df[target_names].reindex(test_imgs))\nprint(\"Two‑Stream agregado a oof_external/test_external.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:24.241154Z","iopub.execute_input":"2025-11-17T15:35:24.241401Z","iopub.status.idle":"2025-11-17T15:35:31.813972Z","shell.execute_reply.started":"2025-11-17T15:35:24.241377Z","shell.execute_reply":"2025-11-17T15:35:31.813268Z"}},"outputs":[],"execution_count":null},{"id":"a006d2f6","cell_type":"code","source":"class ImgDS(Dataset):\n    def __init__(self, relpaths, tfm, root=DATA_DIR):\n        self.rel = relpaths; self.tfm = tfm; self.root = root\n    def __len__(self): return len(self.rel)\n    def __getitem__(self, i):\n        p = Path(self.root) / self.rel[i]\n        try:\n            im = Image.open(p).convert(\"RGB\")\n        except Exception:\n            im = Image.new(\"RGB\", (512, 256), (0,0,0))\n        return self.tfm(im)\n\ndef embed_with_model(model, tfm, relpaths, bs=16):\n    dl = DataLoader(ImgDS(relpaths, tfm), batch_size=bs, shuffle=False, num_workers=2, pin_memory=True)\n    out = []\n    model.eval()\n    with torch.no_grad(), torch.amp.autocast(\"cuda\", enabled=AMP):\n        for xb in dl:\n            xb = xb.to(device)\n            z = model(xb)\n            if isinstance(z, (list, tuple)): z = z[0]\n            out.append(z.detach().cpu().float().numpy())\n    return np.concatenate(out, 0)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:31.816402Z","iopub.execute_input":"2025-11-17T15:35:31.816613Z","iopub.status.idle":"2025-11-17T15:35:31.823668Z","shell.execute_reply.started":"2025-11-17T15:35:31.816594Z","shell.execute_reply":"2025-11-17T15:35:31.822877Z"}},"outputs":[],"execution_count":null},{"id":"cb834ca1","cell_type":"code","source":"\ndef collect_mats_per_target(j):\n    oof_cols = []\n    tst_cols = []\n    for df in oof_external:\n        oof_cols.append(df.iloc[:, j].values.reshape(-1,1))\n    for df in test_external:\n        tst_cols.append(df.iloc[:, j].values.reshape(-1,1))\n\n    # Two‑Stream siempre incluido\n    oof_cols.append(ts_oof_df.iloc[:, j].values.reshape(-1,1))\n    tst_cols.append(ts_test_df.iloc[:, j].values.reshape(-1,1))\n\n\n    if not oof_cols or not tst_cols:\n        return None, None\n    return np.concatenate(oof_cols, 1), np.concatenate(tst_cols, 1)\n\nprint(\"Ramas externas OOF/Test:\", len(oof_external), len(test_external))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:31.824587Z","iopub.execute_input":"2025-11-17T15:35:31.824863Z","iopub.status.idle":"2025-11-17T15:35:31.844596Z","shell.execute_reply.started":"2025-11-17T15:35:31.824842Z","shell.execute_reply":"2025-11-17T15:35:31.843970Z"}},"outputs":[],"execution_count":null},{"id":"2ebc812f","cell_type":"code","source":"\ndef calibrate_with_oof(y_true, y_pred):\n    lr = LinearRegression()\n    lr.fit(y_pred.reshape(-1,1), y_true)\n    return lambda z: lr.predict(z.reshape(-1,1))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:31.845241Z","iopub.execute_input":"2025-11-17T15:35:31.845483Z","iopub.status.idle":"2025-11-17T15:35:31.857714Z","shell.execute_reply.started":"2025-11-17T15:35:31.845461Z","shell.execute_reply":"2025-11-17T15:35:31.857155Z"}},"outputs":[],"execution_count":null},{"id":"dee54238","cell_type":"code","source":"\ndef project_constraints(df, tn):\n    arr = df.values.copy()\n    g = tn.index(\"Dry_Green_g\")\n    c = tn.index(\"Dry_Clover_g\")\n    d = tn.index(\"Dry_Dead_g\")\n    G = tn.index(\"GDM_g\")\n    Tt = tn.index(\"Dry_Total_g\")\n    for i in range(len(arr)):\n        g0, c0, d0, G0, T0 = arr[i, g], arr[i, c], arr[i, d], arr[i, G], arr[i, Tt]\n        g1 = 0.5*(g0 + max(0.0, G0 - c0))\n        c1 = 0.5*(c0 + max(0.0, G0 - g0))\n        g1, c1 = max(0.0, g1), max(0.0, c1)\n        G1 = g1 + c1\n        d1 = 0.5*(d0 + max(0.0, T0 - G1))\n        d1 = max(0.0, d1)\n        arr[i, g], arr[i, c], arr[i, d] = g1, c1, d1\n        arr[i, G]  = g1 + c1\n        arr[i, Tt] = g1 + c1 + d1\n    return pd.DataFrame(arr, index=df.index, columns=df.columns)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:31.858321Z","iopub.execute_input":"2025-11-17T15:35:31.858512Z","iopub.status.idle":"2025-11-17T15:35:31.867426Z","shell.execute_reply.started":"2025-11-17T15:35:31.858498Z","shell.execute_reply":"2025-11-17T15:35:31.866637Z"}},"outputs":[],"execution_count":null},{"id":"ffa5b8a8","cell_type":"code","source":"\npreds_stack = np.zeros((len(test_imgs), len(target_names)), dtype=np.float32)\n\nfor j, t in enumerate(target_names):\n    X_tr, X_te = collect_mats_per_target(j)\n    if X_tr is None:\n        print(f\"[WARN] Sin ramas para {t}. Predicciones = 0.0\")\n        preds_stack[:, j] = 0.0\n        continue\n\n    y_tr = y_wide[target_names].values[:, j]\n    sc = StandardScaler(with_mean=False)\n    Xtr = sc.fit_transform(X_tr); Xte = sc.transform(X_te)\n\n    en = ElasticNetCV(l1_ratio=[.1,.3,.5,.7,.9,1.0], cv=5, max_iter=5000, random_state=42)\n    en.fit(Xtr, y_tr)\n\n    oof_pred = en.predict(Xtr)\n    cal = calibrate_with_oof(y_tr, oof_pred)\n\n    preds_stack[:, j] = cal(en.predict(Xte))\n\npreds_df = pd.DataFrame(preds_stack, index=test_imgs, columns=target_names).clip(lower=0)\npreds_df = project_constraints(preds_df, target_names)\n\npreds_long = (preds_df.reset_index()\n              .melt(id_vars=\"index\", var_name=\"target_name\", value_name=\"target\")\n              .rename(columns={\"index\":\"image_path\"}))\nsubmission = (test[[\"sample_id\",\"image_path\",\"target_name\"]]\n              .merge(preds_long, on=[\"image_path\",\"target_name\"], how=\"left\")\n              [[\"sample_id\",\"target\"]])\nassert submission.shape[0] == test.shape[0], \"Submission size mismatch\"\n\nprint(\"submission.csv escrito. Filas:\", len(submission))\nsubmission.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:31.868003Z","iopub.execute_input":"2025-11-17T15:35:31.868188Z","iopub.status.idle":"2025-11-17T15:35:32.974742Z","shell.execute_reply.started":"2025-11-17T15:35:31.868175Z","shell.execute_reply":"2025-11-17T15:35:32.974059Z"}},"outputs":[],"execution_count":null},{"id":"ebfe1ea6","cell_type":"code","source":"\nfrom pathlib import Path\nimport pandas as pd\n\nSAVE_DIR = Path(\"/kaggle/working/csiro_oof_cache\")\nSAVE_DIR.mkdir(exist_ok=True, parents=True)\n\n# Guarda Two‑Stream\nif 'ts_oof_df' in locals() and 'ts_test_df' in locals():\n    ts_oof_df.to_csv(SAVE_DIR / \"oof_twostream.csv\")\n    ts_test_df.to_csv(SAVE_DIR / \"test_twostream.csv\")\n    print(\"✅ Two‑Stream exportado.\")\n\nprint(\"\\nTodos los archivos se han guardado en:\", SAVE_DIR)\nprint(\"→ Sube esta carpeta como dataset privado a Kaggle para usar en la v10.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:32.975409Z","iopub.execute_input":"2025-11-17T15:35:32.975584Z","iopub.status.idle":"2025-11-17T15:35:32.988743Z","shell.execute_reply.started":"2025-11-17T15:35:32.975570Z","shell.execute_reply":"2025-11-17T15:35:32.987955Z"}},"outputs":[],"execution_count":null},{"id":"28ac692e-6f3f-4b83-b23b-d6704623ebab","cell_type":"code","source":"\nimport os\nimport pandas as pd\nimport numpy as np\nfrom pathlib import Path\n\n# Directorio base de salida (Kaggle Working Directory)\nOUTPUT_DIR = Path(\"/kaggle/working\")\nprint(\"📁 Directorio de salida:\", OUTPUT_DIR)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:32.989544Z","iopub.execute_input":"2025-11-17T15:35:32.989895Z","iopub.status.idle":"2025-11-17T15:35:32.995825Z","shell.execute_reply.started":"2025-11-17T15:35:32.989869Z","shell.execute_reply":"2025-11-17T15:35:32.995156Z"}},"outputs":[],"execution_count":null},{"id":"75b1e247-5466-4358-af88-1982dd482cb5","cell_type":"code","source":"\n# Función de normalización de rutas\ndef normalize_path(p):\n    return str(p).replace(\".jpg\", \"\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:35:32.996460Z","iopub.execute_input":"2025-11-17T15:35:32.996654Z","iopub.status.idle":"2025-11-17T15:35:33.012810Z","shell.execute_reply.started":"2025-11-17T15:35:32.996639Z","shell.execute_reply":"2025-11-17T15:35:33.012151Z"}},"outputs":[],"execution_count":null},{"id":"20c4f579-67fd-47b3-82f2-89ea659645b8","cell_type":"code","source":"\n# Se espera que las siguientes variables ya existan tras el pipeline TwoStream\n# oof_preds, test_preds -> matrices numpy [n_samples, 5]\n# train_image_paths, test_image_paths -> listas con rutas de imágenes\n# TARGETS -> lista con los nombres de las 5 variables objetivo\n\ntry:\n    target_names\n    ts_oof_df, ts_test_df\n    train_imgs, test_imgs\nexcept NameError:\n    raise RuntimeError(\"⚠️ Ejecuta primero el pipeline de TwoStream antes de esta sección.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:41:29.563628Z","iopub.execute_input":"2025-11-17T15:41:29.564428Z","iopub.status.idle":"2025-11-17T15:41:29.568283Z","shell.execute_reply.started":"2025-11-17T15:41:29.564402Z","shell.execute_reply":"2025-11-17T15:41:29.567594Z"}},"outputs":[],"execution_count":null},{"id":"30986e2e-4426-454b-9373-837c9cfe9612","cell_type":"code","source":"\n# Normalización de paths\ntrain_paths = [normalize_path(p) for p in train_imgs]\ntest_paths = [normalize_path(p) for p in test_imgs]\nprint(\"Ejemplo path normalizado:\", train_paths[0])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:41:46.181744Z","iopub.execute_input":"2025-11-17T15:41:46.182014Z","iopub.status.idle":"2025-11-17T15:41:46.186847Z","shell.execute_reply.started":"2025-11-17T15:41:46.181995Z","shell.execute_reply":"2025-11-17T15:41:46.186167Z"}},"outputs":[],"execution_count":null},{"id":"de95cfd2-cfca-40e9-8084-9ce1ae89e130","cell_type":"code","source":"\n# Crear DataFrames en formato WIDE (para stacker)\ndf_oof = pd.DataFrame(ts_oof_df, columns=target_names)\ndf_test = pd.DataFrame(ts_test_df, columns=target_names)\ndf_oof.insert(0, \"image_path\", train_paths)\ndf_test.insert(0, \"image_path\", test_paths)\n\n# Guardar formato WIDE\ndf_oof.to_csv(OUTPUT_DIR / \"oof_twostream.csv\", index=False)\ndf_test.to_csv(OUTPUT_DIR / \"test_twostream.csv\", index=False)\nprint(\"✅ Archivos guardados: oof_twostream.csv y test_twostream.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:43:54.704651Z","iopub.execute_input":"2025-11-17T15:43:54.704928Z","iopub.status.idle":"2025-11-17T15:43:54.716146Z","shell.execute_reply.started":"2025-11-17T15:43:54.704909Z","shell.execute_reply":"2025-11-17T15:43:54.715414Z"}},"outputs":[],"execution_count":null},{"id":"eb8051d7-711d-488f-a03f-bac9d98c971b","cell_type":"code","source":"\n# Crear formato LONG (para Kaggle)\ndef make_long(df):\n    records = []\n    for _, row in df.iterrows():\n        img = row[\"image_path\"]\n        for col in target_names:\n            records.append((f\"{img}__{col}\", row[col]))\n    return pd.DataFrame(records, columns=[\"sample_id\", \"target\"])\n\ndf_oof_long = make_long(df_oof)\ndf_test_long = make_long(df_test)\n\ndf_oof_long.to_csv(OUTPUT_DIR / \"oof_twostream_long.csv\", index=False)\ndf_test_long.to_csv(OUTPUT_DIR / \"test_twostream_long.csv\", index=False)\nprint(\"📄 Archivos guardados: oof_twostream_long.csv y test_twostream_long.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:46:30.417814Z","iopub.execute_input":"2025-11-17T15:46:30.418068Z","iopub.status.idle":"2025-11-17T15:46:30.449495Z","shell.execute_reply.started":"2025-11-17T15:46:30.418053Z","shell.execute_reply":"2025-11-17T15:46:30.448736Z"}},"outputs":[],"execution_count":null},{"id":"249be86d-a9c9-40ff-a0e9-25ba9191dd35","cell_type":"code","source":"\n# Mostrar primeras filas de cada archivo\nprint(\"\\n🔹 OOF (wide):\")\ndisplay(df_oof.head())\n\nprint(\"\\n🔹 TEST (wide):\")\ndisplay(df_test.head())\n\nprint(\"\\n🔹 OOF (long):\")\ndisplay(df_oof_long.head())\n\nprint(\"\\n🔹 TEST (long):\")\ndisplay(df_test_long.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:46:39.436459Z","iopub.execute_input":"2025-11-17T15:46:39.436990Z","iopub.status.idle":"2025-11-17T15:46:39.462248Z","shell.execute_reply.started":"2025-11-17T15:46:39.436968Z","shell.execute_reply":"2025-11-17T15:46:39.461556Z"}},"outputs":[],"execution_count":null},{"id":"3d6d17ab-19a1-410e-aff8-fbf7d9f07515","cell_type":"code","source":"\n# Mostrar listado final de CSVs generados\nfiles = sorted([f for f in os.listdir(OUTPUT_DIR) if f.endswith(\".csv\")])\nprint(\"\\n📦 Archivos generados en /kaggle/working/:\")\ndisplay(pd.DataFrame(files, columns=[\"Archivo\"]))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T15:46:55.180710Z","iopub.execute_input":"2025-11-17T15:46:55.181349Z","iopub.status.idle":"2025-11-17T15:46:55.189794Z","shell.execute_reply.started":"2025-11-17T15:46:55.181325Z","shell.execute_reply":"2025-11-17T15:46:55.189153Z"}},"outputs":[],"execution_count":null}]}