{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","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"}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**Equipo:**  \n**Integrantes:** \\\nAna Sofía Matti Ríos \\\nJesus David Ayala Morales \\\nDaniel Eduardo Alvarez Terrazas \n\n**Curso:**\nReconocimiento de Patrones    \n\nEn este proyecto se aborda el problema de estimar la biomasa a partir de imágenes.  \nEl objetivo es construir un modelo capaz de predecir un valor continuo de biomasa a partir de información visual.","metadata":{}},{"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":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:44.987924Z","iopub.execute_input":"2025-12-08T06:01:44.988275Z","iopub.status.idle":"2025-12-08T06:01:45.424318Z","shell.execute_reply.started":"2025-12-08T06:01:44.988251Z","shell.execute_reply":"2025-12-08T06:01:45.423557Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nfrom skimage.io import imread\nfrom skimage.transform import resize\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom sklearn.model_selection import KFold, GridSearchCV, cross_val_score\nfrom sklearn.metrics import mean_squared_error, r2_score, make_scorer\nfrom sklearn.ensemble import RandomForestRegressor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.425625Z","iopub.execute_input":"2025-12-08T06:01:45.425895Z","iopub.status.idle":"2025-12-08T06:01:45.430138Z","shell.execute_reply.started":"2025-12-08T06:01:45.425875Z","shell.execute_reply":"2025-12-08T06:01:45.429402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\n\nTRAIN_CSV = '/kaggle/input/csiro-biomass/train.csv'\n#TEST_CSV  = '/kaggle/input/csiro-biomass/test.csv'\nTRAIN_IMG_ROOT = '/kaggle/input/csiro-biomass/'\n#TEST_IMG_ROOT  = '/kaggle/input/csiro-biomass'\n\ntrain_df = pd.read_csv(TRAIN_CSV)\n\nprint(\"Train shape:\", train_df.shape)\ntrain_df.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.430936Z","iopub.execute_input":"2025-12-08T06:01:45.431210Z","iopub.status.idle":"2025-12-08T06:01:45.463717Z","shell.execute_reply.started":"2025-12-08T06:01:45.431186Z","shell.execute_reply":"2025-12-08T06:01:45.462871Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TEST_CSV = '/kaggle/input/csiro-biomass/test.csv'\nTEST_IMG_ROOT = '/kaggle/input/csiro-biomass/'\n\ntest_meta = pd.read_csv(TEST_CSV)\n\ntest_meta['img_fullpath'] = test_meta['image_path'].apply(\n    lambda x: os.path.join(TEST_IMG_ROOT, x)\n)\n\ntest_meta['image_id'] = test_meta['sample_id'].str.split(\"__\").str[0]\ntest_meta['target_name'] = test_meta['sample_id'].str.split(\"__\").str[1]\n\ntest_images = test_meta[['image_id', 'img_fullpath']].drop_duplicates().reset_index(drop=True)\n\nprint(\"Filas en test_meta (formato largo):\", test_meta.shape[0])\nprint(\"Imágenes únicas en test:\", test_images.shape[0])\n\ntest_meta.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.465197Z","iopub.execute_input":"2025-12-08T06:01:45.465410Z","iopub.status.idle":"2025-12-08T06:01:45.482548Z","shell.execute_reply.started":"2025-12-08T06:01:45.465394Z","shell.execute_reply":"2025-12-08T06:01:45.481803Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Cada imagen aparece 5 veces o sea una por cada target_name asi que vamos a pivotear el dataframe para tener una fila por imagen.","metadata":{}},{"cell_type":"code","source":"train_df[\"image_id\"] = train_df[\"sample_id\"].apply(lambda x: x.split(\"__\")[0])\n\ndf_pivot = train_df.pivot_table(\n    index=[\"image_id\", \"image_path\", \"Sampling_Date\", \"State\", \"Species\", \"Pre_GSHH_NDVI\", \"Height_Ave_cm\"],\n    columns=\"target_name\",\n    values=\"target\"\n).reset_index()\n\ndf_pivot.columns.name = None\ndf_final = df_pivot.copy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.483411Z","iopub.execute_input":"2025-12-08T06:01:45.483689Z","iopub.status.idle":"2025-12-08T06:01:45.501051Z","shell.execute_reply.started":"2025-12-08T06:01:45.483671Z","shell.execute_reply":"2025-12-08T06:01:45.500464Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_final['img_fullpath'] = df_final['image_path'].apply(lambda x: os.path.join(TRAIN_IMG_ROOT, x))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.501771Z","iopub.execute_input":"2025-12-08T06:01:45.502014Z","iopub.status.idle":"2025-12-08T06:01:45.506790Z","shell.execute_reply.started":"2025-12-08T06:01:45.501985Z","shell.execute_reply":"2025-12-08T06:01:45.506241Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_final.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.507587Z","iopub.execute_input":"2025-12-08T06:01:45.508311Z","iopub.status.idle":"2025-12-08T06:01:45.535829Z","shell.execute_reply.started":"2025-12-08T06:01:45.508291Z","shell.execute_reply":"2025-12-08T06:01:45.535137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fecha → año/mes\ndf_final['Sampling_Date'] = pd.to_datetime(df_final['Sampling_Date'], errors='coerce')\ndf_final['year'] = df_final['Sampling_Date'].dt.year\ndf_final['month'] = df_final['Sampling_Date'].dt.month\n\n# Codificación de variables categóricas\ndf_final['State'] = df_final['State'].astype('category').cat.codes\ndf_final['Species'] = df_final['Species'].astype('category').cat.codes\n\n# Target\nTARGET_COL = 'Dry_Total_g'\ndf_final['log_target'] = np.log1p(df_final[TARGET_COL])\n\nTARGET_COLS = [\n    'Dry_Green_g',\n    'Dry_Dead_g',\n    'Dry_Clover_g',\n    'GDM_g',\n    'Dry_Total_g'\n]\n\nLOG_TARGET_COLS = [f'log_{c}' for c in TARGET_COLS]\nfor col, log_col in zip(TARGET_COLS, LOG_TARGET_COLS):\n    df_final[log_col] = np.log1p(df_final[col])\n\n# Metadatos a usar\nMETA_COLS = ['Pre_GSHH_NDVI', 'Height_Ave_cm', 'State', 'Species', 'year', 'month']\n\nprint(\"Shape final:\", df_final.shape)\ndf_final.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.536629Z","iopub.execute_input":"2025-12-08T06:01:45.536922Z","iopub.status.idle":"2025-12-08T06:01:45.580414Z","shell.execute_reply.started":"2025-12-08T06:01:45.536898Z","shell.execute_reply":"2025-12-08T06:01:45.579896Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## EDA","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(8,5))\nsns.histplot(df_final[TARGET_COL], kde=True, bins=30, color='green')\nplt.title(f\"Distribución de {TARGET_COL}\")\n#plt.xlabel(TARGET_COL)\n#plt.ylabel(\"Frecuencia\")\nplt.show()\n\ndisplay(df_final[TARGET_COL].describe())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.581247Z","iopub.execute_input":"2025-12-08T06:01:45.581497Z","iopub.status.idle":"2025-12-08T06:01:45.836660Z","shell.execute_reply.started":"2025-12-08T06:01:45.581480Z","shell.execute_reply":"2025-12-08T06:01:45.836005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"missing = df_final.isna().sum()\nprint(\"vals faltantes por columna:\\n\", missing)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.839087Z","iopub.execute_input":"2025-12-08T06:01:45.839505Z","iopub.status.idle":"2025-12-08T06:01:45.844849Z","shell.execute_reply.started":"2025-12-08T06:01:45.839486Z","shell.execute_reply":"2025-12-08T06:01:45.844276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(8,6))\nsns.heatmap(df_final[META_COLS + [TARGET_COL]].corr(), annot=True, cmap='coolwarm')\nplt.title(\"Correlación entre metadatos y target\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:45.845546Z","iopub.execute_input":"2025-12-08T06:01:45.845842Z","iopub.status.idle":"2025-12-08T06:01:46.147721Z","shell.execute_reply.started":"2025-12-08T06:01:45.845818Z","shell.execute_reply":"2025-12-08T06:01:46.147149Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## extracción de features","metadata":{}},{"cell_type":"code","source":"for path in df_final['img_fullpath'].head().values:\n    print(path, os.path.exists(path))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:46.148442Z","iopub.execute_input":"2025-12-08T06:01:46.148654Z","iopub.status.idle":"2025-12-08T06:01:46.153794Z","shell.execute_reply.started":"2025-12-08T06:01:46.148638Z","shell.execute_reply":"2025-12-08T06:01:46.152925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = (64, 64)\n\ndef extract_image_features(df, img_col='img_fullpath', size=IMG_SIZE):\n    features = []\n    for path in tqdm(df[img_col].values):\n        img = imread(path)\n        if img.ndim == 3:  # escala de gtises \n            img = img.mean(axis=2)\n        img_resized = resize(img, size, anti_aliasing=True)\n        features.append(img_resized.flatten())\n    return np.array(features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:46.154444Z","iopub.execute_input":"2025-12-08T06:01:46.154661Z","iopub.status.idle":"2025-12-08T06:01:46.170029Z","shell.execute_reply.started":"2025-12-08T06:01:46.154641Z","shell.execute_reply":"2025-12-08T06:01:46.169453Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train_img = extract_image_features(df_final)  # una fila por imagen de train\nX_test_img  = extract_image_features(test_images, img_col='img_fullpath')\n\nprint(\"Shape imágenes train:\", X_train_img.shape)\nprint(\"Shape imágenes test:\", X_test_img.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:01:46.170709Z","iopub.execute_input":"2025-12-08T06:01:46.170929Z","iopub.status.idle":"2025-12-08T06:03:42.541076Z","shell.execute_reply.started":"2025-12-08T06:01:46.170908Z","shell.execute_reply":"2025-12-08T06:03:42.540428Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#df_final.columns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:03:42.541871Z","iopub.execute_input":"2025-12-08T06:03:42.542074Z","iopub.status.idle":"2025-12-08T06:03:42.545626Z","shell.execute_reply.started":"2025-12-08T06:03:42.542057Z","shell.execute_reply":"2025-12-08T06:03:42.545066Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train = np.hstack([X_train_img, df_final[META_COLS].values])\nX_test  = np.hstack([X_test_img,  np.zeros((X_test_img.shape[0], len(META_COLS)))])  # test no tiene metadatos\n\ny = df_final[LOG_TARGET_COLS].values\n\nprint(\"Shape X_train:\", X_train.shape)\nprint(\"Shape X_test:\", X_test.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:03:42.546263Z","iopub.execute_input":"2025-12-08T06:03:42.546555Z","iopub.status.idle":"2025-12-08T06:03:42.568322Z","shell.execute_reply.started":"2025-12-08T06:03:42.546539Z","shell.execute_reply":"2025-12-08T06:03:42.567681Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Definimos RMSE para el scoring","metadata":{}},{"cell_type":"code","source":"WEIGHTS = np.array([0.1,0.1,0.1,0.2,0.5])\ndef rmse_weighted(y_true, y_pred):\n    se = (y_true - y_pred) ** 2\n    mse_per_target = se.mean(axis=0)\n    wmse = np.sum(WEIGHTS * mse_per_target)\n    return np.sqrt(wmse)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:03:42.569058Z","iopub.execute_input":"2025-12-08T06:03:42.569385Z","iopub.status.idle":"2025-12-08T06:03:42.579709Z","shell.execute_reply.started":"2025-12-08T06:03:42.569358Z","shell.execute_reply":"2025-12-08T06:03:42.579193Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Hacemos la eval iniciañ con cross-validation","metadata":{}},{"cell_type":"code","source":"rf = RandomForestRegressor(random_state=42, n_jobs=-1)\n\ncv = KFold(n_splits=3, shuffle=True, random_state=42)\n\nr2_scores = cross_val_score(rf, X_train, y, cv=cv, scoring='r2')\nrmse_scores = -cross_val_score(rf, X_train, y, cv=cv,\n                                scoring=make_scorer(rmse_weighted, greater_is_better=False))\n\nprint(\"R² promedio CV:\", r2_scores.mean())\nprint(\"RMSE promedio CV:\", rmse_scores.mean())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:03:42.580407Z","iopub.execute_input":"2025-12-08T06:03:42.580652Z","iopub.status.idle":"2025-12-08T06:05:09.797030Z","shell.execute_reply.started":"2025-12-08T06:03:42.580636Z","shell.execute_reply":"2025-12-08T06:05:09.796390Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Ajustamos hiperparametros con GridSeachCV","metadata":{}},{"cell_type":"code","source":"param_grid = {\n    'n_estimators': [100, 200],\n    'max_depth': [10, 20],\n    #'min_samples_split': [2, 5],\n    #'min_samples_leaf': [1, 2]\n}\n\ngrid_rf = GridSearchCV(\n    estimator=rf,\n    param_grid=param_grid,\n    scoring=make_scorer(rmse_weighted, greater_is_better=False),\n    cv=cv,\n    n_jobs=-1,\n    verbose=2\n)\n\ngrid_rf.fit(X_train, y)\n\nbest_model = grid_rf.best_estimator_\nprint(\"Mejores parámetros encontrados:\", grid_rf.best_params_)\nprint(\"Mejor RMSE CV:\", -grid_rf.best_score_)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:05:09.797820Z","iopub.execute_input":"2025-12-08T06:05:09.798007Z","iopub.status.idle":"2025-12-08T06:09:42.824192Z","shell.execute_reply.started":"2025-12-08T06:05:09.797992Z","shell.execute_reply":"2025-12-08T06:09:42.823436Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pred = best_model.predict(X_train)\nr2_total = r2_score(y, y_pred)\nrmse_total = rmse_weighted(y, y_pred)\n\nprint(f\"RMSE sobre todo el set: {rmse_total:.4f}\")\nprint(f\"R² sobre todo el set: {r2_total:.4f}\")\n\n# Visualización predicciones vs reales\nplt.figure(figsize=(6,6))\nplt.scatter(y, y_pred, alpha=0.6)\nplt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--')\nplt.xlabel(\"Log Biomasa real\")\nplt.ylabel(\"Log Biomasa predicha\")\nplt.title(\"Predicciones vs Valores reales\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:09:42.825075Z","iopub.execute_input":"2025-12-08T06:09:42.825411Z","iopub.status.idle":"2025-12-08T06:09:43.024414Z","shell.execute_reply.started":"2025-12-08T06:09:42.825383Z","shell.execute_reply":"2025-12-08T06:09:43.023572Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Predicciones para el test set","metadata":{}},{"cell_type":"code","source":"y_test_pred_log = best_model.predict(X_test)\ny_test_pred = np.expm1(y_test_pred_log)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:09:43.025249Z","iopub.execute_input":"2025-12-08T06:09:43.025493Z","iopub.status.idle":"2025-12-08T06:09:43.053576Z","shell.execute_reply.started":"2025-12-08T06:09:43.025466Z","shell.execute_reply":"2025-12-08T06:09:43.052864Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_meta = pd.read_csv(\"/kaggle/input/csiro-biomass/test.csv\")\nreal_sample_id = test_meta[\"sample_id\"].iloc[0]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:09:43.054314Z","iopub.execute_input":"2025-12-08T06:09:43.054538Z","iopub.status.idle":"2025-12-08T06:09:43.063402Z","shell.execute_reply.started":"2025-12-08T06:09:43.054522Z","shell.execute_reply":"2025-12-08T06:09:43.062718Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_df = pd.DataFrame(y_test_pred, columns=TARGET_COLS)\npred_df['image_id'] = test_images['image_id'].values\n\npred_long = pred_df.melt(\n    id_vars='image_id',\n    value_vars=TARGET_COLS,\n    var_name='target_name',\n    value_name='target'\n)\n\npred_long['sample_id'] = pred_long['image_id'] + \"__\" + pred_long['target_name']\n\nsubmission_df = test_meta[['sample_id']].merge(\n    pred_long[['sample_id', 'target']],\n    on='sample_id',\n    how='left'\n)\n\nsubmission_df = submission_df.sort_values('sample_id').reset_index(drop=True)\n\nsubmission_df.to_csv(\"/kaggle/working/submission.csv\", index=False)\nsubmission_df.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:09:43.064168Z","iopub.execute_input":"2025-12-08T06:09:43.064595Z","iopub.status.idle":"2025-12-08T06:09:43.088065Z","shell.execute_reply.started":"2025-12-08T06:09:43.064569Z","shell.execute_reply":"2025-12-08T06:09:43.087393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"submission_df shape:\", submission_df.shape)\nprint(\"test_meta shape:\", test_meta.shape)\nprint(\"Columnas:\", submission_df.columns.tolist())\n\nprint(\"¿Hay NaNs en target?:\", submission_df['target'].isna().any())\n\nsubmission_df.head()\nsubmission_df.tail()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-08T06:09:43.089089Z","iopub.execute_input":"2025-12-08T06:09:43.089395Z","iopub.status.idle":"2025-12-08T06:09:43.109117Z","shell.execute_reply.started":"2025-12-08T06:09:43.089369Z","shell.execute_reply":"2025-12-08T06:09:43.108477Z"}},"outputs":[],"execution_count":null}]}