{"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":"none","dataSources":[{"sourceId":97984,"databundleVersionId":14096757,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# ECG","metadata":{}},{"cell_type":"markdown","source":"## Visualizar señales de distintos pacientes","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nimport random\nimport cv2\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nBASE_PATH = '/kaggle/input/physionet-ecg-image-digitization'\nTRAIN_PATH = os.path.join(BASE_PATH, 'train')\n\ndef visualizar_paciente_completo():\n    if not os.path.exists(TRAIN_PATH):\n        return\n\n    all_patients = [d for d in os.listdir(TRAIN_PATH) if os.path.isdir(os.path.join(TRAIN_PATH, d))]\n    if not all_patients:\n        return\n        \n    patient_id = random.choice(all_patients)\n    patient_folder = os.path.join(TRAIN_PATH, patient_id)\n    \n    csv_path = os.path.join(patient_folder, f\"{patient_id}.csv\")\n    if not os.path.exists(csv_path):\n        csvs = glob.glob(os.path.join(patient_folder, \"*.csv\"))\n        if csvs: csv_path = csvs[0]\n    \n    images = sorted(glob.glob(os.path.join(patient_folder, \"*.png\")) + glob.glob(os.path.join(patient_folder, \"*.jpg\")))\n    \n    total_plots = len(images) + 1\n    \n    plt.figure(figsize=(20, 4 * total_plots))\n    \n    plt.subplot(total_plots, 1, 1)\n    if os.path.exists(csv_path):\n        df = pd.read_csv(csv_path)\n        if 'II' in df.columns:\n            plt.plot(df['II'], color='black', label='Lead II', linewidth=1)\n\n        plt.title(f\"Target Signal: {patient_id}\")\n        plt.legend(loc='upper right')\n        plt.grid(True, alpha=0.3)\n        plt.xlim(0, len(df))\n    \n    for i, img_path in enumerate(images):\n        img = cv2.imread(img_path)\n        if img is not None:\n            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n            plt.subplot(total_plots, 1, i + 2)\n            plt.imshow(img)\n            plt.title(os.path.basename(img_path))\n            plt.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\nvisualizar_paciente_completo()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T02:46:59.005272Z","iopub.execute_input":"2025-12-04T02:46:59.005594Z","iopub.status.idle":"2025-12-04T02:47:18.104381Z","shell.execute_reply.started":"2025-12-04T02:46:59.005569Z","shell.execute_reply":"2025-12-04T02:47:18.103066Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Análisis estadistico de las imágenes ","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nimport pandas as pd\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm.notebook import tqdm\n\n# Configuración\nTRAIN_PATH = '/kaggle/input/physionet-ecg-image-digitization/train'\nLIMIT = 1000  # Cantidad de pacientes a analizar\n\nstats_data = []\n\n# Obtener carpetas de pacientes\nif os.path.exists(TRAIN_PATH):\n    all_patients = [d for d in os.listdir(TRAIN_PATH) if os.path.isdir(os.path.join(TRAIN_PATH, d))]\n    target_patients = all_patients[:LIMIT]\n\n    for pid in tqdm(target_patients):\n        p_path = os.path.join(TRAIN_PATH, pid)\n        \n        # 1. Leer CSV de Señal\n        csv_file = os.path.join(p_path, f\"{pid}.csv\")\n        min_val, max_val, sig_len = None, None, None\n        \n        if os.path.exists(csv_file):\n            try:\n                df = pd.read_csv(csv_file)\n                # Seleccionar solo columnas numéricas por seguridad\n                cols = df.select_dtypes(include=['float64', 'int64']).columns\n                min_val = df[cols].min().min()\n                max_val = df[cols].max().max()\n                sig_len = len(df)\n            except: pass\n            \n        # 2. Leer dimensiones de una Imagen (referencia)\n        # Priorizamos jpg si existe, sino png\n        imgs = glob.glob(os.path.join(p_path, \"*.png\"))\n        w, h = None, None\n        if imgs:\n            img = cv2.imread(imgs[0])\n            if img is not None:\n                h, w = img.shape[:2]\n\n        stats_data.append({\n            'id': pid,\n            'width': w,\n            'height': h,\n            'min_mv': min_val,\n            'max_mv': max_val,\n            'sig_len': sig_len\n        })\n\n    # Visualización de Resultados\n    df = pd.DataFrame(stats_data)\n\n    plt.figure(figsize=(18, 5))\n\n    plt.subplot(1, 3, 1)\n    sns.scatterplot(data=df, x='width', y='height', alpha=0.5)\n    plt.title(\"Dispersión de Tamaños (Resize necesario)\")\n    plt.xlabel(\"Ancho\")\n    plt.ylabel(\"Alto\")\n\n    plt.subplot(1, 3, 2)\n    sns.histplot(df['max_mv'], color='green', kde=True, label='Max')\n    sns.histplot(df['min_mv'], color='red', kde=True, label='Min')\n    plt.title(\"Rango de Voltajes (mV)\")\n    plt.legend()\n\n    plt.subplot(1, 3, 3)\n    if 'sig_len' in df.columns:\n        sns.histplot(df['sig_len'], bins=10, color='gray')\n    plt.title(\"Longitud de Señal\")\n\n    plt.show()\n\n    print(\"Estadísticas de Voltaje:\")\n    print(df[['min_mv', 'max_mv']].describe())\n\nelse:\n    print(\"No se encontró la ruta del dataset.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T02:47:57.156035Z","iopub.execute_input":"2025-12-04T02:47:57.158052Z","iopub.status.idle":"2025-12-04T02:53:42.106671Z","shell.execute_reply.started":"2025-12-04T02:47:57.157958Z","shell.execute_reply":"2025-12-04T02:53:42.105340Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport os\nimport glob\nimport cv2\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm.notebook import tqdm\n\n# Configuración\nTRAIN_PATH = '/kaggle/input/physionet-ecg-image-digitization/train'\n\nprint(\" Iniciando Escáner de Calidad (PNG y JPG)...\")\n\nquality_stats = []\n\n# 1. Búsqueda robusta de imágenes (PNG y JPG)\n# Usamos un patrón que encuentre ambas extensiones recursivamente\nif os.path.exists(TRAIN_PATH):\n    # Buscamos primero PNG (que son la mayoría)\n    all_images = glob.glob(os.path.join(TRAIN_PATH, \"**/*.png\"), recursive=True)\n    # Agregamos los JPG si existen\n    all_images += glob.glob(os.path.join(TRAIN_PATH, \"**/*.jpg\"), recursive=True)\n    \n    # Tomamos una muestra para no tardar demasiado\n    LIMIT = 500\n    sample_images = all_images[:LIMIT] if len(all_images) > LIMIT else all_images\n    \n    print(f\" Se encontraron {len(all_images)} imágenes. Analizando muestra de {len(sample_images)}...\")\n\n    if len(sample_images) == 0:\n        print(\" ERROR: No se encontraron imágenes .png ni .jpg en la ruta.\")\n    else:\n        # 2. Análisis\n        for img_path in tqdm(sample_images):\n            try:\n                img = cv2.imread(img_path)\n                if img is None: continue\n                    \n                # A. Puntaje de Blur (Varianza del Laplaciano)\n                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n                blur_score = cv2.Laplacian(gray, cv2.CV_64F).var()\n                \n                # B. Puntaje de Saturación (Color)\n                hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n                saturation_mean = hsv[:, :, 1].mean()\n                \n                quality_stats.append({\n                    'file': os.path.basename(img_path),\n                    'blur_score': blur_score,\n                    'saturation_score': saturation_mean\n                })\n            except Exception as e:\n                pass\n\n        # 3. Visualización (Solo si hay datos)\n        if len(quality_stats) > 0:\n            df_qual = pd.DataFrame(quality_stats)\n\n            plt.figure(figsize=(14, 6))\n\n            # Gráfica de Nitidez\n            plt.subplot(1, 2, 1)\n            sns.histplot(df_qual['blur_score'], color='blue', kde=True)\n            plt.axvline(100, color='red', linestyle='--', label='Umbral Nitidez (100)')\n            plt.title(\"Distribución de Nitidez (Blur)\")\n            plt.xlabel(\"Nitidez (Menos = Más borroso)\")\n            plt.legend()\n\n            # Gráfica de Saturación\n            plt.subplot(1, 2, 2)\n            sns.histplot(df_qual['saturation_score'], color='purple', kde=True)\n            plt.title(\"Distribución de Saturación (Color/Cuadrícula)\")\n            plt.xlabel(\"Intensidad de Color Promedio\")\n\n            plt.tight_layout()\n            plt.show()\n            \n            print(f\"Diagnóstico: {len(df_qual[df_qual['blur_score'] < 100])} imágenes son muy borrosas.\")\n        else:\n            print(\" No se pudieron procesar las imágenes (lista vacía o errores de lectura).\")\n\nelse:\n    print(f\" La ruta no existe: {TRAIN_PATH}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T02:54:30.327446Z","iopub.execute_input":"2025-12-04T02:54:30.328177Z","iopub.status.idle":"2025-12-04T02:58:36.178272Z","shell.execute_reply.started":"2025-12-04T02:54:30.328141Z","shell.execute_reply":"2025-12-04T02:58:36.177198Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport glob\nimport os\nimport pandas as pd\nfrom tqdm.notebook import tqdm\n\n# --- CONFIGURACIÓN ---\nTRAIN_PATH = '/kaggle/input/physionet-ecg-image-digitization/train'\nLIMIT = 200  # Analizar 200 imágenes para no tardar tanto (ajusta según necesites)\n\nprint(f\" Iniciando Radar de Calidad HSV (Límite: {LIMIT} imágenes)...\")\n\n# 1. BÚSQUEDA ROBUSTA DE IMÁGENES\n# Buscamos tanto PNG como JPG\nall_images = glob.glob(os.path.join(TRAIN_PATH, \"**/*.png\"), recursive=True)\nall_images += glob.glob(os.path.join(TRAIN_PATH, \"**/*.jpg\"), recursive=True)\n\n# Tomamos la muestra\nif len(all_images) > LIMIT:\n    # Mezclamos al azar para tener variedad\n    import random\n    random.shuffle(all_images)\n    sample_images = all_images[:LIMIT]\nelse:\n    sample_images = all_images\n\nprint(f\" Se encontraron {len(all_images)} imágenes en total. Analizando muestra de {len(sample_images)}...\")\n\nstats = []\n\n# 2. PROCESAMIENTO MASIVO\nfor img_path in tqdm(sample_images):\n    try:\n        # Cargar imagen\n        img = cv2.imread(img_path)\n        if img is None: continue\n            \n        # Conversión a HSV\n        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n        \n        # Métrica Clave: Saturación Media\n        # Alto valor = Mucha cuadrícula, manchas de café, o fondo de color.\n        # Bajo valor (cerca de 0) = Imagen limpia en blanco y negro.\n        sat_score = hsv[:, :, 1].mean()\n        \n        stats.append({\n            'path': img_path,\n            'filename': os.path.basename(img_path),\n            'saturation_score': sat_score\n        })\n        \n    except Exception as e:\n        pass\n\n# Crear DataFrame\ndf = pd.DataFrame(stats)\n\n# 3. VISUALIZACIÓN GLOBAL\nif not df.empty:\n    plt.figure(figsize=(12, 5))\n    \n    # Histograma de Saturación\n    sns.histplot(df['saturation_score'], color='purple', kde=True)\n    plt.axvline(df['saturation_score'].mean(), color='red', linestyle='--', label='Media')\n    plt.title(\"Distribución de 'Suciedad' (Saturación HSV)\")\n    plt.xlabel(\"Saturación Promedio (0 = B/N Puro, >20 = Cuadrícula Fuerte)\")\n    plt.legend()\n    plt.show()\n    \n    print(f\"Saturación Promedio del Dataset: {df['saturation_score'].mean():.2f}\")\n    \n    # 4. GALERÍA DE LOS \"PEORES\" CASOS\n    # Vamos a ver las 3 imágenes con MAYOR saturación (las más difíciles)\n    print(\"\\n ANALIZANDO LAS 3 IMÁGENES MÁS 'SUCIAS' (Mayor Saturación)...\")\n    \n    worst_images = df.sort_values('saturation_score', ascending=False).head(3)\n    \n    for idx, row in worst_images.iterrows():\n        img = cv2.imread(row['path'])\n        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n        \n        plt.figure(figsize=(15, 4))\n        plt.suptitle(f\"Archivo: {row['filename']} | Saturación: {row['saturation_score']:.1f}\", fontsize=14)\n        \n        # Imagen Original\n        plt.subplot(1, 4, 1)\n        plt.imshow(img_rgb)\n        plt.title(\"Original\")\n        plt.axis('off')\n        \n        # Canal H (Matiz)\n        plt.subplot(1, 4, 2)\n        plt.imshow(img_hsv[:,:,0], cmap='hsv') \n        plt.title(\"Hue (Tono)\")\n        plt.axis('off')\n        \n        # Canal S (Saturación) - EL IMPORTANTE\n        plt.subplot(1, 4, 3)\n        plt.imshow(img_hsv[:,:,1], cmap='magma') \n        plt.title(\"Saturation (Intensidad)\")\n        plt.axis('off')\n        \n        # Canal V (Brillo)\n        plt.subplot(1, 4, 4)\n        plt.imshow(img_hsv[:,:,2], cmap='gray')\n        plt.title(\"Value (Brillo/Trazo)\")\n        plt.axis('off')\n        \n        plt.tight_layout()\n        plt.show()\n\nelse:\n    print(\" No se pudieron procesar datos.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T02:58:53.143428Z","iopub.execute_input":"2025-12-04T02:58:53.143790Z","iopub.status.idle":"2025-12-04T03:00:31.035642Z","shell.execute_reply.started":"2025-12-04T02:58:53.143763Z","shell.execute_reply":"2025-12-04T03:00:31.033717Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport glob\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom skimage import io, color, filters, morphology\nimport random\n\n# --- 1. BUSCAR UNA IMAGEN EN KAGGLE ---\nBASE_PATH = '/kaggle/input/physionet-ecg-image-digitization/train'\n\n# Buscamos archivos .jpg recursivamente (suelen ser las fotos difíciles/sucias)\n# Si no encuentra jpg, busca png\nimage_paths = glob.glob(os.path.join(BASE_PATH, \"**/*.jpg\"), recursive=True)\nif not image_paths:\n    image_paths = glob.glob(os.path.join(BASE_PATH, \"**/*.png\"), recursive=True)\n\nif len(image_paths) > 0:\n    # Seleccionamos una al azar para probar\n    img_path = random.choice(image_paths)\n    print(f\"Procesando archivo: {img_path}\")\n\n    try:\n        # --- 2. CARGAR Y CONVERTIR ---\n        img = io.imread(img_path)\n        \n        # Convertir a HSV (skimage maneja rangos 0-1 para floats)\n        img_hsv = color.rgb2hsv(img)\n        h = img_hsv[:, :, 0]\n        s = img_hsv[:, :, 1] # Saturación\n        v = img_hsv[:, :, 2] # Valor (Brillo)\n\n        # --- 3. FILTRO DINÁMICO (OTSU) PARA LA CUADRÍCULA ---\n        # Calcula el umbral de separación automáticamente\n        thresh_s = filters.threshold_otsu(s)\n        grid_mask = s > thresh_s\n\n        # --- 4. FILTRO ADAPTATIVO PARA LA SEÑAL ---\n        # Detecta lo oscuro comparando con el vecindario local (resistente a sombras)\n        block_size = 35 \n        adaptive_thresh = filters.threshold_local(v, block_size, offset=0.1)\n        signal_mask = v < adaptive_thresh\n\n        # --- 5. FUSIÓN Y LIMPIEZA ---\n        # Dilatamos la máscara de la cuadrícula para cubrir bordes rosados\n        grid_mask_dilated = morphology.dilation(grid_mask, morphology.square(3))\n        \n        # Lógica: La señal es lo que es OSCURO y NO es CUADRÍCULA\n        final_clean = signal_mask & (~grid_mask_dilated)\n\n        # --- 6. VISUALIZACIÓN ---\n        plt.figure(figsize=(16, 10))\n\n        # Original\n        plt.subplot(2, 3, 1)\n        plt.imshow(img)\n        plt.title(\"Original\")\n        plt.axis('off')\n\n        # Canal Saturación\n        plt.subplot(2, 3, 2)\n        plt.imshow(s, cmap='magma')\n        plt.title(\"Canal Saturación\")\n        plt.axis('off')\n\n        # Histograma + Otsu\n        plt.subplot(2, 3, 3)\n        plt.hist(s.ravel(), 256, [0, 1], color='purple', alpha=0.6)\n        plt.axvline(thresh_s, color='red', linestyle='--', linewidth=2, label=f'Umbral: {thresh_s:.2f}')\n        plt.title(\"Histograma Saturación\")\n        plt.legend()\n\n        # Máscara Cuadrícula\n        plt.subplot(2, 3, 4)\n        plt.imshow(grid_mask, cmap='gray')\n        plt.title(\"Cuadrícula Detectada\")\n        plt.axis('off')\n\n        # Máscara Señal Bruta\n        plt.subplot(2, 3, 5)\n        plt.imshow(signal_mask, cmap='gray')\n        plt.title(\"Señal Bruta (Con ruido)\")\n        plt.axis('off')\n\n        # Resultado Final\n        plt.subplot(2, 3, 6)\n        plt.imshow(final_clean, cmap='gray')\n        plt.title(\"Resultado Final (Limpio)\")\n        plt.axis('off')\n\n        plt.tight_layout()\n        plt.show()\n\n    except Exception as e:\n        print(f\"Error procesando la imagen: {e}\")\nelse:\n    print(\"No se encontraron imágenes en la ruta especificada.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T03:10:42.226817Z","iopub.execute_input":"2025-12-04T03:10:42.227222Z","iopub.status.idle":"2025-12-04T03:11:05.265873Z","shell.execute_reply.started":"2025-12-04T03:10:42.227191Z","shell.execute_reply":"2025-12-04T03:11:05.263979Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### crear modelo","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom scipy import signal\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.multioutput import MultiOutputRegressor\nfrom sklearn.linear_model import Ridge\nfrom sklearn.neighbors import KNeighborsRegressor\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.pipeline import make_pipeline\nfrom tqdm.notebook import tqdm\n\nBASE_PATH = '/kaggle/input/physionet-ecg-image-digitization/train'\nIMG_SIZE = (64, 128)  # (Alto, Ancho) - Muy pequeño para que Sklearn aguante\nTARGET_LEN = 5000     # Puntos de salida\nLIMIT_SAMPLES = 1000  # Usamos solo 1000 pacientes para no saturar RAM\n\ndef load_data_sklearn(base_path, limit=1000):\n    X = []\n    y = []\n    \n    # Obtener lista de carpetas\n    if not os.path.exists(base_path): return np.array([]), np.array([])\n    patients = [d for d in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, d))]\n    patients = patients[:limit] # Recortar lista\n    \n    print(f\"Cargando datos de {len(patients)} pacientes en RAM...\")\n    \n    for pid in tqdm(patients):\n        folder = os.path.join(base_path, pid)\n        \n        # Buscar CSV e Imagen\n        csvs = glob.glob(os.path.join(folder, \"*.csv\"))\n        imgs = glob.glob(os.path.join(folder, \"*.png\")) + glob.glob(os.path.join(folder, \"*.jpg\"))\n        \n        if csvs and imgs:\n            try:\n                # PROCESAR IMAGEN\n                img = cv2.imread(imgs[0])\n                if img is None: continue\n                \n                # Resize agresivo para reducir features\n                img = cv2.resize(img, (IMG_SIZE[1], IMG_SIZE[0])) # cv2 usa (Ancho, Alto)\n                \n                # Convertir a Grises \n                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n                \n                # Aplanar: De matriz 2D a vector 1D\n                flat_img = img.flatten() / 255.0  # Normalizar 0-1\n                \n                # PROCESAR SEÑAL\n                sig_df = pd.read_csv(csvs[0])\n                raw_sig = sig_df['II'].values if 'II' in sig_df.columns else sig_df.iloc[:, 0].values\n                \n                # Resample a 5000 puntos\n                if len(raw_sig) != TARGET_LEN:\n                    raw_sig = signal.resample(raw_sig, TARGET_LEN)\n                \n                # Normalizar señal \n                raw_sig = raw_sig / 5.0\n                \n                X.append(flat_img)\n                y.append(raw_sig)\n                \n            except Exception as e:\n                continue\n\n    return np.array(X), np.array(y)\n\n# 1. Cargar Datos\nX, y = load_data_sklearn(BASE_PATH, limit=LIMIT_SAMPLES)\n\nif len(X) > 0:\n    print(f\"\\nDimensiones:\")\n    print(f\"Input X (Imágenes aplanadas): {X.shape}\")\n    print(f\"Target y (Señales): {y.shape}\")\n    \n    # Split\n    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)\n\n    # MODELO 1: RIDGE REGRESSION\n    print(\"\\nEntrenando Ridge Regression (Modelo Lineal)...\")\n    model_ridge = Ridge(alpha=1.0)\n    model_ridge.fit(X_train, y_train)\n    \n    score_ridge = model_ridge.score(X_val, y_val)\n    print(f\"Ridge R2 Score: {score_ridge:.4f}\")\n\n    # MODELO 2: KNN \n    print(\"\\nEntrenando KNN (K-Nearest Neighbors)...\")\n    # Usamos n_neighbors=5 para suavizar\n    model_knn = KNeighborsRegressor(n_neighbors=5, metric='cosine') \n    model_knn.fit(X_train, y_train)\n    \n    score_knn = model_knn.score(X_val, y_val)\n    print(f\"KNN R2 Score: {score_knn:.4f}\")\n\n    # VISUALIZACIÓN DE RESULTADOS\n    print(\"\\nVisualizando predicción de Validación...\")\n    \n    # Predecir sobre el primer ejemplo de validación\n    pred_ridge = model_ridge.predict(X_val[0:1])[0]\n    pred_knn = model_knn.predict(X_val[0:1])[0]\n    real_sig = y_val[0]\n\n    plt.figure(figsize=(15, 6))\n    \n    plt.plot(real_sig, label='Real (Ground Truth)', color='black', alpha=0.5, linewidth=2)\n    plt.plot(pred_ridge, label=f'Ridge (Lineal) - R2: {score_ridge:.2f}', color='red', linestyle='--')\n    plt.plot(pred_knn, label=f'KNN (Vecinos) - R2: {score_knn:.2f}', color='blue', linestyle=':')\n    \n    plt.title(\"Comparación: Modelos Clásicos vs Señal Real\")\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n    plt.show()\n\nelse:\n    print(\"No se cargaron datos.\")\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T05:01:34.025406Z","iopub.execute_input":"2025-12-04T05:01:34.027229Z","iopub.status.idle":"2025-12-04T05:07:10.720302Z","shell.execute_reply.started":"2025-12-04T05:01:34.027180Z","shell.execute_reply":"2025-12-04T05:07:10.718526Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\nif len(X) > 0:\n    print(f\"\\nDimensiones:\")\n    print(f\"Input X (Imágenes aplanadas): {X.shape}\") # (N_samples, 8192 features)\n    print(f\"Target y (Señales): {y.shape}\")           # (N_samples, 5000 features)\n\n    # 2. Split\n    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)\n\n    # --- MODELO 1: RIDGE REGRESSION (Lineal robusto) ---\n    # Es rápido y maneja bien muchas features.\n    print(\"\\nEntrenando Ridge Regression (Modelo Lineal)...\")\n    model_ridge = Ridge(alpha=1.0)\n    model_ridge.fit(X_train, y_train)\n    \n    score_ridge = model_ridge.score(X_val, y_val)\n    print(f\"Ridge R2 Score: {score_ridge:.4f}\")\n\n    # --- MODELO 2: KNN (Vecinos Cercanos) ---\n    # \"Busca la imagen más parecida y copia su señal\"\n    # Advertencia: KNN es lento al predecir si hay muchos datos\n    print(\"\\nEntrenando KNN (K-Nearest Neighbors)...\")\n    # Usamos n_neighbors=5 para suavizar\n    model_knn = KNeighborsRegressor(n_neighbors=5, metric='cosine') \n    model_knn.fit(X_train, y_train)\n    \n    score_knn = model_knn.score(X_val, y_val)\n    print(f\"KNN R2 Score: {score_knn:.4f}\")\n\n    # --- VISUALIZACIÓN DE RESULTADOS ---\n    print(\"\\nVisualizando predicción de Validación...\")\n    \n    # Predecir sobre el primer ejemplo de validación\n    pred_ridge = model_ridge.predict(X_val[0:1])[0]\n    pred_knn = model_knn.predict(X_val[0:1])[0]\n    real_sig = y_val[0]\n\n    plt.figure(figsize=(15, 6))\n    \n    plt.plot(real_sig, label='Real (Ground Truth)', color='black', alpha=0.5, linewidth=2)\n    plt.plot(pred_ridge -.2, label=f'Ridge (Lineal) - R2: {score_ridge:.2f}', color='red')\n    plt.plot(pred_knn-.4, label=f'KNN (Vecinos) - R2: {score_knn:.2f}', color='blue')\n    \n    plt.title(\"Comparación: Modelos Clásicos vs Señal Real\")\n    plt.legend()\n    plt.grid(True, alpha=0.3)\n    plt.show()\n\nelse:\n    print(\"No se cargaron datos.\")\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-04T05:15:43.195689Z","iopub.execute_input":"2025-12-04T05:15:43.196138Z","iopub.status.idle":"2025-12-04T05:15:45.834660Z","shell.execute_reply.started":"2025-12-04T05:15:43.196108Z","shell.execute_reply":"2025-12-04T05:15:45.833395Z"}},"outputs":[],"execution_count":null}]}