{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f0017fcf",
   "metadata": {},
   "source": [
    "# PISA 2025, España: distracción digital, uso de chatbots de IA y rendimiento en Ciencias\n",
    "\n",
    "Este cuaderno reproduce **de principio a fin, con el fichero oficial de la OCDE**, el análisis\n",
    "estadístico detrás del artículo:\n",
    "\n",
    "> [«PISA 2025: IA y distracción digital en España»](https://labs.tever.es/blog/pisa-2025-ia-y-distraccion-en-espana) — labs.tever.es\n",
    "\n",
    "**Autoría:** Tever Díaz García · **Fecha de este cuaderno:** 2026-09-19\n",
    "\n",
    "## Qué es este estudio\n",
    "\n",
    "PISA (Programme for International Student Assessment) es la evaluación internacional de la OCDE\n",
    "que mide cada tres años el desempeño de estudiantes de 15 años en Ciencias, Matemáticas y Lectura.\n",
    "El ciclo **PISA 2025** introduce por primera vez un bloque de preguntas sobre el uso de chatbots de\n",
    "inteligencia artificial en tareas escolares (ítems `ST438Q01DA`–`ST438Q04DA`), además del ítem ya\n",
    "tradicional de distracción por recursos digitales en clase (`ST097Q06DA`; el ítem original en\n",
    "inglés dice *\"distracted by using digital resources (e.g. smartphones, websites, apps)\"* -- mide\n",
    "recursos digitales en general, con los smartphones solo como ejemplo).\n",
    "\n",
    "Este cuaderno cruza esas 5 variables de autoinforme del alumnado español con su rendimiento en\n",
    "Ciencias (los 10 *plausible values* `PV1SCIE`–`PV10SCIE`), usando la metodología estadística oficial\n",
    "de PISA para la ponderación muestral, la combinación de *plausible values* por la regla de Rubin y\n",
    "el error estándar por réplicas BRR-Fay -- y, además, un modelo de regresión ajustado por ESCS que\n",
    "es una decisión analítica propia de este cuaderno, no un procedimiento prescrito por la OCDE\n",
    "(precisión detallada en la sección de método más abajo).\n",
    "\n",
    "## Fuente de datos\n",
    "\n",
    "**Dataset:** OECD, *PISA 2025 Database — Student Questionnaire Data File (PUF)*, fichero\n",
    "`CY09_MS_STU_PUF.sav` (formato SPSS).\n",
    "\n",
    "- **URL de descarga directa** (la usada en este análisis, verificada en esta misma sesión):\n",
    "  `https://webfs.oecd.org/pisa2022/2025/CY09_MS_STU_PUF.zip`\n",
    "- **Página oficial de la OCDE** donde este fichero está documentado y enlazado (nota: la OCDE\n",
    "  reutiliza el directorio histórico `pisa2022` de su CDN de ficheros también para servir el ciclo\n",
    "  2025 — el nombre de la carpeta *no* identifica el ciclo real del dato; el ciclo se confirma dentro\n",
    "  del propio fichero, ver más abajo):\n",
    "  `https://www.oecd.org/en/data/datasets/pisa-2022-database.html`\n",
    "\n",
    "El fichero pesa ≈993 MB comprimido. **Este cuaderno no lo descarga automáticamente** — instrucciones\n",
    "de descarga manual en la siguiente celda."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ebb6341",
   "metadata": {},
   "source": [
    "## 1. Dependencias y descarga del dataset\n",
    "\n",
    "### Instalar dependencias\n",
    "\n",
    "Este análisis usa `pyreadstat` (lectura de ficheros `.sav` de SPSS), `pandas`, `numpy` y\n",
    "`matplotlib`. Instálalas en un entorno virtual:\n",
    "\n",
    "```bash\n",
    "python3 -m venv venv\n",
    "source venv/bin/activate\n",
    "pip install pyreadstat pandas numpy matplotlib\n",
    "```\n",
    "\n",
    "### Descargar el fichero oficial\n",
    "\n",
    "1. Descarga el zip desde la URL oficial:\n",
    "   `https://webfs.oecd.org/pisa2022/2025/CY09_MS_STU_PUF.zip`\n",
    "   (puedes usar `curl -O` o el navegador; el servidor de la OCDE puede exigir un desafío\n",
    "   anti-bot de Cloudflare en descargas automatizadas — si `curl` recibe un 403, descárgalo\n",
    "   manualmente desde el navegador).\n",
    "2. Descomprímelo. Dentro encontrarás `CY09_MS_STU_PUF.sav`.\n",
    "3. Coloca el `.sav` en la ruta `stu_extracted/CY09_MS_STU_PUF.sav` relativa a este cuaderno\n",
    "   (o ajusta `SAV_PATH` en la celda de extracción más abajo).\n",
    "\n",
    "El fichero completo (todos los países) pesa ~2 GB descomprimido; el subset de España que\n",
    "extraemos a continuación pesa ~24 MB y es el único artefacto intermedio que necesitas conservar\n",
    "para reproducir el resto del análisis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c5903167",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:51:54.117066Z",
     "iopub.status.busy": "2026-09-19T09:51:54.116943Z",
     "iopub.status.idle": "2026-09-19T09:51:54.503422Z",
     "shell.execute_reply": "2026-09-19T09:51:54.502989Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pyreadstat: 1.3.6\n",
      "pandas: 3.0.6\n",
      "numpy: 2.5.3\n"
     ]
    }
   ],
   "source": [
    "import pyreadstat\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib\n",
    "matplotlib.use(\"Agg\")  # cambia a \"inline\" si ejecutas esto en Jupyter interactivo\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "print(\"pyreadstat:\", pyreadstat.__version__)\n",
    "print(\"pandas:\", pd.__version__)\n",
    "print(\"numpy:\", np.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1013b30f",
   "metadata": {},
   "source": [
    "## 2. Verificación del ciclo del dataset (honestidad del proceso)\n",
    "\n",
    "Antes de analizar nada: **¿es este fichero realmente PISA 2025, o es PISA 2022 mal etiquetado por\n",
    "la ruta de descarga?** La URL contiene el segmento `/pisa2022/`, lo que en una primera lectura\n",
    "apresurada llevó a un etiquetado erróneo del dataset en una versión temprana de este análisis.\n",
    "\n",
    "La forma correcta de verificarlo no es confiar en el nombre de la carpeta del servidor, sino\n",
    "comprobar el contenido del propio fichero:\n",
    "\n",
    "- La variable de ciclo `CYC` debe valer `\"25MS\"` (PISA 2025, *Main Survey*), no `\"08MS\"` (2022).\n",
    "- Los ítems `ST438Q01DA`–`ST438Q04DA` (uso de chatbots de IA) solo existen en el codebook de 2025 —\n",
    "  no estaban en el cuestionario de 2022.\n",
    "- El fichero complementario `CY09_MS_LDW_PUF.zip` (*Learning in the Digital World*, LDW) es exclusivo del ciclo\n",
    "  2025 y no tiene equivalente en 2022.\n",
    "\n",
    "Las tres comprobaciones coinciden: el fichero servido en `webfs.oecd.org/pisa2022/2025/` es, en\n",
    "efecto, el PUF de **PISA 2025** — la OCDE simplemente reutiliza el directorio histórico `pisa2022`\n",
    "de su CDN de ficheros para publicar también el ciclo siguiente; el nombre de la ruta no es el ciclo\n",
    "del dato."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "81195f0d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:51:54.504498Z",
     "iopub.status.busy": "2026-09-19T09:51:54.504367Z",
     "iopub.status.idle": "2026-09-19T09:52:00.031959Z",
     "shell.execute_reply": "2026-09-19T09:52:00.031464Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Valor(es) único(s) de CYC en el fichero: <StringArray>\n",
      "['25MS']\n",
      "Length: 1, dtype: str\n",
      "Confirmado: CYC = '25MS' -> PISA 2025, Main Survey.\n",
      "Ítems de uso de IA presentes en el fichero: ['ST438Q01DA', 'ST438Q02DA', 'ST438Q03DA', 'ST438Q04DA']\n"
     ]
    }
   ],
   "source": [
    "SAV_PATH = \"stu_extracted/CY09_MS_STU_PUF.sav\"\n",
    "\n",
    "# Lectura solo de las columnas CYC/CNT, sin cargar el fichero completo en memoria\n",
    "# (fix: pyreadstat.read_sav devuelve (dataframe, metadata_container); la variable\n",
    "# de ciclo se lee del dataframe, no del metadata_container, que no es subscriptable)\n",
    "df_check, _ = pyreadstat.read_sav(SAV_PATH, usecols=[\"CYC\", \"CNT\"], metadataonly=False)\n",
    "\n",
    "ciclo_unico = df_check[\"CYC\"].unique()\n",
    "print(\"Valor(es) único(s) de CYC en el fichero:\", ciclo_unico)\n",
    "assert list(ciclo_unico) == [\"25MS\"], (\n",
    "    f\"El fichero NO es PISA 2025 Main Survey (CYC='25MS'); se encontró {ciclo_unico}\"\n",
    ")\n",
    "print(\"Confirmado: CYC = '25MS' -> PISA 2025, Main Survey.\")\n",
    "\n",
    "# Confirmación adicional: los ítems de IA existen en el diccionario de variables del fichero\n",
    "_, meta_full = pyreadstat.read_sav(SAV_PATH, metadataonly=True)\n",
    "items_ia = [c for c in meta_full.column_names if c.startswith(\"ST438Q\")]\n",
    "print(\"Ítems de uso de IA presentes en el fichero:\", items_ia)\n",
    "assert len(items_ia) >= 4, \"No se encontraron los ítems ST438Q0X de uso de IA -- ¿fichero incorrecto?\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "612823cd",
   "metadata": {},
   "source": [
    "## 3. Extracción del subset de España\n",
    "\n",
    "El fichero PUF completo incluye a todos los países participantes (cientos de miles de\n",
    "estudiantes). Para este análisis solo necesitamos las filas con `CNT == \"ESP\"` y un conjunto\n",
    "acotado de columnas:\n",
    "\n",
    "- **Identificación:** `CNT`, `CNTSCHID`, `CNTSTUID`\n",
    "- **Variable de distracción digital:** `ST097Q06DA`\n",
    "- **Variables de uso de IA** (4 tareas): `ST438Q01DA`–`ST438Q04DA`\n",
    "- **Índice socioeconómico-cultural:** `ESCS`\n",
    "- **Peso muestral final:** `W_FSTUWT`\n",
    "- **80 réplicas de remuestreo BRR-Fay:** `W_FSTURWT1`–`W_FSTURWT80` (necesarias para el error\n",
    "  estándar correcto — ver sección de método)\n",
    "- **10 *plausible values* de Ciencias:** `PV1SCIE`–`PV10SCIE`\n",
    "\n",
    "Todos los nombres de columna se confirmaron literalmente contra el diccionario de variables del\n",
    "propio fichero (`pyreadstat` en modo `metadataonly`) antes de usarlos — no se asume ninguna\n",
    "variable por convención de otros ciclos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "74be7b3e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:00.033214Z",
     "iopub.status.busy": "2026-09-19T09:52:00.033081Z",
     "iopub.status.idle": "2026-09-19T09:52:10.435504Z",
     "shell.execute_reply": "2026-09-19T09:52:10.435103Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Leyendo el .sav filtrando por CNT == 'ESP' (recorre el fichero completo una vez)...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filas España: (29966, 100)\n",
      "Subset de España guardado en: esp_subset_full.pkl\n"
     ]
    }
   ],
   "source": [
    "OUT_PATH = \"esp_subset_full.pkl\"\n",
    "\n",
    "pv_cols = [f\"PV{i}SCIE\" for i in range(1, 11)]\n",
    "rep_cols = [f\"W_FSTURWT{i}\" for i in range(1, 81)]\n",
    "\n",
    "usecols = (\n",
    "    [\"CNT\", \"CNTSCHID\", \"CNTSTUID\",\n",
    "     \"ST097Q06DA\", \"ST438Q01DA\", \"ST438Q02DA\", \"ST438Q03DA\", \"ST438Q04DA\",\n",
    "     \"ESCS\", \"W_FSTUWT\"]\n",
    "    + rep_cols\n",
    "    + pv_cols\n",
    ")\n",
    "\n",
    "print(\"Leyendo el .sav filtrando por CNT == 'ESP' (recorre el fichero completo una vez)...\")\n",
    "df, meta = pyreadstat.read_sav(SAV_PATH, usecols=usecols)\n",
    "\n",
    "df_esp = df[df[\"CNT\"] == \"ESP\"].reset_index(drop=True)\n",
    "print(\"Filas España:\", df_esp.shape)\n",
    "\n",
    "missing_expected = [c for c in usecols if c not in df_esp.columns]\n",
    "assert not missing_expected, f\"Columnas ausentes tras lectura: {missing_expected}\"\n",
    "\n",
    "df_esp.to_pickle(OUT_PATH)\n",
    "print(\"Subset de España guardado en:\", OUT_PATH)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f20eaaa",
   "metadata": {},
   "source": [
    "## 4. Método estadístico\n",
    "\n",
    "PISA no permite calcular una media de rendimiento como si fuera un examen convencional: hay que\n",
    "respetar tres particularidades de su diseño, aplicadas **igual para las 5 variables** analizadas\n",
    "(mismo método fijado antes de ver los resultados, para que la comparación entre variables sea\n",
    "válida):\n",
    "\n",
    "**1. Recodificación.** Se excluyen los valores perdidos (`NaN`, ya codificados como tales tras la\n",
    "lectura con `pyreadstat`). Se verificó que en el subset de España los únicos valores presentes son\n",
    "los códigos de escala válidos — 1 a 4 en `ST097Q06DA`, 1 a 5 en `ST438Q01DA`–`Q04DA` — sin códigos\n",
    "residuales de no-respuesta (95-99/5-9).\n",
    "\n",
    "**2. Combinación de los 10 *plausible values* por la regla de Rubin.** El rendimiento en Ciencias\n",
    "de PISA no es una única puntuación por estudiante: son 10 valores plausibles (`PV1SCIE`…`PV10SCIE`)\n",
    "que representan la incertidumbre del modelo de medición. El procedimiento estándar de PISA/IEA es:\n",
    "\n",
    "1. Calcular la media ponderada (peso `W_FSTUWT`) de rendimiento para cada una de las 10 PV.\n",
    "2. **Media final** = promedio de esas 10 medias.\n",
    "3. **Varianza de muestreo** (`var_intra`) = promedio de las 10 varianzas de muestreo, cada una\n",
    "   estimada por remuestreo BRR-Fay (ver punto 3).\n",
    "4. **Varianza de imputación** (`var_inter`) = varianza entre las 10 medias por PV.\n",
    "5. **Varianza total** = `var_intra + (1 + 1/10) · var_inter`.\n",
    "6. **Error estándar** = raíz cuadrada de la varianza total.\n",
    "\n",
    "**3. Error estándar por réplicas BRR-Fay.** El diseño muestral de PISA (por conglomerados, con\n",
    "escuelas y aulas) hace que la varianza \"ingenua\" subestime el error real. PISA calcula el error\n",
    "estándar mediante *Balanced Repeated Replication* con factor de Fay `k = 0.5`, usando 80 pesos de\n",
    "réplica (`W_FSTURWT1`…`W_FSTURWT80`):\n",
    "\n",
    "$$\\text{var}_{BRR} = \\frac{1}{80 \\cdot (1-k)^2} \\sum_{r=1}^{80} (\\hat\\theta_r - \\hat\\theta_{full})^2$$\n",
    "\n",
    "donde $\\hat\\theta_{full}$ es la estimación con el peso final `W_FSTUWT` y $\\hat\\theta_r$ es la\n",
    "misma estimación recalculada con cada uno de los 80 pesos de réplica.\n",
    "\n",
    "**4. Ajuste por nivel socioeconómico (ESCS).** Además de la media cruda por categoría, se ajusta\n",
    "por el índice ESCS mediante una regresión lineal ponderada:\n",
    "\n",
    "$$\\text{Ciencias}_{PVi} \\sim \\text{categoría}(\\text{dummies}) + \\text{ESCS}$$\n",
    "\n",
    "Se reporta la media ajustada por categoría (predicción al ESCS medio **muestral ponderado**\n",
    "con `W_FSTUWT`, no la media aritmética simple), combinando los coeficientes igual que en el\n",
    "punto 2 (sobre las 10 PV y las 80 réplicas). Las filas con `ESCS` perdido se excluyen **solo**\n",
    "del modelo ajustado, no del crudo — es un ajuste de muestra, documentado como tal, no de método.\n",
    "\n",
    "**Precisión metodológica importante:** los puntos 2 y 3 (ponderación muestral, combinación de\n",
    "*plausible values* por la regla de Rubin, error estándar BRR-Fay) sí son la metodología\n",
    "estadística oficial documentada por la OCDE para PISA. El modelo de regresión\n",
    "`Ciencias ~ categoría + ESCS` de este punto 4, en cambio, es una **decisión analítica propia**\n",
    "de este cuaderno para obtener \"medias ajustadas\" comparables — no un modelo prescrito por la\n",
    "OCDE — que se apoya en ese mismo armazón oficial (pesos, PVs, réplicas) para su combinación e\n",
    "inferencia.\n",
    "\n",
    "**5. Intervalo de confianza.** Aproximación normal al 95%: media ± 1.96 · SE."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b0314e9c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:10.436571Z",
     "iopub.status.busy": "2026-09-19T09:52:10.436483Z",
     "iopub.status.idle": "2026-09-19T09:52:10.474083Z",
     "shell.execute_reply": "2026-09-19T09:52:10.473658Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Subset cargado: (29966, 100)\n"
     ]
    }
   ],
   "source": [
    "IN_PATH = \"esp_subset_full.pkl\"\n",
    "FAY_K = 0.5\n",
    "\n",
    "df = pd.read_pickle(IN_PATH)\n",
    "\n",
    "PV_COLS = [f\"PV{i}SCIE\" for i in range(1, 11)]\n",
    "REP_COLS = [f\"W_FSTURWT{i}\" for i in range(1, 81)]\n",
    "WEIGHT = \"W_FSTUWT\"\n",
    "\n",
    "VARS = [\"ST097Q06DA\", \"ST438Q01DA\", \"ST438Q02DA\", \"ST438Q03DA\", \"ST438Q04DA\"]\n",
    "\n",
    "LABELS = {\n",
    "    \"ST097Q06DA\": \"Distracción por recursos digitales (clase de ciencias)\",\n",
    "    \"ST438Q01DA\": \"IA: resumir un texto\",\n",
    "    \"ST438Q02DA\": \"IA: investigación preliminar sobre un tema\",\n",
    "    \"ST438Q03DA\": \"IA: redactar textos para tareas escritas\",\n",
    "    \"ST438Q04DA\": \"IA: ayudarme a aprender\",\n",
    "}\n",
    "\n",
    "CAT_LABELS = {\n",
    "    \"ST097Q06DA\": {1: \"Cada clase\", 2: \"La mayoría de clases\", 3: \"Algunas clases\", 4: \"Nunca/casi nunca\"},\n",
    "    \"ST438Q01DA\": {1: \"Nunca/casi nunca\", 2: \"1-2 veces/año\", 3: \"1-2 veces/mes\", 4: \"1-2 veces/semana\", 5: \"A diario/casi a diario\"},\n",
    "}\n",
    "for v in [\"ST438Q02DA\", \"ST438Q03DA\", \"ST438Q04DA\"]:\n",
    "    CAT_LABELS[v] = CAT_LABELS[\"ST438Q01DA\"]\n",
    "\n",
    "print(\"Subset cargado:\", df.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f3d6462f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:10.475380Z",
     "iopub.status.busy": "2026-09-19T09:52:10.475277Z",
     "iopub.status.idle": "2026-09-19T09:52:10.481396Z",
     "shell.execute_reply": "2026-09-19T09:52:10.481059Z"
    }
   },
   "outputs": [],
   "source": [
    "def weighted_mean(values, weights):\n",
    "    mask = ~np.isnan(values)\n",
    "    if mask.sum() == 0:\n",
    "        return np.nan\n",
    "    return np.average(values[mask], weights=weights[mask])\n",
    "\n",
    "\n",
    "def brr_fay_variance(sub, pv_col, weight_col, rep_cols, estimator):\n",
    "    \"\"\"Varianza BRR-Fay para un estimador (función de (pv_values, weight_array) -> float).\"\"\"\n",
    "    full_est = estimator(sub[pv_col].values, sub[weight_col].values)\n",
    "    sq_diffs = []\n",
    "    for r in rep_cols:\n",
    "        rep_est = estimator(sub[pv_col].values, sub[r].values)\n",
    "        sq_diffs.append((rep_est - full_est) ** 2)\n",
    "    var = (1.0 / (80 * (1 - FAY_K) ** 2)) * np.sum(sq_diffs)\n",
    "    return full_est, var\n",
    "\n",
    "\n",
    "def rubin_combine(pv_means, pv_vars):\n",
    "    \"\"\"Combina 10 estimaciones (una por plausible value) siguiendo la regla de Rubin.\"\"\"\n",
    "    pv_means = np.array(pv_means)\n",
    "    pv_vars = np.array(pv_vars)\n",
    "    m = len(pv_means)\n",
    "    final_mean = pv_means.mean()\n",
    "    var_intra = pv_vars.mean()\n",
    "    var_inter = pv_means.var(ddof=1)\n",
    "    var_total = var_intra + (1 + 1.0 / m) * var_inter\n",
    "    se = np.sqrt(var_total)\n",
    "    return final_mean, se\n",
    "\n",
    "\n",
    "def category_means_raw(data, var_col, weight_col, rep_cols, pv_cols):\n",
    "    \"\"\"Media de Ciencias por categoría de var_col (Rubin + BRR-Fay). Devuelve\n",
    "    dict categoría -> (media, SE, n).\"\"\"\n",
    "    results = {}\n",
    "    cats = sorted(data[var_col].dropna().unique())\n",
    "    for cat in cats:\n",
    "        sub = data[data[var_col] == cat]\n",
    "        n = len(sub)\n",
    "        pv_means, pv_vars = [], []\n",
    "        for pv in pv_cols:\n",
    "            m, v = brr_fay_variance(sub, pv, weight_col, rep_cols, weighted_mean)\n",
    "            pv_means.append(m)\n",
    "            pv_vars.append(v)\n",
    "        mean, se = rubin_combine(pv_means, pv_vars)\n",
    "        results[cat] = (mean, se, n)\n",
    "    return results\n",
    "\n",
    "\n",
    "def weighted_ols_adjusted_means(data, var_col, escs_col, weight_col, rep_cols, pv_cols):\n",
    "    \"\"\"Regresión ponderada Ciencias ~ C(var_col) + ESCS, combinando sobre las\n",
    "    10 PV con Rubin y BRR-Fay sobre las réplicas. Devuelve medias ajustadas\n",
    "    (predicción a ESCS = media MUESTRAL PONDERADA con W_FSTUWT, no la media\n",
    "    aritmética simple) por categoría.\"\"\"\n",
    "    sub = data.dropna(subset=[var_col, escs_col]).copy()\n",
    "    cats = sorted(sub[var_col].dropna().unique())\n",
    "    # BUG FIX (auditoría externa): antes se usaba sub[escs_col].mean(), la\n",
    "    # media aritmética no ponderada de ESCS -- inconsistente con que el resto\n",
    "    # del análisis pondera todo con W_FSTUWT para representar a la población.\n",
    "    # Corregido a media ponderada:\n",
    "    escs_mean = np.average(sub[escs_col].values, weights=sub[weight_col].values)\n",
    "\n",
    "    X_cat = pd.get_dummies(sub[var_col], prefix=\"cat\").astype(float)\n",
    "    dummy_cols = [f\"cat_{c}\" for c in cats]\n",
    "    X_cat = X_cat[dummy_cols]\n",
    "    X = np.column_stack([X_cat.values, sub[escs_col].values])\n",
    "\n",
    "    def fit_adjusted_means(pv_values, weight_values):\n",
    "        w = weight_values\n",
    "        y = pv_values\n",
    "        Xw = X * np.sqrt(w)[:, None]\n",
    "        yw = y * np.sqrt(w)\n",
    "        coef, *_ = np.linalg.lstsq(Xw, yw, rcond=None)\n",
    "        adj = coef[:len(cats)] + coef[-1] * escs_mean\n",
    "        return adj\n",
    "\n",
    "    pv_adj_means = []\n",
    "    for pv in pv_cols:\n",
    "        full_adj = fit_adjusted_means(sub[pv].values, sub[weight_col].values)\n",
    "        pv_adj_means.append(full_adj)\n",
    "    pv_adj_means = np.array(pv_adj_means)\n",
    "\n",
    "    pv_rep_vars = np.zeros((10, len(cats)))\n",
    "    for i, pv in enumerate(pv_cols):\n",
    "        full_adj = pv_adj_means[i]\n",
    "        sq_diffs = np.zeros(len(cats))\n",
    "        for r in rep_cols:\n",
    "            rep_adj = fit_adjusted_means(sub[pv].values, sub[r].values)\n",
    "            sq_diffs += (rep_adj - full_adj) ** 2\n",
    "        pv_rep_vars[i] = (1.0 / (80 * (1 - FAY_K) ** 2)) * sq_diffs\n",
    "\n",
    "    results = {}\n",
    "    for j, cat in enumerate(cats):\n",
    "        mean, se = rubin_combine(pv_adj_means[:, j], pv_rep_vars[:, j])\n",
    "        results[cat] = (mean, se, int((sub[var_col] == cat).sum()))\n",
    "    return results, len(sub)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f7761b7",
   "metadata": {},
   "source": [
    "### Ejecutar el análisis para las 5 variables\n",
    "\n",
    "Se calcula, para cada una de las 5 variables, la media cruda por categoría y la media ajustada\n",
    "por ESCS, ambas con su error estándar BRR-Fay e IC 95%. También se comprueba si la relación es\n",
    "monótona (a más uso/distracción, rendimiento sistemáticamente menor o mayor) — un chequeo de\n",
    "consistencia interna, no una prueba de causalidad."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b310ecf8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:10.482313Z",
     "iopub.status.busy": "2026-09-19T09:52:10.482206Z",
     "iopub.status.idle": "2026-09-19T09:52:11.198307Z",
     "shell.execute_reply": "2026-09-19T09:52:11.197653Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "======================================================================\n",
      "RESULTADOS CRUDOS (media ponderada de Ciencias por categoría)\n",
      "======================================================================\n",
      "\n",
      "--- ST097Q06DA: Distracción por recursos digitales (clase de ciencias) ---\n",
      "  1 (Cada clase                  ) n= 2860  media= 457.3  SE= 4.29  IC95%=[ 448.9,  465.7]\n",
      "  2 (La mayoría de clases        ) n= 4977  media= 477.5  SE= 3.02  IC95%=[ 471.6,  483.4]\n",
      "  3 (Algunas clases              ) n= 9093  media= 483.0  SE= 1.88  IC95%=[ 479.4,  486.7]\n",
      "  4 (Nunca/casi nunca            ) n=10146  media= 489.5  SE= 2.00  IC95%=[ 485.6,  493.4]\n",
      "\n",
      "--- ST438Q01DA: IA: resumir un texto ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Nunca/casi nunca            ) n= 7235  media= 497.3  SE= 2.47  IC95%=[ 492.5,  502.2]\n",
      "  2 (1-2 veces/año               ) n= 3365  media= 476.5  SE= 2.73  IC95%=[ 471.1,  481.8]\n",
      "  3 (1-2 veces/mes               ) n= 5649  media= 488.1  SE= 2.34  IC95%=[ 483.6,  492.7]\n",
      "  4 (1-2 veces/semana            ) n= 6441  media= 481.6  SE= 2.02  IC95%=[ 477.6,  485.5]\n",
      "  5 (A diario/casi a diario      ) n= 4090  media= 470.5  SE= 2.92  IC95%=[ 464.8,  476.2]\n",
      "\n",
      "--- ST438Q02DA: IA: investigación preliminar sobre un tema ---\n",
      "  1 (Nunca/casi nunca            ) n= 5407  media= 482.2  SE= 2.56  IC95%=[ 477.1,  487.2]\n",
      "  2 (1-2 veces/año               ) n= 3666  media= 468.9  SE= 3.47  IC95%=[ 462.1,  475.7]\n",
      "  3 (1-2 veces/mes               ) n= 6512  media= 489.9  SE= 2.41  IC95%=[ 485.1,  494.6]\n",
      "  4 (1-2 veces/semana            ) n= 6798  media= 493.6  SE= 1.94  IC95%=[ 489.8,  497.4]\n",
      "  5 (A diario/casi a diario      ) n= 4281  media= 480.8  SE= 2.77  IC95%=[ 475.3,  486.2]\n",
      "\n",
      "--- ST438Q03DA: IA: redactar textos para tareas escritas ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Nunca/casi nunca            ) n= 8481  media= 497.2  SE= 2.12  IC95%=[ 493.0,  501.3]\n",
      "  2 (1-2 veces/año               ) n= 3829  media= 478.6  SE= 2.68  IC95%=[ 473.3,  483.8]\n",
      "  3 (1-2 veces/mes               ) n= 5579  media= 481.2  SE= 3.05  IC95%=[ 475.2,  487.1]\n",
      "  4 (1-2 veces/semana            ) n= 5020  media= 483.0  SE= 2.12  IC95%=[ 478.9,  487.2]\n",
      "  5 (A diario/casi a diario      ) n= 3581  media= 473.6  SE= 2.73  IC95%=[ 468.3,  479.0]\n",
      "\n",
      "--- ST438Q04DA: IA: ayudarme a aprender ---\n",
      "  1 (Nunca/casi nunca            ) n= 4314  media= 477.8  SE= 2.99  IC95%=[ 472.0,  483.7]\n",
      "  2 (1-2 veces/año               ) n= 2816  media= 468.1  SE= 3.90  IC95%=[ 460.4,  475.7]\n",
      "  3 (1-2 veces/mes               ) n= 5295  media= 487.5  SE= 2.08  IC95%=[ 483.4,  491.6]\n",
      "  4 (1-2 veces/semana            ) n= 7914  media= 493.3  SE= 2.14  IC95%=[ 489.1,  497.5]\n",
      "  5 (A diario/casi a diario      ) n= 6266  media= 485.2  SE= 2.20  IC95%=[ 480.9,  489.5]\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 70)\n",
    "print(\"RESULTADOS CRUDOS (media ponderada de Ciencias por categoría)\")\n",
    "print(\"=\" * 70)\n",
    "raw_results = {}\n",
    "for var in VARS:\n",
    "    print(f\"\\n--- {var}: {LABELS[var]} ---\")\n",
    "    res = category_means_raw(df, var, WEIGHT, REP_COLS, PV_COLS)\n",
    "    raw_results[var] = res\n",
    "    for cat, (mean, se, n) in sorted(res.items()):\n",
    "        lab = CAT_LABELS[var][int(cat)]\n",
    "        ci_lo, ci_hi = mean - 1.96 * se, mean + 1.96 * se\n",
    "        print(f\"  {int(cat)} ({lab:28s}) n={n:5d}  media={mean:6.1f}  SE={se:5.2f}  IC95%=[{ci_lo:6.1f}, {ci_hi:6.1f}]\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7156c960",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:11.199632Z",
     "iopub.status.busy": "2026-09-19T09:52:11.199522Z",
     "iopub.status.idle": "2026-09-19T09:52:15.107645Z",
     "shell.execute_reply": "2026-09-19T09:52:15.107167Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "======================================================================\n",
      "RESULTADOS AJUSTADOS POR ESCS (regresión ponderada, media a ESCS medio muestral)\n",
      "======================================================================\n",
      "\n",
      "--- ST097Q06DA: Distracción por recursos digitales (clase de ciencias) ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Cada clase                  ) n= 2796  media_ajustada= 460.6  SE= 3.73  IC95%=[ 453.3,  468.0]\n",
      "  2 (La mayoría de clases        ) n= 4906  media_ajustada= 478.3  SE= 2.66  IC95%=[ 473.1,  483.5]\n",
      "  3 (Algunas clases              ) n= 8987  media_ajustada= 484.2  SE= 1.67  IC95%=[ 480.9,  487.5]\n",
      "  4 (Nunca/casi nunca            ) n=10016  media_ajustada= 490.1  SE= 1.71  IC95%=[ 486.7,  493.4]\n",
      "\n",
      "--- ST438Q01DA: IA: resumir un texto ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Nunca/casi nunca            ) n= 7118  media_ajustada= 500.3  SE= 2.27  IC95%=[ 495.8,  504.7]\n",
      "  2 (1-2 veces/año               ) n= 3314  media_ajustada= 480.0  SE= 2.63  IC95%=[ 474.8,  485.1]\n",
      "  3 (1-2 veces/mes               ) n= 5588  media_ajustada= 488.3  SE= 2.01  IC95%=[ 484.3,  492.2]\n",
      "  4 (1-2 veces/semana            ) n= 6374  media_ajustada= 481.5  SE= 1.73  IC95%=[ 478.1,  484.8]\n",
      "  5 (A diario/casi a diario      ) n= 4014  media_ajustada= 470.0  SE= 2.63  IC95%=[ 464.8,  475.1]\n",
      "\n",
      "--- ST438Q02DA: IA: investigación preliminar sobre un tema ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Nunca/casi nunca            ) n= 5328  media_ajustada= 487.1  SE= 2.38  IC95%=[ 482.4,  491.7]\n",
      "  2 (1-2 veces/año               ) n= 3592  media_ajustada= 473.6  SE= 3.11  IC95%=[ 467.5,  479.6]\n",
      "  3 (1-2 veces/mes               ) n= 6434  media_ajustada= 490.5  SE= 2.16  IC95%=[ 486.3,  494.7]\n",
      "  4 (1-2 veces/semana            ) n= 6724  media_ajustada= 491.7  SE= 1.69  IC95%=[ 488.4,  495.0]\n",
      "  5 (A diario/casi a diario      ) n= 4218  media_ajustada= 479.4  SE= 2.48  IC95%=[ 474.6,  484.3]\n",
      "\n",
      "--- ST438Q03DA: IA: redactar textos para tareas escritas ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Nunca/casi nunca            ) n= 8369  media_ajustada= 500.1  SE= 1.90  IC95%=[ 496.4,  503.8]\n",
      "  2 (1-2 veces/año               ) n= 3787  media_ajustada= 481.0  SE= 2.45  IC95%=[ 476.2,  485.8]\n",
      "  3 (1-2 veces/mes               ) n= 5502  media_ajustada= 481.5  SE= 2.69  IC95%=[ 476.2,  486.8]\n",
      "  4 (1-2 veces/semana            ) n= 4956  media_ajustada= 482.3  SE= 1.87  IC95%=[ 478.6,  485.9]\n",
      "  5 (A diario/casi a diario      ) n= 3516  media_ajustada= 473.0  SE= 2.43  IC95%=[ 468.2,  477.7]\n",
      "\n",
      "--- ST438Q04DA: IA: ayudarme a aprender ---\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1 (Nunca/casi nunca            ) n= 4233  media_ajustada= 483.7  SE= 2.81  IC95%=[ 478.2,  489.2]\n",
      "  2 (1-2 veces/año               ) n= 2762  media_ajustada= 472.9  SE= 3.43  IC95%=[ 466.1,  479.6]\n",
      "  3 (1-2 veces/mes               ) n= 5237  media_ajustada= 488.8  SE= 1.95  IC95%=[ 485.0,  492.7]\n",
      "  4 (1-2 veces/semana            ) n= 7819  media_ajustada= 492.4  SE= 1.91  IC95%=[ 488.7,  496.2]\n",
      "  5 (A diario/casi a diario      ) n= 6186  media_ajustada= 483.9  SE= 1.97  IC95%=[ 480.1,  487.8]\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 70)\n",
    "print(\"RESULTADOS AJUSTADOS POR ESCS (regresión ponderada, media a ESCS medio muestral)\")\n",
    "print(\"=\" * 70)\n",
    "adj_results = {}\n",
    "n_adj_used = {}\n",
    "for var in VARS:\n",
    "    print(f\"\\n--- {var}: {LABELS[var]} ---\")\n",
    "    res, n_used = weighted_ols_adjusted_means(df, var, \"ESCS\", WEIGHT, REP_COLS, PV_COLS)\n",
    "    adj_results[var] = res\n",
    "    n_adj_used[var] = n_used\n",
    "    for cat, (mean, se, n) in sorted(res.items()):\n",
    "        lab = CAT_LABELS[var][int(cat)]\n",
    "        ci_lo, ci_hi = mean - 1.96 * se, mean + 1.96 * se\n",
    "        print(f\"  {int(cat)} ({lab:28s}) n={n:5d}  media_ajustada={mean:6.1f}  SE={se:5.2f}  IC95%=[{ci_lo:6.1f}, {ci_hi:6.1f}]\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d523a4e1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:15.108507Z",
     "iopub.status.busy": "2026-09-19T09:52:15.108422Z",
     "iopub.status.idle": "2026-09-19T09:52:15.111235Z",
     "shell.execute_reply": "2026-09-19T09:52:15.110858Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "======================================================================\n",
      "CHEQUEO DE MONOTONICIDAD (medias crudas, orden de categoría)\n",
      "======================================================================\n",
      "ST097Q06DA: medias en orden de categoría = [np.float64(457.3), np.float64(477.5), np.float64(483.0), np.float64(489.5)]  -> monótona descendente: False | monótona ascendente: True\n",
      "ST438Q01DA: medias en orden de categoría = [np.float64(497.3), np.float64(476.5), np.float64(488.1), np.float64(481.6), np.float64(470.5)]  -> monótona descendente: False | monótona ascendente: False\n",
      "ST438Q02DA: medias en orden de categoría = [np.float64(482.2), np.float64(468.9), np.float64(489.9), np.float64(493.6), np.float64(480.8)]  -> monótona descendente: False | monótona ascendente: False\n",
      "ST438Q03DA: medias en orden de categoría = [np.float64(497.2), np.float64(478.6), np.float64(481.2), np.float64(483.0), np.float64(473.6)]  -> monótona descendente: False | monótona ascendente: False\n",
      "ST438Q04DA: medias en orden de categoría = [np.float64(477.8), np.float64(468.1), np.float64(487.5), np.float64(493.3), np.float64(485.2)]  -> monótona descendente: False | monótona ascendente: False\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 70)\n",
    "print(\"CHEQUEO DE MONOTONICIDAD (medias crudas, orden de categoría)\")\n",
    "print(\"=\" * 70)\n",
    "for var in VARS:\n",
    "    means_ordered = [raw_results[var][c][0] for c in sorted(raw_results[var])]\n",
    "    diffs = np.diff(means_ordered)\n",
    "    monotonic_desc = np.all(diffs <= 0)\n",
    "    monotonic_asc = np.all(diffs >= 0)\n",
    "    print(f\"{var}: medias en orden de categoría = {[round(m,1) for m in means_ordered]}  \"\n",
    "          f\"-> monótona descendente: {monotonic_desc} | monótona ascendente: {monotonic_asc}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93308130",
   "metadata": {},
   "source": [
    "### Verificación adicional: ¿el efecto del ajuste por ESCS es real, o solo por cambiar de muestra?\n",
    "\n",
    "El modelo ajustado usa una muestra ligeramente distinta a la del crudo (excluye los casos con\n",
    "`ESCS` perdido). Para aislar el efecto genuino del ajuste estadístico del efecto de simplemente\n",
    "cambiar de submuestra, se recalcula la media **cruda** — mismo método, sin ESCS en el modelo —\n",
    "pero restringida a los mismos casos con `ESCS` no perdido que usa el modelo ajustado."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "dfaf64ef",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:15.112006Z",
     "iopub.status.busy": "2026-09-19T09:52:15.111927Z",
     "iopub.status.idle": "2026-09-19T09:52:15.801469Z",
     "shell.execute_reply": "2026-09-19T09:52:15.800861Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "MEDIA CRUDA RESTRINGIDA a casos con ESCS no perdido (mismo n que el ajustado)\n",
      "vs. media cruda original (todos los casos) vs. media ajustada por ESCS\n",
      "====================================================================================================\n",
      "\n",
      "--- ST097Q06DA ---\n",
      "  1 (Cada clase               ) n= 2796  media_cruda_restringida= 459.75  SE=4.288\n",
      "  2 (La mayoría de clases     ) n= 4906  media_cruda_restringida= 478.43  SE=3.000\n",
      "  3 (Algunas clases           ) n= 8987  media_cruda_restringida= 483.96  SE=1.825\n",
      "  4 (Nunca/casi nunca         ) n=10016  media_cruda_restringida= 490.47  SE=1.969\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- ST438Q01DA ---\n",
      "  1 (Nunca/casi nunca         ) n= 7118  media_cruda_restringida= 498.97  SE=2.481\n",
      "  2 (1-2 veces/año            ) n= 3314  media_cruda_restringida= 478.12  SE=2.648\n",
      "  3 (1-2 veces/mes            ) n= 5588  media_cruda_restringida= 489.14  SE=2.330\n",
      "  4 (1-2 veces/semana         ) n= 6374  media_cruda_restringida= 482.21  SE=2.019\n",
      "  5 (A diario/casi a diario   ) n= 4014  media_cruda_restringida= 471.30  SE=2.985\n",
      "\n",
      "--- ST438Q02DA ---\n",
      "  1 (Nunca/casi nunca         ) n= 5328  media_cruda_restringida= 483.06  SE=2.582\n",
      "  2 (1-2 veces/año            ) n= 3592  media_cruda_restringida= 471.14  SE=3.350\n",
      "  3 (1-2 veces/mes            ) n= 6434  media_cruda_restringida= 491.07  SE=2.355\n",
      "  4 (1-2 veces/semana         ) n= 6724  media_cruda_restringida= 494.43  SE=1.946\n",
      "  5 (A diario/casi a diario   ) n= 4218  media_cruda_restringida= 481.37  SE=2.812\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- ST438Q03DA ---\n",
      "  1 (Nunca/casi nunca         ) n= 8369  media_cruda_restringida= 498.16  SE=2.096\n",
      "  2 (1-2 veces/año            ) n= 3787  media_cruda_restringida= 479.47  SE=2.648\n",
      "  3 (1-2 veces/mes            ) n= 5502  media_cruda_restringida= 482.93  SE=2.980\n",
      "  4 (1-2 veces/semana         ) n= 4956  media_cruda_restringida= 484.04  SE=2.137\n",
      "  5 (A diario/casi a diario   ) n= 3516  media_cruda_restringida= 474.31  SE=2.755\n",
      "\n",
      "--- ST438Q04DA ---\n",
      "  1 (Nunca/casi nunca         ) n= 4233  media_cruda_restringida= 479.44  SE=3.023\n",
      "  2 (1-2 veces/año            ) n= 2762  media_cruda_restringida= 469.62  SE=3.888\n",
      "  3 (1-2 veces/mes            ) n= 5237  media_cruda_restringida= 488.48  SE=2.098\n",
      "  4 (1-2 veces/semana         ) n= 7819  media_cruda_restringida= 494.42  SE=2.113\n",
      "  5 (A diario/casi a diario   ) n= 6186  media_cruda_restringida= 485.87  SE=2.225\n"
     ]
    }
   ],
   "source": [
    "print(\"=\" * 100)\n",
    "print(\"MEDIA CRUDA RESTRINGIDA a casos con ESCS no perdido (mismo n que el ajustado)\")\n",
    "print(\"vs. media cruda original (todos los casos) vs. media ajustada por ESCS\")\n",
    "print(\"=\" * 100)\n",
    "\n",
    "restricted_results = {}\n",
    "for var in VARS:\n",
    "    df_restricted = df.dropna(subset=[var, \"ESCS\"]).copy()\n",
    "    res_restricted = category_means_raw(df_restricted, var, WEIGHT, REP_COLS, PV_COLS)\n",
    "    restricted_results[var] = res_restricted\n",
    "    print(f\"\\n--- {var} ---\")\n",
    "    for cat in sorted(res_restricted.keys()):\n",
    "        mean, se, n = res_restricted[cat]\n",
    "        lab = CAT_LABELS[var][int(cat)]\n",
    "        print(f\"  {int(cat)} ({lab:25s}) n={n:5d}  media_cruda_restringida={mean:7.2f}  SE={se:.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e24d16d8",
   "metadata": {},
   "source": [
    "### Guardar la tabla de resultados\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "3af809f4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:15.802996Z",
     "iopub.status.busy": "2026-09-19T09:52:15.802881Z",
     "iopub.status.idle": "2026-09-19T09:52:15.817387Z",
     "shell.execute_reply": "2026-09-19T09:52:15.816909Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>variable</th>\n",
       "      <th>label_variable</th>\n",
       "      <th>categoria</th>\n",
       "      <th>categoria_label</th>\n",
       "      <th>n_crudo</th>\n",
       "      <th>media_cruda</th>\n",
       "      <th>se_cruda</th>\n",
       "      <th>ic95_lo_cruda</th>\n",
       "      <th>ic95_hi_cruda</th>\n",
       "      <th>n_ajustado</th>\n",
       "      <th>media_ajustada_escs</th>\n",
       "      <th>se_ajustada</th>\n",
       "      <th>ic95_lo_ajustada</th>\n",
       "      <th>ic95_hi_ajustada</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>ST097Q06DA</td>\n",
       "      <td>Distracción por recursos digitales (clase de c...</td>\n",
       "      <td>1</td>\n",
       "      <td>Cada clase</td>\n",
       "      <td>2860</td>\n",
       "      <td>457.32</td>\n",
       "      <td>4.291</td>\n",
       "      <td>448.91</td>\n",
       "      <td>465.73</td>\n",
       "      <td>2796</td>\n",
       "      <td>460.63</td>\n",
       "      <td>3.735</td>\n",
       "      <td>453.31</td>\n",
       "      <td>467.95</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>ST097Q06DA</td>\n",
       "      <td>Distracción por recursos digitales (clase de c...</td>\n",
       "      <td>2</td>\n",
       "      <td>La mayoría de clases</td>\n",
       "      <td>4977</td>\n",
       "      <td>477.50</td>\n",
       "      <td>3.016</td>\n",
       "      <td>471.59</td>\n",
       "      <td>483.41</td>\n",
       "      <td>4906</td>\n",
       "      <td>478.30</td>\n",
       "      <td>2.662</td>\n",
       "      <td>473.09</td>\n",
       "      <td>483.52</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>ST097Q06DA</td>\n",
       "      <td>Distracción por recursos digitales (clase de c...</td>\n",
       "      <td>3</td>\n",
       "      <td>Algunas clases</td>\n",
       "      <td>9093</td>\n",
       "      <td>483.04</td>\n",
       "      <td>1.877</td>\n",
       "      <td>479.36</td>\n",
       "      <td>486.72</td>\n",
       "      <td>8987</td>\n",
       "      <td>484.20</td>\n",
       "      <td>1.668</td>\n",
       "      <td>480.93</td>\n",
       "      <td>487.47</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>ST097Q06DA</td>\n",
       "      <td>Distracción por recursos digitales (clase de c...</td>\n",
       "      <td>4</td>\n",
       "      <td>Nunca/casi nunca</td>\n",
       "      <td>10146</td>\n",
       "      <td>489.52</td>\n",
       "      <td>2.003</td>\n",
       "      <td>485.59</td>\n",
       "      <td>493.44</td>\n",
       "      <td>10016</td>\n",
       "      <td>490.06</td>\n",
       "      <td>1.714</td>\n",
       "      <td>486.70</td>\n",
       "      <td>493.42</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>ST438Q01DA</td>\n",
       "      <td>IA: resumir un texto</td>\n",
       "      <td>1</td>\n",
       "      <td>Nunca/casi nunca</td>\n",
       "      <td>7235</td>\n",
       "      <td>497.34</td>\n",
       "      <td>2.468</td>\n",
       "      <td>492.50</td>\n",
       "      <td>502.18</td>\n",
       "      <td>7118</td>\n",
       "      <td>500.29</td>\n",
       "      <td>2.268</td>\n",
       "      <td>495.85</td>\n",
       "      <td>504.74</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>ST438Q01DA</td>\n",
       "      <td>IA: resumir un texto</td>\n",
       "      <td>2</td>\n",
       "      <td>1-2 veces/año</td>\n",
       "      <td>3365</td>\n",
       "      <td>476.50</td>\n",
       "      <td>2.729</td>\n",
       "      <td>471.15</td>\n",
       "      <td>481.85</td>\n",
       "      <td>3314</td>\n",
       "      <td>479.98</td>\n",
       "      <td>2.625</td>\n",
       "      <td>474.83</td>\n",
       "      <td>485.12</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>ST438Q01DA</td>\n",
       "      <td>IA: resumir un texto</td>\n",
       "      <td>3</td>\n",
       "      <td>1-2 veces/mes</td>\n",
       "      <td>5649</td>\n",
       "      <td>488.14</td>\n",
       "      <td>2.338</td>\n",
       "      <td>483.56</td>\n",
       "      <td>492.72</td>\n",
       "      <td>5588</td>\n",
       "      <td>488.26</td>\n",
       "      <td>2.007</td>\n",
       "      <td>484.33</td>\n",
       "      <td>492.20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>ST438Q01DA</td>\n",
       "      <td>IA: resumir un texto</td>\n",
       "      <td>4</td>\n",
       "      <td>1-2 veces/semana</td>\n",
       "      <td>6441</td>\n",
       "      <td>481.56</td>\n",
       "      <td>2.018</td>\n",
       "      <td>477.60</td>\n",
       "      <td>485.52</td>\n",
       "      <td>6374</td>\n",
       "      <td>481.46</td>\n",
       "      <td>1.728</td>\n",
       "      <td>478.07</td>\n",
       "      <td>484.85</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>ST438Q01DA</td>\n",
       "      <td>IA: resumir un texto</td>\n",
       "      <td>5</td>\n",
       "      <td>A diario/casi a diario</td>\n",
       "      <td>4090</td>\n",
       "      <td>470.47</td>\n",
       "      <td>2.917</td>\n",
       "      <td>464.76</td>\n",
       "      <td>476.19</td>\n",
       "      <td>4014</td>\n",
       "      <td>469.98</td>\n",
       "      <td>2.628</td>\n",
       "      <td>464.83</td>\n",
       "      <td>475.13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>ST438Q02DA</td>\n",
       "      <td>IA: investigación preliminar sobre un tema</td>\n",
       "      <td>1</td>\n",
       "      <td>Nunca/casi nunca</td>\n",
       "      <td>5407</td>\n",
       "      <td>482.16</td>\n",
       "      <td>2.564</td>\n",
       "      <td>477.13</td>\n",
       "      <td>487.18</td>\n",
       "      <td>5328</td>\n",
       "      <td>487.07</td>\n",
       "      <td>2.381</td>\n",
       "      <td>482.40</td>\n",
       "      <td>491.73</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>ST438Q02DA</td>\n",
       "      <td>IA: investigación preliminar sobre un tema</td>\n",
       "      <td>2</td>\n",
       "      <td>1-2 veces/año</td>\n",
       "      <td>3666</td>\n",
       "      <td>468.88</td>\n",
       "      <td>3.466</td>\n",
       "      <td>462.09</td>\n",
       "      <td>475.68</td>\n",
       "      <td>3592</td>\n",
       "      <td>473.56</td>\n",
       "      <td>3.106</td>\n",
       "      <td>467.48</td>\n",
       "      <td>479.65</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>ST438Q02DA</td>\n",
       "      <td>IA: investigación preliminar sobre un tema</td>\n",
       "      <td>3</td>\n",
       "      <td>1-2 veces/mes</td>\n",
       "      <td>6512</td>\n",
       "      <td>489.86</td>\n",
       "      <td>2.408</td>\n",
       "      <td>485.14</td>\n",
       "      <td>494.58</td>\n",
       "      <td>6434</td>\n",
       "      <td>490.49</td>\n",
       "      <td>2.161</td>\n",
       "      <td>486.25</td>\n",
       "      <td>494.73</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>ST438Q02DA</td>\n",
       "      <td>IA: investigación preliminar sobre un tema</td>\n",
       "      <td>4</td>\n",
       "      <td>1-2 veces/semana</td>\n",
       "      <td>6798</td>\n",
       "      <td>493.65</td>\n",
       "      <td>1.939</td>\n",
       "      <td>489.85</td>\n",
       "      <td>497.44</td>\n",
       "      <td>6724</td>\n",
       "      <td>491.72</td>\n",
       "      <td>1.692</td>\n",
       "      <td>488.40</td>\n",
       "      <td>495.04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>ST438Q02DA</td>\n",
       "      <td>IA: investigación preliminar sobre un tema</td>\n",
       "      <td>5</td>\n",
       "      <td>A diario/casi a diario</td>\n",
       "      <td>4281</td>\n",
       "      <td>480.75</td>\n",
       "      <td>2.772</td>\n",
       "      <td>475.32</td>\n",
       "      <td>486.19</td>\n",
       "      <td>4218</td>\n",
       "      <td>479.44</td>\n",
       "      <td>2.475</td>\n",
       "      <td>474.58</td>\n",
       "      <td>484.29</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>ST438Q03DA</td>\n",
       "      <td>IA: redactar textos para tareas escritas</td>\n",
       "      <td>1</td>\n",
       "      <td>Nunca/casi nunca</td>\n",
       "      <td>8481</td>\n",
       "      <td>497.16</td>\n",
       "      <td>2.123</td>\n",
       "      <td>493.00</td>\n",
       "      <td>501.32</td>\n",
       "      <td>8369</td>\n",
       "      <td>500.12</td>\n",
       "      <td>1.900</td>\n",
       "      <td>496.40</td>\n",
       "      <td>503.85</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>ST438Q03DA</td>\n",
       "      <td>IA: redactar textos para tareas escritas</td>\n",
       "      <td>2</td>\n",
       "      <td>1-2 veces/año</td>\n",
       "      <td>3829</td>\n",
       "      <td>478.58</td>\n",
       "      <td>2.683</td>\n",
       "      <td>473.32</td>\n",
       "      <td>483.83</td>\n",
       "      <td>3787</td>\n",
       "      <td>481.02</td>\n",
       "      <td>2.448</td>\n",
       "      <td>476.22</td>\n",
       "      <td>485.82</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>ST438Q03DA</td>\n",
       "      <td>IA: redactar textos para tareas escritas</td>\n",
       "      <td>3</td>\n",
       "      <td>1-2 veces/mes</td>\n",
       "      <td>5579</td>\n",
       "      <td>481.15</td>\n",
       "      <td>3.053</td>\n",
       "      <td>475.17</td>\n",
       "      <td>487.14</td>\n",
       "      <td>5502</td>\n",
       "      <td>481.50</td>\n",
       "      <td>2.690</td>\n",
       "      <td>476.23</td>\n",
       "      <td>486.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>ST438Q03DA</td>\n",
       "      <td>IA: redactar textos para tareas escritas</td>\n",
       "      <td>4</td>\n",
       "      <td>1-2 veces/semana</td>\n",
       "      <td>5020</td>\n",
       "      <td>483.04</td>\n",
       "      <td>2.124</td>\n",
       "      <td>478.87</td>\n",
       "      <td>487.20</td>\n",
       "      <td>4956</td>\n",
       "      <td>482.25</td>\n",
       "      <td>1.873</td>\n",
       "      <td>478.58</td>\n",
       "      <td>485.92</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>18</th>\n",
       "      <td>ST438Q03DA</td>\n",
       "      <td>IA: redactar textos para tareas escritas</td>\n",
       "      <td>5</td>\n",
       "      <td>A diario/casi a diario</td>\n",
       "      <td>3581</td>\n",
       "      <td>473.64</td>\n",
       "      <td>2.729</td>\n",
       "      <td>468.29</td>\n",
       "      <td>478.98</td>\n",
       "      <td>3516</td>\n",
       "      <td>472.96</td>\n",
       "      <td>2.430</td>\n",
       "      <td>468.20</td>\n",
       "      <td>477.73</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19</th>\n",
       "      <td>ST438Q04DA</td>\n",
       "      <td>IA: ayudarme a aprender</td>\n",
       "      <td>1</td>\n",
       "      <td>Nunca/casi nunca</td>\n",
       "      <td>4314</td>\n",
       "      <td>477.84</td>\n",
       "      <td>2.994</td>\n",
       "      <td>471.97</td>\n",
       "      <td>483.71</td>\n",
       "      <td>4233</td>\n",
       "      <td>483.69</td>\n",
       "      <td>2.809</td>\n",
       "      <td>478.18</td>\n",
       "      <td>489.19</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>20</th>\n",
       "      <td>ST438Q04DA</td>\n",
       "      <td>IA: ayudarme a aprender</td>\n",
       "      <td>2</td>\n",
       "      <td>1-2 veces/año</td>\n",
       "      <td>2816</td>\n",
       "      <td>468.08</td>\n",
       "      <td>3.903</td>\n",
       "      <td>460.43</td>\n",
       "      <td>475.73</td>\n",
       "      <td>2762</td>\n",
       "      <td>472.85</td>\n",
       "      <td>3.431</td>\n",
       "      <td>466.13</td>\n",
       "      <td>479.58</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>21</th>\n",
       "      <td>ST438Q04DA</td>\n",
       "      <td>IA: ayudarme a aprender</td>\n",
       "      <td>3</td>\n",
       "      <td>1-2 veces/mes</td>\n",
       "      <td>5295</td>\n",
       "      <td>487.52</td>\n",
       "      <td>2.079</td>\n",
       "      <td>483.44</td>\n",
       "      <td>491.59</td>\n",
       "      <td>5237</td>\n",
       "      <td>488.85</td>\n",
       "      <td>1.946</td>\n",
       "      <td>485.03</td>\n",
       "      <td>492.66</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>22</th>\n",
       "      <td>ST438Q04DA</td>\n",
       "      <td>IA: ayudarme a aprender</td>\n",
       "      <td>4</td>\n",
       "      <td>1-2 veces/semana</td>\n",
       "      <td>7914</td>\n",
       "      <td>493.26</td>\n",
       "      <td>2.140</td>\n",
       "      <td>489.07</td>\n",
       "      <td>497.45</td>\n",
       "      <td>7819</td>\n",
       "      <td>492.43</td>\n",
       "      <td>1.914</td>\n",
       "      <td>488.68</td>\n",
       "      <td>496.18</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>23</th>\n",
       "      <td>ST438Q04DA</td>\n",
       "      <td>IA: ayudarme a aprender</td>\n",
       "      <td>5</td>\n",
       "      <td>A diario/casi a diario</td>\n",
       "      <td>6266</td>\n",
       "      <td>485.18</td>\n",
       "      <td>2.205</td>\n",
       "      <td>480.86</td>\n",
       "      <td>489.50</td>\n",
       "      <td>6186</td>\n",
       "      <td>483.91</td>\n",
       "      <td>1.965</td>\n",
       "      <td>480.06</td>\n",
       "      <td>487.76</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      variable                                     label_variable  categoria  \\\n",
       "0   ST097Q06DA  Distracción por recursos digitales (clase de c...          1   \n",
       "1   ST097Q06DA  Distracción por recursos digitales (clase de c...          2   \n",
       "2   ST097Q06DA  Distracción por recursos digitales (clase de c...          3   \n",
       "3   ST097Q06DA  Distracción por recursos digitales (clase de c...          4   \n",
       "4   ST438Q01DA                               IA: resumir un texto          1   \n",
       "5   ST438Q01DA                               IA: resumir un texto          2   \n",
       "6   ST438Q01DA                               IA: resumir un texto          3   \n",
       "7   ST438Q01DA                               IA: resumir un texto          4   \n",
       "8   ST438Q01DA                               IA: resumir un texto          5   \n",
       "9   ST438Q02DA         IA: investigación preliminar sobre un tema          1   \n",
       "10  ST438Q02DA         IA: investigación preliminar sobre un tema          2   \n",
       "11  ST438Q02DA         IA: investigación preliminar sobre un tema          3   \n",
       "12  ST438Q02DA         IA: investigación preliminar sobre un tema          4   \n",
       "13  ST438Q02DA         IA: investigación preliminar sobre un tema          5   \n",
       "14  ST438Q03DA           IA: redactar textos para tareas escritas          1   \n",
       "15  ST438Q03DA           IA: redactar textos para tareas escritas          2   \n",
       "16  ST438Q03DA           IA: redactar textos para tareas escritas          3   \n",
       "17  ST438Q03DA           IA: redactar textos para tareas escritas          4   \n",
       "18  ST438Q03DA           IA: redactar textos para tareas escritas          5   \n",
       "19  ST438Q04DA                            IA: ayudarme a aprender          1   \n",
       "20  ST438Q04DA                            IA: ayudarme a aprender          2   \n",
       "21  ST438Q04DA                            IA: ayudarme a aprender          3   \n",
       "22  ST438Q04DA                            IA: ayudarme a aprender          4   \n",
       "23  ST438Q04DA                            IA: ayudarme a aprender          5   \n",
       "\n",
       "           categoria_label  n_crudo  media_cruda  se_cruda  ic95_lo_cruda  \\\n",
       "0               Cada clase     2860       457.32     4.291         448.91   \n",
       "1     La mayoría de clases     4977       477.50     3.016         471.59   \n",
       "2           Algunas clases     9093       483.04     1.877         479.36   \n",
       "3         Nunca/casi nunca    10146       489.52     2.003         485.59   \n",
       "4         Nunca/casi nunca     7235       497.34     2.468         492.50   \n",
       "5            1-2 veces/año     3365       476.50     2.729         471.15   \n",
       "6            1-2 veces/mes     5649       488.14     2.338         483.56   \n",
       "7         1-2 veces/semana     6441       481.56     2.018         477.60   \n",
       "8   A diario/casi a diario     4090       470.47     2.917         464.76   \n",
       "9         Nunca/casi nunca     5407       482.16     2.564         477.13   \n",
       "10           1-2 veces/año     3666       468.88     3.466         462.09   \n",
       "11           1-2 veces/mes     6512       489.86     2.408         485.14   \n",
       "12        1-2 veces/semana     6798       493.65     1.939         489.85   \n",
       "13  A diario/casi a diario     4281       480.75     2.772         475.32   \n",
       "14        Nunca/casi nunca     8481       497.16     2.123         493.00   \n",
       "15           1-2 veces/año     3829       478.58     2.683         473.32   \n",
       "16           1-2 veces/mes     5579       481.15     3.053         475.17   \n",
       "17        1-2 veces/semana     5020       483.04     2.124         478.87   \n",
       "18  A diario/casi a diario     3581       473.64     2.729         468.29   \n",
       "19        Nunca/casi nunca     4314       477.84     2.994         471.97   \n",
       "20           1-2 veces/año     2816       468.08     3.903         460.43   \n",
       "21           1-2 veces/mes     5295       487.52     2.079         483.44   \n",
       "22        1-2 veces/semana     7914       493.26     2.140         489.07   \n",
       "23  A diario/casi a diario     6266       485.18     2.205         480.86   \n",
       "\n",
       "    ic95_hi_cruda  n_ajustado  media_ajustada_escs  se_ajustada  \\\n",
       "0          465.73        2796               460.63        3.735   \n",
       "1          483.41        4906               478.30        2.662   \n",
       "2          486.72        8987               484.20        1.668   \n",
       "3          493.44       10016               490.06        1.714   \n",
       "4          502.18        7118               500.29        2.268   \n",
       "5          481.85        3314               479.98        2.625   \n",
       "6          492.72        5588               488.26        2.007   \n",
       "7          485.52        6374               481.46        1.728   \n",
       "8          476.19        4014               469.98        2.628   \n",
       "9          487.18        5328               487.07        2.381   \n",
       "10         475.68        3592               473.56        3.106   \n",
       "11         494.58        6434               490.49        2.161   \n",
       "12         497.44        6724               491.72        1.692   \n",
       "13         486.19        4218               479.44        2.475   \n",
       "14         501.32        8369               500.12        1.900   \n",
       "15         483.83        3787               481.02        2.448   \n",
       "16         487.14        5502               481.50        2.690   \n",
       "17         487.20        4956               482.25        1.873   \n",
       "18         478.98        3516               472.96        2.430   \n",
       "19         483.71        4233               483.69        2.809   \n",
       "20         475.73        2762               472.85        3.431   \n",
       "21         491.59        5237               488.85        1.946   \n",
       "22         497.45        7819               492.43        1.914   \n",
       "23         489.50        6186               483.91        1.965   \n",
       "\n",
       "    ic95_lo_ajustada  ic95_hi_ajustada  \n",
       "0             453.31            467.95  \n",
       "1             473.09            483.52  \n",
       "2             480.93            487.47  \n",
       "3             486.70            493.42  \n",
       "4             495.85            504.74  \n",
       "5             474.83            485.12  \n",
       "6             484.33            492.20  \n",
       "7             478.07            484.85  \n",
       "8             464.83            475.13  \n",
       "9             482.40            491.73  \n",
       "10            467.48            479.65  \n",
       "11            486.25            494.73  \n",
       "12            488.40            495.04  \n",
       "13            474.58            484.29  \n",
       "14            496.40            503.85  \n",
       "15            476.22            485.82  \n",
       "16            476.23            486.78  \n",
       "17            478.58            485.92  \n",
       "18            468.20            477.73  \n",
       "19            478.18            489.19  \n",
       "20            466.13            479.58  \n",
       "21            485.03            492.66  \n",
       "22            488.68            496.18  \n",
       "23            480.06            487.76  "
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = []\n",
    "for var in VARS:\n",
    "    for cat in sorted(raw_results[var].keys()):\n",
    "        rm, rse, rn = raw_results[var][cat]\n",
    "        am, ase, an = adj_results[var][cat]\n",
    "        rows.append({\n",
    "            \"variable\": var,\n",
    "            \"label_variable\": LABELS[var],\n",
    "            \"categoria\": int(cat),\n",
    "            \"categoria_label\": CAT_LABELS[var][int(cat)],\n",
    "            \"n_crudo\": rn,\n",
    "            \"media_cruda\": round(rm, 2),\n",
    "            \"se_cruda\": round(rse, 3),\n",
    "            \"ic95_lo_cruda\": round(rm - 1.96 * rse, 2),\n",
    "            \"ic95_hi_cruda\": round(rm + 1.96 * rse, 2),\n",
    "            \"n_ajustado\": an,\n",
    "            \"media_ajustada_escs\": round(am, 2),\n",
    "            \"se_ajustada\": round(ase, 3),\n",
    "            \"ic95_lo_ajustada\": round(am - 1.96 * ase, 2),\n",
    "            \"ic95_hi_ajustada\": round(am + 1.96 * ase, 2),\n",
    "        })\n",
    "res_df = pd.DataFrame(rows)\n",
    "res_df.to_csv(\"04_resultados.csv\", index=False)\n",
    "res_df"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97107cfb",
   "metadata": {},
   "source": [
    "## 5. Gráficos\n",
    "\n",
    "Se genera un panel individual por variable (media cruda vs. ajustada por ESCS, con barras de error\n",
    "al 95%), a partir de la tabla de resultados ya calculada — sin recalcular ninguna cifra, solo\n",
    "visualizarlas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "852e5da0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-19T09:52:15.818481Z",
     "iopub.status.busy": "2026-09-19T09:52:15.818369Z",
     "iopub.status.idle": "2026-09-19T09:52:16.789109Z",
     "shell.execute_reply": "2026-09-19T09:52:16.788642Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1560981/90703746.py:70: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n",
      "  plt.show()\n",
      "/tmp/ipykernel_1560981/90703746.py:70: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n",
      "  plt.show()\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Guardado: panel-1-distraccion.png\n",
      "Guardado: panel-2-ia-resumir.png\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1560981/90703746.py:70: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n",
      "  plt.show()\n",
      "/tmp/ipykernel_1560981/90703746.py:70: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n",
      "  plt.show()\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Guardado: panel-3-ia-investigar.png\n",
      "Guardado: panel-4-ia-redactar.png\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Guardado: panel-5-ia-aprender.png\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1560981/90703746.py:70: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n",
      "  plt.show()\n"
     ]
    }
   ],
   "source": [
    "COLOR_RAW = \"#AE4D02\"   # accent-deep del sistema de diseño (texto naranja AA)\n",
    "COLOR_ADJ = \"#3b6ea5\"\n",
    "\n",
    "PANELS = [\n",
    "    (\"ST097Q06DA\", \"panel-1-distraccion.png\",\n",
    "     \"Distracción por recursos digitales en clase de Ciencias\\ny rendimiento en Ciencias — PISA 2025, España\"),\n",
    "    (\"ST438Q01DA\", \"panel-2-ia-resumir.png\",\n",
    "     \"Uso de IA para resumir un texto\\ny rendimiento en Ciencias — PISA 2025, España\"),\n",
    "    (\"ST438Q02DA\", \"panel-3-ia-investigar.png\",\n",
    "     \"Uso de IA para investigación preliminar sobre un tema\\ny rendimiento en Ciencias — PISA 2025, España\"),\n",
    "    (\"ST438Q03DA\", \"panel-4-ia-redactar.png\",\n",
    "     \"Uso de IA para redactar textos de tareas escolares\\ny rendimiento en Ciencias — PISA 2025, España\"),\n",
    "    (\"ST438Q04DA\", \"panel-5-ia-aprender.png\",\n",
    "     \"Uso de IA para \\u201cayudarme a aprender\\u201d\\ny rendimiento en Ciencias — PISA 2025, España\"),\n",
    "]\n",
    "\n",
    "plt.rcParams.update({\n",
    "    \"font.size\": 15,\n",
    "    \"axes.titlesize\": 17,\n",
    "    \"axes.labelsize\": 16,\n",
    "    \"xtick.labelsize\": 14,\n",
    "    \"ytick.labelsize\": 14,\n",
    "    \"legend.fontsize\": 14,\n",
    "})\n",
    "\n",
    "for var, fname, title in PANELS:\n",
    "    sub = res_df[res_df[\"variable\"] == var].sort_values(\"categoria\")\n",
    "    assert len(sub) > 0, f\"Variable {var} no encontrada en la tabla de resultados\"\n",
    "\n",
    "    cats_labels = sub[\"categoria_label\"].tolist()\n",
    "    x = range(len(cats_labels))\n",
    "\n",
    "    raw_m = sub[\"media_cruda\"].to_numpy()\n",
    "    raw_lo = sub[\"ic95_lo_cruda\"].to_numpy()\n",
    "    raw_hi = sub[\"ic95_hi_cruda\"].to_numpy()\n",
    "    raw_err = [raw_m - raw_lo, raw_hi - raw_m]\n",
    "\n",
    "    adj_m = sub[\"media_ajustada_escs\"].to_numpy()\n",
    "    adj_lo = sub[\"ic95_lo_ajustada\"].to_numpy()\n",
    "    adj_hi = sub[\"ic95_hi_ajustada\"].to_numpy()\n",
    "    adj_err = [adj_m - adj_lo, adj_hi - adj_m]\n",
    "\n",
    "    fig, ax = plt.subplots(figsize=(10, 7.5))\n",
    "\n",
    "    offset = 0.07\n",
    "    x_raw = [xi - offset for xi in x]\n",
    "    x_adj = [xi + offset for xi in x]\n",
    "\n",
    "    ax.errorbar(x_raw, raw_m, yerr=raw_err, fmt=\"o-\", color=COLOR_RAW,\n",
    "                label=\"Crudo\", capsize=6, linewidth=2.5, markersize=9)\n",
    "    ax.errorbar(x_adj, adj_m, yerr=adj_err, fmt=\"s--\", color=COLOR_ADJ,\n",
    "                label=\"Ajustado por ESCS\", capsize=6, linewidth=2.5, markersize=9)\n",
    "\n",
    "    ax.set_xticks(list(x))\n",
    "    ax.set_xticklabels(cats_labels, rotation=20, ha=\"right\")\n",
    "    ax.set_ylabel(\"Puntuación media en Ciencias (PISA 2025)\")\n",
    "    ax.set_title(title, pad=16)\n",
    "    ax.grid(axis=\"y\", linestyle=\":\", alpha=0.6)\n",
    "    ax.legend(loc=\"best\", frameon=True)\n",
    "\n",
    "    n_crudo_total = int(sub[\"n_crudo\"].sum())\n",
    "    n_adj_total = int(sub[\"n_ajustado\"].sum())\n",
    "    fig.text(0.01, 0.01,\n",
    "              f\"España, PISA 2025. n crudo = {n_crudo_total:,} · n ajustado (con ESCS no perdido) = {n_adj_total:,}. \"\n",
    "              f\"Barras de error: IC 95%.\".replace(\",\", \" \"),\n",
    "              fontsize=10, color=\"#555555\")\n",
    "\n",
    "    plt.tight_layout(rect=[0, 0.03, 1, 1])\n",
    "    plt.savefig(fname, dpi=150, bbox_inches=\"tight\")\n",
    "    plt.show()\n",
    "    plt.close(fig)\n",
    "    print(f\"Guardado: {fname}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3e5f239",
   "metadata": {},
   "source": [
    "## 6. Resultado y salvedades\n",
    "\n",
    "**Muestra:** España, PISA 2025, n = 29 966 estudiantes con ponderación muestral `W_FSTUWT`.\n",
    "\n",
    "### Cifras clave (media cruda de Ciencias, IC 95%)\n",
    "\n",
    "**Distracción por recursos digitales en clase de Ciencias (`ST097Q06DA`):**\n",
    "\n",
    "| Categoría | n | Media | IC 95% |\n",
    "|---|---|---|---|\n",
    "| Cada clase | 2 860 | 457.3 | [448.9, 465.7] |\n",
    "| La mayoría de clases | 4 977 | 477.5 | [471.6, 483.4] |\n",
    "| Algunas clases | 9 093 | 483.0 | [479.4, 486.7] |\n",
    "| Nunca/casi nunca | 10 146 | 489.5 | [485.6, 493.4] |\n",
    "\n",
    "Brecha bruta entre \"cada clase\" y \"nunca/casi nunca\": **≈32.2 puntos** de Ciencias. La relación es\n",
    "monótona: a más distracción reportada, menor rendimiento medio — en crudo y también tras ajustar\n",
    "por ESCS.\n",
    "\n",
    "**Uso de chatbots de IA (`ST438Q01DA`–`ST438Q04DA`):** en ninguna de las 4 tareas la relación es\n",
    "monótona (ver el chequeo de la celda anterior). Pero, además, la **forma** de esa no-monotonicidad\n",
    "difiere entre tareas — no hay un único patrón de \"uso moderado óptimo\" común a las cuatro:\n",
    "\n",
    "| Tarea | Categoría con la media más alta | Media |\n",
    "|---|---|---|\n",
    "| Resumir texto (`ST438Q01DA`) | Nunca/casi nunca | 497.3 |\n",
    "| Investigación preliminar (`ST438Q02DA`) | 1-2 veces/semana | 493.6 |\n",
    "| Redactar trabajos (`ST438Q03DA`) | Nunca/casi nunca | 497.2 |\n",
    "| Ayudarme a aprender (`ST438Q04DA`) | 1-2 veces/semana | 493.3 |\n",
    "\n",
    "En \"resumir texto\" y \"redactar trabajos\", el grupo que **nunca** usa el chatbot presenta la media\n",
    "más alta de las 5 categorías — no una de las más bajas. En \"buscar información\" y \"ayudarme a\n",
    "aprender\", el máximo aparece en el uso semanal. Por tanto, **los datos no sostienen una regla\n",
    "general del tipo \"más uso = peor rendimiento\" ni un patrón único de \"uso moderado óptimo\" común a\n",
    "las cuatro tareas** — la relación entre frecuencia de uso y rendimiento en Ciencias, en este\n",
    "dataset, depende de la tarea.\n",
    "\n",
    "### Salvedad: IC individuales no son un contraste de significación entre categorías\n",
    "\n",
    "Este análisis descriptivo calcula intervalos de confianza **individuales por categoría**, pero no\n",
    "un contraste directo de la diferencia entre dos categorías (que requeriría propagar conjuntamente\n",
    "la varianza de los *plausible values* y de las réplicas BRR-Fay **sobre la diferencia**, no solo\n",
    "mirar si los intervalos individuales se solapan). Por tanto, no se afirma aquí que las diferencias\n",
    "entre categorías con intervalos solapados sean o no estadísticamente significativas — ese\n",
    "contraste queda fuera del alcance de este cuaderno.\n",
    "\n",
    "### Salvedad de honestidad: la cifra \"un año escolar completo\"\n",
    "\n",
    "Este dataset **no contiene una variable de \"puntos PISA por año de escolaridad\"**. El *PISA 2025\n",
    "Results Volume I* sí documenta, en su Box I.2.2, una estimación general de que el alumnado de 15\n",
    "años gana en promedio **~20 puntos PISA** por año de escolaridad y edad — esa es la cifra oficial\n",
    "vigente, no \"25-39 puntos\" ni una referencia a *PISA in Focus* de ciclos previos. Pero ese\n",
    "benchmark es **general** (pensado para interpretar cambios de puntuación a través del tiempo o\n",
    "entre ciclos) y el informe oficial **no lo aplica en ningún punto de su capítulo dedicado al uso de\n",
    "IA** al hallazgo de frecuencia de uso de chatbots. La equivalencia específica \"usar la IA bien o\n",
    "mal equivale a un año escolar\" que circula en cobertura secundaria es una conversión hecha por\n",
    "terceros, no algo que el informe de la OCDE afirme literalmente sobre este hallazgo.\n",
    "**No se reproduce aquí de forma independiente** — este cuaderno reporta la brecha bruta de puntos\n",
    "observada (≈32 puntos en distracción por recursos digitales), pero la traducción de esa brecha a\n",
    "\"años de escolaridad\" no está verificada con este dataset ni respaldada así por el informe oficial,\n",
    "y no debe citarse como confirmada por este análisis.\n",
    "\n",
    "### Sobre causalidad\n",
    "\n",
    "Todo lo anterior es **correlacional**, de corte transversal, con datos de autoinforme. El ajuste\n",
    "por ESCS reduce el papel del nivel socioeconómico como variable de confusión, pero no permite\n",
    "afirmar causalidad en ninguna dirección (p. ej. no se puede descartar que el rendimiento previo\n",
    "influya en la frecuencia de uso reportada, en vez de al revés).\n",
    "\n",
    "---\n",
    "\n",
    "**Artefactos generados por este cuaderno:** `04_resultados.csv` (tabla completa) y 5 gráficos\n",
    "`panel-1-distraccion.png` … `panel-5-ia-aprender.png`.\n",
    "\n",
    "**Réplica exacta:** con el fichero `.sav` oficial en la ruta indicada, este cuaderno se ejecuta de\n",
    "principio a fin sin intervención manual y reproduce las cifras citadas en el artículo del blog.\n"
   ]
  }
 ],
 "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}