{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "m3-s12-01",
   "metadata": {},
   "source": "# M3 · Sesión 12 — Evaluación del módulo y defensa\n\n**Modelos de Aprendizaje Automático** — 6 ECTS  \nMáster en Inteligencia Artificial, Automatización y Agentes Digitales · Next Educación · Próxima edición\n\n---\n\n## Práctica de la sesión: Prueba práctica y defensa\n\nEvaluación sumativa del módulo.\n\n| | |\n|---|---|\n| **Stack** | Entorno del máster |\n| **Entregable** | Prueba resuelta y defensa realizada. |\n\n### Objetivos que se evalúan\n\n1. Demostrar dominio del ciclo de modelado.\n2. Interpretar y comunicar resultados.\n3. Cerrar con el modelo que se llevará al Módulo 6.\n\n> **Cómo se corrige.** No se evalúa solo el resultado: se evalúan la justificación escrita de cada decisión, la honestidad de la validación y que el cuaderno se ejecute de principio a fin en un equipo distinto al tuyo."
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-02",
   "metadata": {},
   "source": "---\n## 0. Entorno\n\nEjecuta esta celda antes que ninguna otra. Fija la semilla y el estilo de figuras que se usa en todo el máster, de modo que los resultados sean comparables entre alumnos."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-03",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "import platform\nimport numpy as np\nimport pandas as pd\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nRANDOM_STATE = 42\nrng = np.random.default_rng(RANDOM_STATE)\nnp.random.seed(RANDOM_STATE)\n\npd.set_option(\"display.width\", 118)\npd.set_option(\"display.max_columns\", 40)\nplt.rcParams.update({\n    \"figure.figsize\": (9, 4.2), \"figure.dpi\": 110, \"axes.grid\": True,\n    \"grid.alpha\": .25, \"axes.spines.top\": False, \"axes.spines.right\": False,\n    \"font.size\": 10, \"axes.titlesize\": 12, \"axes.titleweight\": \"bold\",\n    \"axes.titlecolor\": \"#12395e\", \"axes.labelcolor\": \"#34495e\",\n})\nAZUL, OSCURO, ROJO = \"#2a76dd\", \"#12395e\", \"#e74c3c\"\n\nprint(\"Python\", platform.python_version(), \"| pandas\", pd.__version__,\n      \"| numpy\", np.__version__, \"| matplotlib\", matplotlib.__version__)"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-04",
   "metadata": {},
   "source": "---\n## 1. Datos de la sesión: cartera de clientes B2B\n\nEl conjunto es sintético y determinista: se regenera igual en cualquier equipo, de modo que los resultados del cuaderno son comparables entre alumnos. Sustitúyelo por el dataset de tu propio proyecto cuando lo tengas."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-05",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "N = 4000\nantiguedad = rng.integers(1, 72, N)\nconsumo = np.round(np.exp(rng.normal(4.2, .55, N)), 2)\nincidencias = rng.poisson(1.4, N)\nsegmento = rng.choice([\"Micro\", \"Pyme\", \"Mediana\", \"Corporativa\"], N, p=[.42, .33, .17, .08])\ndescuento = np.clip(rng.normal(.12, .07, N), 0, .45).round(3)\n\nlogit = (-1.15 - .022 * antiguedad + .0035 * consumo + .38 * incidencias\n         + 1.7 * descuento + rng.normal(0, .55, N))\nbaja = (1 / (1 + np.exp(-logit)) > rng.random(N)).astype(int)\n\ndf = pd.DataFrame({\"antiguedad_meses\": antiguedad, \"consumo_mensual\": consumo,\n                   \"incidencias_12m\": incidencias, \"segmento\": segmento,\n                   \"descuento_aplicado\": descuento, \"baja\": baja})\ndf.loc[rng.random(N) < .04, \"consumo_mensual\"] = np.nan   # nulos reales\ndf.head()"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-06",
   "metadata": {},
   "source": "### 1.1 Perfilado\n\nAntes de tocar nada, mirar el dato. Este bloque es el mínimo exigible en cualquier entrega del módulo."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-07",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "resumen = pd.DataFrame({\n    \"tipo\": df.dtypes.astype(str),\n    \"nulos\": df.isna().sum(),\n    \"nulos_%\": (df.isna().mean() * 100).round(2),\n    \"distintos\": df.nunique(),\n})\nprint(f\"Filas: {len(df):,} | Columnas: {df.shape[1]} | Tasa de baja: {df['baja'].mean():.1%}\")\nresumen"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-08",
   "metadata": {},
   "source": "### 1.2 Lectura visual\n\nUna figura que responde a una pregunta concreta, no un gráfico decorativo."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-09",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n\ntasa = df.groupby(\"segmento\")[\"baja\"].agg([\"mean\", \"size\"]).sort_values(\"mean\")\nax[0].barh(tasa.index, tasa[\"mean\"], color=AZUL)\nax[0].set_title(\"Tasa de baja por segmento\")\nax[0].set_xlabel(\"proporción de bajas\")\nfor y, (v, n) in enumerate(tasa.values):\n    ax[0].text(v + .004, y, f\"{v:.1%} (n={int(n)})\", va=\"center\", fontsize=9, color=OSCURO)\n\nfor etiqueta, color in [(0, AZUL), (1, ROJO)]:\n    sub = df.loc[df[\"baja\"] == etiqueta, \"consumo_mensual\"].dropna()\n    ax[1].hist(sub, bins=40, alpha=.6, color=color, label=\"baja\" if etiqueta else \"permanece\")\nax[1].set_title(\"Consumo mensual según resultado\")\nax[1].set_xlabel(\"consumo mensual (€)\"); ax[1].legend()\n\nfig.suptitle(\"Perfilado inicial del conjunto\", y=1.02, color=OSCURO, fontweight=\"bold\")\nplt.tight_layout(); plt.show()"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-10",
   "metadata": {},
   "source": "---\n## 2. Ejemplo trabajado: una comparación honesta\n\nAntes de pasar a los pasos de la práctica, un ejemplo completo del tipo de razonamiento que se evalúa: una diferencia observada acompañada de su incertidumbre, no un número suelto."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-11",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "def bootstrap_diferencia(a, b, n=2000):\n    \"\"\"Intervalo del 95 % para la diferencia de medias por remuestreo.\"\"\"\n    dif = [rng.choice(a, len(a), replace=True).mean() - rng.choice(b, len(b), replace=True).mean()\n           for _ in range(n)]\n    return np.mean(dif), np.percentile(dif, [2.5, 97.5])\n\nalta = df.loc[df[\"incidencias_12m\"] >= 3, \"baja\"].to_numpy()\nbaja_inc = df.loc[df[\"incidencias_12m\"] == 0, \"baja\"].to_numpy()\nd, (lo, hi) = bootstrap_diferencia(alta, baja_inc)\n\nprint(f\"Tasa de baja con 3+ incidencias : {alta.mean():.1%}  (n={len(alta)})\")\nprint(f\"Tasa de baja sin incidencias    : {baja_inc.mean():.1%}  (n={len(baja_inc)})\")\nprint(f\"Diferencia                      : {d:+.1%}  IC95% [{lo:+.1%}, {hi:+.1%}]\")\nprint(\"\\nEl intervalo no contiene el cero: la diferencia es consistente con los datos disponibles.\")"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-12",
   "metadata": {},
   "source": "---\n## 3. Tu turno: Prueba práctica y defensa\n\nA partir de aquí trabajas tú. Cada paso tiene una celda de código y una celda de justificación: **las dos se corrigen**. Si adaptas la práctica a tu propio proyecto, indícalo en el primer paso."
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-13",
   "metadata": {},
   "source": "### 3.1 Resolver la prueba."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-14",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "# Paso 1: Resolver la prueba.\n# TODO\n"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-15",
   "metadata": {},
   "source": "> **Justificación del paso 1.** _(dos o tres frases)_ Qué has decidido, con qué criterio, y qué alternativa has descartado y por qué."
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-16",
   "metadata": {},
   "source": "### 3.2 Defender el modelo."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-17",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "# Paso 2: Defender el modelo.\n# TODO\n"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-18",
   "metadata": {},
   "source": "> **Justificación del paso 2.** _(dos o tres frases)_ Qué has decidido, con qué criterio, y qué alternativa has descartado y por qué."
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-19",
   "metadata": {},
   "source": "### 3.3 Recibir devolución."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-20",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "# Paso 3: Recibir devolución.\n# TODO\n"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-21",
   "metadata": {},
   "source": "> **Justificación del paso 3.** _(dos o tres frases)_ Qué has decidido, con qué criterio, y qué alternativa has descartado y por qué."
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-22",
   "metadata": {},
   "source": "---\n## 4. Cierre y entrega\n\n**Entregable:** Prueba resuelta y defensa realizada.\n\nAntes de subir el cuaderno al Campus Virtual, repasa la lista. La celda de abajo verifica la parte que puede verificarse sola; el resto lo marcas tú al completarlo."
  },
  {
   "cell_type": "code",
   "id": "m3-s12-23",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "comprobaciones = {\n    \"Semilla aleatoria fijada\": \"RANDOM_STATE\" in dir(),\n    \"Sin rutas absolutas del equipo\": True,\n    \"Justificacion escrita en todos los pasos\": False,\n    \"Ejecutado de arriba abajo sin errores\": False,\n    \"Sin datos personales ni credenciales\": True,\n}\nfor k, v in comprobaciones.items():\n    print((\"  OK   \" if v else \" FALTA \") + \"| \" + k)\n\nprint()\nprint(\"Listo para entregar.\" if all(comprobaciones.values())\n      else \"Completa lo que falta antes de entregar.\")"
  },
  {
   "cell_type": "markdown",
   "id": "m3-s12-24",
   "metadata": {},
   "source": "---\n*M3 · Sesión 12 — Evaluación del módulo y defensa · Máster en Inteligencia Artificial, Automatización y Agentes Digitales · Next Educación*  \n*Material docente elaborado por Sami Halawa Ribas. Pendiente de validación por la Dirección del programa.*"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11",
   "mimetype": "text/x-python",
   "file_extension": ".py",
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}