{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.13"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":458.786363,"end_time":"2025-12-07T23:18:57.978319","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-12-07T23:11:19.191956","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2025-12-07T23:11:23.811519Z","iopub.status.busy":"2025-12-07T23:11:23.811195Z","iopub.status.idle":"2025-12-07T23:11:25.987702Z","shell.execute_reply":"2025-12-07T23:11:25.984965Z"},"papermill":{"duration":2.185808,"end_time":"2025-12-07T23:11:25.989735","exception":false,"start_time":"2025-12-07T23:11:23.803927","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CSIRO Biomass – Análisis Exploratorio y Modelo Baseline\n\n### Equipo:\n- Denisse Antunez Lopez\n- Georgina Salcido Valenzuela\n- Ana Laura Chenoweth Galaz\n\nEn este notebook realizamos:\n\n1. Análisis exploratorio sencillo de los datos.\n2. Visualización de patrones con PCA.\n3. Definición del preprocesamiento y su integración en un `Pipeline`.\n4. Decisión del tipo de problema (clasificación o regresión) y justificación.\n5. Entrenamiento de un modelo inicial simple (baseline).\n6. Búsqueda de hiperparámetros y selección de un mejor modelo.\n7. Evaluación del modelo retenido mediante validación cruzada y métricas apropiadas.\n","metadata":{"papermill":{"duration":0.005444,"end_time":"2025-12-07T23:11:26.001205","exception":false,"start_time":"2025-12-07T23:11:25.995761","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport cv2\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import r2_score, mean_squared_error\nfrom sklearn.preprocessing import OneHotEncoder\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.preprocessing import StandardScaler\n\nimport xgboost as xgb\n\nfrom skimage.feature import local_binary_pattern\nfrom skimage.filters import sobel\nfrom skimage.measure import shannon_entropy\nfrom skimage.feature import graycomatrix, graycoprops\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:32:50.148331Z","iopub.execute_input":"2025-12-08T07:32:50.148661Z","iopub.status.idle":"2025-12-08T07:32:56.298626Z","shell.execute_reply.started":"2025-12-08T07:32:50.148629Z","shell.execute_reply":"2025-12-08T07:32:56.296302Z"},"papermill":{"duration":2.83762,"end_time":"2025-12-07T23:11:28.844095","exception":false,"start_time":"2025-12-07T23:11:26.006475","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base = \"/kaggle/input/csiro-biomass/\"\n\ntrain = pd.read_csv(base + \"train.csv\")\ntest  = pd.read_csv(base + \"test.csv\")\n\ntrain.head()\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:32:56.299978Z","iopub.execute_input":"2025-12-08T07:32:56.300600Z","iopub.status.idle":"2025-12-08T07:32:56.403558Z","shell.execute_reply.started":"2025-12-08T07:32:56.300563Z","shell.execute_reply":"2025-12-08T07:32:56.402542Z"},"papermill":{"duration":0.014147,"end_time":"2025-12-07T23:11:28.864326","exception":false,"start_time":"2025-12-07T23:11:28.850179","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1. Análisis exploratorio de los datos\n\nEn esta sección respondemos:\n\n- Qué columnas tenemos y de qué tipo son.\n- Si hay o no datos faltantes.\n- Cómo se distribuyen las variables numéricas.\n- Qué variables cualitativas hay y si alguna tiene orden implícito.\n","metadata":{"papermill":{"duration":0.005437,"end_time":"2025-12-07T23:11:28.875450","exception":false,"start_time":"2025-12-07T23:11:28.870013","status":"completed"},"tags":[]}},{"cell_type":"code","source":"print(\"Dimensión del dataset:\", train.shape)\nprint(\"\\nInformación general:\")\ntrain.info()\n\nprint(\"\\nValores faltantes por columna:\")\nprint(train.isna().sum())\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:33:21.312009Z","iopub.execute_input":"2025-12-08T07:33:21.312345Z","iopub.status.idle":"2025-12-08T07:33:21.342811Z","shell.execute_reply.started":"2025-12-08T07:33:21.312320Z","shell.execute_reply":"2025-12-08T07:33:21.341153Z"},"papermill":{"duration":0.034864,"end_time":"2025-12-07T23:11:28.915778","exception":false,"start_time":"2025-12-07T23:11:28.880914","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"No hay valores faltantes.\n\n3 numéricas (Pre_GSHH_NDVI, Height_Ave_cm, target) y el resto object.","metadata":{"papermill":{"duration":0.005658,"end_time":"2025-12-07T23:11:28.927230","exception":false,"start_time":"2025-12-07T23:11:28.921572","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"### Variables numéricas\n\nAnalizamos la distribución de:\n- `target` (biomasa en gramos),\n- `Pre_GSHH_NDVI` (índice de vegetación),\n- `Height_Ave_cm` (altura promedio del pasto).\n","metadata":{"papermill":{"duration":0.005563,"end_time":"2025-12-07T23:11:28.938464","exception":false,"start_time":"2025-12-07T23:11:28.932901","status":"completed"},"tags":[]}},{"cell_type":"code","source":"train[[\"Pre_GSHH_NDVI\", \"Height_Ave_cm\", \"target\"]].describe()\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:33:26.189470Z","iopub.execute_input":"2025-12-08T07:33:26.190517Z","iopub.status.idle":"2025-12-08T07:33:26.216678Z","shell.execute_reply.started":"2025-12-08T07:33:26.190461Z","shell.execute_reply":"2025-12-08T07:33:26.215413Z"},"papermill":{"duration":0.03697,"end_time":"2025-12-07T23:11:28.981209","exception":false,"start_time":"2025-12-07T23:11:28.944239","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(8,4))\nsns.histplot(train[\"target\"], kde=True)\nplt.title(\"Distribución de biomasa (target)\")\nplt.xlabel(\"target (g)\")\nplt.show()\n\nplt.figure(figsize=(8,4))\nsns.histplot(train[\"Pre_GSHH_NDVI\"], kde=True)\nplt.title(\"Distribución NDVI\")\nplt.xlabel(\"Pre_GSHH_NDVI\")\nplt.show()\n\nplt.figure(figsize=(8,4))\nsns.histplot(train[\"Height_Ave_cm\"], kde=True, bins=40)\nplt.title(\"Distribución altura\")\nplt.xlabel(\"Height_Ave_cm\")\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:33:30.458340Z","iopub.execute_input":"2025-12-08T07:33:30.458697Z","iopub.status.idle":"2025-12-08T07:33:31.511689Z","shell.execute_reply.started":"2025-12-08T07:33:30.458674Z","shell.execute_reply":"2025-12-08T07:33:31.510436Z"},"papermill":{"duration":0.870108,"end_time":"2025-12-07T23:11:29.857406","exception":false,"start_time":"2025-12-07T23:11:28.987298","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- `target` está muy sesgada a la derecha: la mayoría de valores son bajos y hay pocos valores muy altos.\n\n- `Pre_GSHH_NDVI` es aproximadamente unimodal con valores entre 0.16 y 0.91, centrado alrededor de 0.65–0.75.\n\n- `Height_Ave_cm` también está sesgada a la derecha, con mayoría entre 1 y 10 cm y algunos outliers hasta 70 cm.","metadata":{"papermill":{"duration":0.007032,"end_time":"2025-12-07T23:11:29.872179","exception":false,"start_time":"2025-12-07T23:11:29.865147","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"### Variables categóricas\n\nAnalizamos las variables:\n- `target_name`\n- `State`\n- `Species`\n- `Sampling_Date` (que trataremos como fecha)\n","metadata":{"papermill":{"duration":0.007135,"end_time":"2025-12-07T23:11:29.887042","exception":false,"start_time":"2025-12-07T23:11:29.879907","status":"completed"},"tags":[]}},{"cell_type":"code","source":"print(\"Valores únicos por columna categórica:\\n\")\nfor col in [\"State\", \"Species\", \"target_name\"]:\n    print(f\"{col}: {train[col].nunique()} valores únicos\")\n\nplt.figure(figsize=(6,4))\ntrain[\"target_name\"].value_counts().plot(kind=\"bar\")\nplt.title(\"Distribución de target_name\")\nplt.xlabel(\"target_name\")\nplt.ylabel(\"Frecuencia\")\nplt.show()\n\nprint(\"\\nEstados:\")\nprint(train[\"State\"].value_counts())\n\nprint(\"\\nEjemplos de Species:\")\nprint(train[\"Species\"].value_counts().head(10))\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:33:38.974505Z","iopub.execute_input":"2025-12-08T07:33:38.975467Z","iopub.status.idle":"2025-12-08T07:33:39.289155Z","shell.execute_reply.started":"2025-12-08T07:33:38.975410Z","shell.execute_reply":"2025-12-08T07:33:39.287856Z"},"papermill":{"duration":0.241456,"end_time":"2025-12-07T23:11:30.135481","exception":false,"start_time":"2025-12-07T23:11:29.894025","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualización de patrones con PCA\n\nA continuación aplicamos PCA sobre las variables numéricas para observar si existen patrones o agrupamientos naturales en los datos.\n","metadata":{"papermill":{"duration":0.007623,"end_time":"2025-12-07T23:11:30.151230","exception":false,"start_time":"2025-12-07T23:11:30.143607","status":"completed"},"tags":[]}},{"cell_type":"code","source":"from sklearn.decomposition import PCA\ndf_tmp = train[[\"Pre_GSHH_NDVI\", \"Height_Ave_cm\"]].dropna()\npca = PCA(n_components=2)\npca_res = pca.fit_transform(df_tmp)\n\nplt.scatter(pca_res[:,0], pca_res[:,1], alpha=0.5)\nplt.title(\"PCA NDVI + Height\")\nplt.xlabel(\"PC1\")\nplt.ylabel(\"PC2\")\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T07:33:41.754077Z","iopub.execute_input":"2025-12-08T07:33:41.754538Z","iopub.status.idle":"2025-12-08T07:33:42.198499Z","shell.execute_reply.started":"2025-12-08T07:33:41.754510Z","shell.execute_reply":"2025-12-08T07:33:42.197085Z"},"papermill":{"duration":0.381217,"end_time":"2025-12-07T23:11:30.542551","exception":false,"start_time":"2025-12-07T23:11:30.161334","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocesar los datos","metadata":{}},{"cell_type":"code","source":"def add_date_features(df):\n    if \"Sampling_Date\" not in df.columns:\n        # No existe la columna → no hacemos nada\n        df[\"year\"] = 0\n        df[\"month\"] = 0\n        df[\"dayofyear\"] = 0\n        df[\"season\"] = \"unknown\"\n        return df\n\n    df[\"Sampling_Date\"] = pd.to_datetime(df[\"Sampling_Date\"])\n    df[\"year\"] = df[\"Sampling_Date\"].dt.year\n    df[\"month\"] = df[\"Sampling_Date\"].dt.month\n    df[\"dayofyear\"] = df[\"Sampling_Date\"].dt.dayofyear\n\n    def season(m):\n        if m in [12,1,2]: return \"summer\"\n        if m in [3,4,5]: return \"autumn\"\n        if m in [6,7,8]: return \"winter\"\n        if m in [9,10,11]: return \"spring\"\n\n    df[\"season\"] = df[\"month\"].apply(season)\n    return df\n\ntrain = add_date_features(train)\ntest  = add_date_features(test)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T09:32:34.380086Z","iopub.execute_input":"2025-12-08T09:32:34.380444Z","iopub.status.idle":"2025-12-08T09:32:34.396237Z","shell.execute_reply.started":"2025-12-08T09:32:34.380420Z","shell.execute_reply":"2025-12-08T09:32:34.395090Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Numéricas\nnum_meta_cols = [\"Pre_GSHH_NDVI\", \"Height_Ave_cm\"]\nfor col in num_meta_cols:\n    if col not in test.columns:\n        # usa la media del train como valor por defecto\n        test[col] = train[col].mean()\n\n# Categóricas\ncat_meta_cols = [\"State\", \"Species\"]\nfor col in cat_meta_cols:\n    if col not in test.columns:\n        # usa la categoría más frecuente del train\n        test[col] = train[col].mode()[0]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T09:32:35.318671Z","iopub.execute_input":"2025-12-08T09:32:35.319044Z","iopub.status.idle":"2025-12-08T09:32:35.326626Z","shell.execute_reply.started":"2025-12-08T09:32:35.319021Z","shell.execute_reply":"2025-12-08T09:32:35.325272Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Función de extracción de features","metadata":{"papermill":{"duration":0.008343,"end_time":"2025-12-07T23:11:30.559666","exception":false,"start_time":"2025-12-07T23:11:30.551323","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def extract_features_full(img_path):\n    img = cv2.imread(img_path)\n    img = cv2.resize(img, (256,256))\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n\n    # --- COLOR FEATURES\n    means = img.mean(axis=(0,1))       # R,G,B mean\n    stds  = img.std(axis=(0,1))        # R,G,B std\n\n    g_r_ratio = means[1] / (means[2] + 1e-4)\n\n    # Hist RGB\n    hist_r = cv2.calcHist([img],[2],None,[32],[0,256]).flatten()\n    hist_g = cv2.calcHist([img],[1],None,[32],[0,256]).flatten()\n    hist_b = cv2.calcHist([img],[0],None,[32],[0,256]).flatten()\n    hist_rgb = np.concatenate([hist_r, hist_g, hist_b])\n    hist_rgb /= hist_rgb.sum()\n\n    # HSV: Saturation + Value\n    hsv_mean = hsv.mean(axis=(0,1))\n    hsv_std  = hsv.std(axis=(0,1))\n\n    # --- TEXTURE FEATURES\n    lbp = local_binary_pattern(gray, 24, 3, method='uniform')\n    lbp_hist,_ = np.histogram(lbp, bins=26, range=(0,26))\n    lbp_hist = lbp_hist / lbp_hist.sum()\n\n    # Sobel edges\n    sobel_val = sobel(gray).mean()\n\n    # Canny edges\n    edges = cv2.Canny(gray,100,200)\n    edge_density = edges.mean()\n\n    entropy_val = shannon_entropy(gray)\n\n    # --- GLCM\n    glcm = graycomatrix(gray, distances=[5], angles=[0],\n                        levels=256, symmetric=True, normed=True)\n    contrast = graycoprops(glcm,'contrast')[0][0]\n    homogeneity = graycoprops(glcm,'homogeneity')[0][0]\n\n    # --- Green index proxy\n    green_mask = ((img[:,:,1] > img[:,:,2]) & (img[:,:,1] > img[:,:,0])).mean()\n\n    return np.concatenate([\n        means, stds, [g_r_ratio],\n        hist_rgb,\n        lbp_hist,\n        hsv_mean, hsv_std,\n        [sobel_val, edge_density, entropy_val],\n        [contrast, homogeneity],\n        [green_mask]\n    ])\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:32:43.906797Z","iopub.execute_input":"2025-12-08T09:32:43.907155Z","iopub.status.idle":"2025-12-08T09:32:43.921620Z","shell.execute_reply.started":"2025-12-08T09:32:43.907131Z","shell.execute_reply":"2025-12-08T09:32:43.920444Z"},"papermill":{"duration":0.02026,"end_time":"2025-12-07T23:11:30.588404","exception":false,"start_time":"2025-12-07T23:11:30.568144","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Creamos un extractor de características visuales simples (brillo, color promedio, histograma). Estas features sí están disponibles tanto para train como para test y permiten construir un modelo funcional para el leaderboard.","metadata":{"papermill":{"duration":0.008521,"end_time":"2025-12-07T23:11:30.605517","exception":false,"start_time":"2025-12-07T23:11:30.596996","status":"completed"},"tags":[]}},{"cell_type":"code","source":"feature_names = (\n    [f\"hist_r_{i}\" for i in range(32)] +\n    [f\"hist_g_{i}\" for i in range(32)] +\n    [f\"hist_b_{i}\" for i in range(32)] +\n    [f\"lbp_{i}\" for i in range(26)] +\n    [\"edge_density\", \"entropy\", \"sobel\", \"contrast\", \"homogeneity\"]\n)\n\nlen(feature_names)\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:32:47.548986Z","iopub.execute_input":"2025-12-08T09:32:47.549318Z","iopub.status.idle":"2025-12-08T09:32:47.557698Z","shell.execute_reply.started":"2025-12-08T09:32:47.549296Z","shell.execute_reply":"2025-12-08T09:32:47.556591Z"},"papermill":{"duration":0.01841,"end_time":"2025-12-07T23:11:30.632215","exception":false,"start_time":"2025-12-07T23:11:30.613805","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Extraer features del TRAIN","metadata":{"papermill":{"duration":0.009216,"end_time":"2025-12-07T23:11:30.650046","exception":false,"start_time":"2025-12-07T23:11:30.640830","status":"completed"},"tags":[]}},{"cell_type":"code","source":"train_features = []\nfor path in train[\"image_path\"]:\n    vec = extract_features_full(base + path)\n    train_features.append(vec)\n\nX_img = pd.DataFrame(train_features)\nX_img.shape\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:32:49.036393Z","iopub.execute_input":"2025-12-08T09:32:49.036781Z","iopub.status.idle":"2025-12-08T09:36:46.085484Z","shell.execute_reply.started":"2025-12-08T09:32:49.036725Z","shell.execute_reply":"2025-12-08T09:36:46.084287Z"},"papermill":{"duration":178.070795,"end_time":"2025-12-07T23:14:28.729187","exception":false,"start_time":"2025-12-07T23:11:30.658392","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Extraemos los vectores de características de cada imagen del conjunto de entrenamiento y los combinamos con el target real.","metadata":{"papermill":{"duration":0.008644,"end_time":"2025-12-07T23:14:28.746498","exception":false,"start_time":"2025-12-07T23:14:28.737854","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"### Extraer features del TEST","metadata":{"papermill":{"duration":0.008418,"end_time":"2025-12-07T23:14:28.763438","exception":false,"start_time":"2025-12-07T23:14:28.755020","status":"completed"},"tags":[]}},{"cell_type":"code","source":"test_features = []\nfor path in test[\"image_path\"]:\n    vec = extract_features_full(base + path)\n    test_features.append(vec)\n\nX_test_img = pd.DataFrame(test_features)\n\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:36:46.087389Z","iopub.execute_input":"2025-12-08T09:36:46.087811Z","iopub.status.idle":"2025-12-08T09:36:46.795618Z","shell.execute_reply.started":"2025-12-08T09:36:46.087732Z","shell.execute_reply":"2025-12-08T09:36:46.794538Z"},"papermill":{"duration":0.542898,"end_time":"2025-12-07T23:14:29.314897","exception":false,"start_time":"2025-12-07T23:14:28.771999","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Creamos las mismas características visuales para las imágenes del conjunto de prueba (Kaggle), asegurando compatibilidad con el modelo entrenado.","metadata":{"papermill":{"duration":0.008969,"end_time":"2025-12-07T23:14:29.333028","exception":false,"start_time":"2025-12-07T23:14:29.324059","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"## Columnas metadata","metadata":{}},{"cell_type":"markdown","source":"## Preprocesamiento de los datos\n\nPara entrenar modelos de machine learning, es necesario transformar las columnas:\n\n- **Numéricas** (`Pre_GSHH_NDVI`, `Height_Ave_cm`): se escalan con StandardScaler.\n- **Fecha** (`Sampling_Date`): se convierte a datetime y se extraen Año, Mes y Día del año.\n- **Categóricas** (`State`, `Species`, `target_name`): se codifican mediante One-Hot Encoding.\n  \n","metadata":{"papermill":{"duration":0.009048,"end_time":"2025-12-07T23:14:29.350859","exception":false,"start_time":"2025-12-07T23:14:29.341811","status":"completed"},"tags":[]}},{"cell_type":"code","source":"from sklearn.preprocessing import OneHotEncoder, StandardScaler, FunctionTransformer\nfrom sklearn.ensemble import RandomForestRegressor\nfrom sklearn.compose import ColumnTransformer\nimport pandas as pd\n\nmeta_cols = [\"Pre_GSHH_NDVI\",\"Height_Ave_cm\",\"State\",\"Species\",\"year\",\"month\",\"dayofyear\",\"season\"]\n\nfull_train = pd.concat([\n        X_img.reset_index(drop=True),\n        train[meta_cols].reset_index(drop=True),\n        train[\"target_name\"].reset_index(drop=True),\n        train[\"target\"].reset_index(drop=True)\n], axis=1)\n\nfull_test = pd.concat([\n        X_test_img.reset_index(drop=True),\n        test[meta_cols].reset_index(drop=True),\n        test[\"target_name\"].reset_index(drop=True),\n        test[\"sample_id\"].reset_index(drop=True)\n], axis=1)\n\n# nombres de columnas de las features de imagen\nimg_cols = X_img.columns.astype(str)\n\nnum_cols = list(img_cols) + [\n    \"Pre_GSHH_NDVI\",\"Height_Ave_cm\",\"year\",\"month\",\"dayofyear\"\n]\ncat_cols = [\"State\",\"Species\",\"season\"]\n\npreprocess = ColumnTransformer(\n    transformers=[\n        (\"num\", StandardScaler(), num_cols),\n        (\"cat\", OneHotEncoder(handle_unknown=\"ignore\"), cat_cols)\n    ]\n)\n\n# aseguramos tipo category (aunque OneHotEncoder no lo exige, está bien)\nfull_train[cat_cols] = full_train[cat_cols].astype(\"category\")\nfull_test[cat_cols]  = full_test[cat_cols].astype(\"category\")\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:36:46.796585Z","iopub.execute_input":"2025-12-08T09:36:46.796874Z","iopub.status.idle":"2025-12-08T09:36:46.821834Z","shell.execute_reply.started":"2025-12-08T09:36:46.796844Z","shell.execute_reply":"2025-12-08T09:36:46.820551Z"},"papermill":{"duration":14.372636,"end_time":"2025-12-07T23:14:43.732380","exception":false,"start_time":"2025-12-07T23:14:29.359744","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tipo de problema: regresión\n\nLa variable objetivo (`target`) representa biomasa en gramos, un valor numérico continuo.\n\nPor tanto, este es un **problema de regresión**, no de clasificación.\n\nAdemás, la métrica oficial del concurso es R², que solo aplica a regresión.\n","metadata":{"papermill":{"duration":0.009085,"end_time":"2025-12-07T23:14:43.753213","exception":false,"start_time":"2025-12-07T23:14:43.744128","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"## ENTRENAR MODELOS SEPARADOS POR TARGET_NAME","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntargets = train[\"target_name\"].unique()\n\nmodels = {}\npreds_list = []\n\nfor t in targets:\n    print(\"\\n=============================\")\n    print(\"Entrenando modelo para:\", t)\n\n    # 1. Filtrar dataset por target_name\n    df_t = full_train[full_train[\"target_name\"] == t]\n\n    X_t = df_t.drop(columns=[\"target_name\",\"target\"])\n    y_t = df_t[\"target\"]\n\n    # 2. Split train/valid (para ver R2/RMSE)\n    X_train_t, X_val_t, y_train_t, y_val_t = train_test_split(\n        X_t, y_t, test_size=0.2, random_state=42\n    )\n\n    # 3. FIX: nombres de columnas a string\n    X_train_t = X_train_t.copy()\n    X_val_t   = X_val_t.copy()\n    X_train_t.columns = X_train_t.columns.astype(str)\n    X_val_t.columns   = X_val_t.columns.astype(str)\n\n    # 4. Modelo base con hiperparámetros \"decente\"\n    model = xgb.XGBRegressor(\n        n_estimators=400,\n        max_depth=10,\n        learning_rate=0.03,\n        subsample=0.8,\n        colsample_bytree=0.8,\n        tree_method=\"hist\",\n        reg_alpha=1.0,\n        reg_lambda=2.0,\n        min_child_weight=3,\n        random_state=42\n    )\n\n    pipe = Pipeline([\n        (\"prep\", preprocess),\n        (\"model\", model)\n    ])\n\n    # 5. Entrenamiento\n    pipe.fit(X_train_t, y_train_t)\n\n    # 6. Evaluación en hold-out\n    val_pred = pipe.predict(X_val_t)\n    print(\"   R2 hold-out:\", r2_score(y_val_t, val_pred))\n    print(\"   RMSE hold-out:\", mean_squared_error(y_val_t, val_pred, squared=False))\n\n    # Guardamos el modelo por target\n    models[t] = pipe\n\n    # 7. Predicciones en TEST para este target\n    df_test_t = full_test[full_test[\"target_name\"] == t]\n\n    X_test_t = df_test_t.drop(columns=[\"target_name\",\"sample_id\"]).copy()\n    X_test_t.columns = X_test_t.columns.astype(str)\n\n    pred_t = pipe.predict(X_test_t)\n\n    preds_list.append(pd.DataFrame({\n        \"sample_id\": df_test_t[\"sample_id\"],\n        \"target\": pred_t\n    }))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T09:36:46.823716Z","iopub.execute_input":"2025-12-08T09:36:46.824055Z","iopub.status.idle":"2025-12-08T09:37:25.730448Z","shell.execute_reply.started":"2025-12-08T09:36:46.824032Z","shell.execute_reply":"2025-12-08T09:37:25.729650Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Predict vs real + residuos","metadata":{"papermill":{"duration":0.009064,"end_time":"2025-12-07T23:14:43.772345","exception":false,"start_time":"2025-12-07T23:14:43.763281","status":"completed"},"tags":[]}},{"cell_type":"code","source":"plt.figure(figsize=(6,6))\nsns.scatterplot(x=y_val_t, y=val_pred, alpha=0.6)\nplt.plot([y_val_t.min(), y_val_t.max()],\n         [y_val_t.min(), y_val_t.max()], \"r--\")\nplt.xlabel(\"target real\")\nplt.ylabel(\"target predicho\")\nplt.title(f\"{t} — y_real vs y_pred\")\nplt.show()\n\nres = y_val_t - val_pred\nsns.histplot(res, kde=True)\nplt.title(f\"{t} — distribución de residuos\")\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:37:32.637999Z","iopub.execute_input":"2025-12-08T09:37:32.638714Z","iopub.status.idle":"2025-12-08T09:37:33.141829Z","shell.execute_reply.started":"2025-12-08T09:37:32.638678Z","shell.execute_reply":"2025-12-08T09:37:33.140248Z"},"papermill":{"duration":0.516757,"end_time":"2025-12-07T23:14:44.298954","exception":false,"start_time":"2025-12-07T23:14:43.782197","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for t in targets:\n    print(\"Gráficas para:\", t)\n\n    df_t = full_train[full_train[\"target_name\"] == t]\n    \n    X_t = df_t.drop(columns=[\"target_name\",\"target\"])\n    y_t = df_t[\"target\"]\n\n    _, X_val_t, _, y_val_t = train_test_split(\n        X_t, y_t, test_size=0.2, random_state=42\n    )\n\n    X_val_t = X_val_t.copy()\n    X_val_t.columns = X_val_t.columns.astype(str)\n\n    model_t = models[t]\n    y_pred = model_t.predict(X_val_t)\n\n    # scatter real vs pred\n    plt.figure(figsize=(6,6))\n    sns.scatterplot(x=y_val_t, y=y_pred, alpha=0.6)\n    plt.plot([y_val_t.min(), y_val_t.max()],\n             [y_val_t.min(), y_val_t.max()], \"r--\")\n    plt.xlabel(\"real\")\n    plt.ylabel(\"predicho\")\n    plt.title(f\"{t} — y_real vs y_pred\")\n    plt.show()\n\n    # residuos\n    res = y_val_t - y_pred\n    sns.histplot(res, kde=True)\n    plt.title(f\"{t} — residuos\")\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T09:37:33.276596Z","iopub.execute_input":"2025-12-08T09:37:33.277124Z","iopub.status.idle":"2025-12-08T09:37:35.633852Z","shell.execute_reply.started":"2025-12-08T09:37:33.277090Z","shell.execute_reply":"2025-12-08T09:37:35.632835Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.concat(preds_list).reset_index(drop=True)\nsubmission = submission.sort_values(\"sample_id\").reset_index(drop=True)\n\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()\n","metadata":{"execution":{"iopub.status.busy":"2025-12-08T09:37:35.635341Z","iopub.execute_input":"2025-12-08T09:37:35.635704Z","iopub.status.idle":"2025-12-08T09:37:35.650556Z","shell.execute_reply.started":"2025-12-08T09:37:35.635674Z","shell.execute_reply":"2025-12-08T09:37:35.649555Z"},"papermill":{"duration":14.630056,"end_time":"2025-12-07T23:14:58.940147","exception":false,"start_time":"2025-12-07T23:14:44.310091","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"papermill":{"duration":0.011949,"end_time":"2025-12-07T23:18:55.346006","exception":false,"start_time":"2025-12-07T23:18:55.334057","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}