{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Significativo no es importante\n",
    "\n",
    "El tamaño del efecto, la potencia, y la demostración de que copiando los datos diez veces el valor p se derrumba y el efecto no se mueve.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 14 de **Estadística desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/estadistica-desde-cero/tamano-del-efecto/\n",
    "\n",
    "Este es el cuaderno de **soluciones**. Trae el código de cada ejercicio, la\n",
    "explicación de la trampa y la respuesta del quiz. Si vienes del cuaderno de\n",
    "práctica sin haberlo intentado, vuelve 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 12 quedó dicho: **significativo no quiere decir\n",
    "importante**.\n",
    "\n",
    "Ahora lo vamos a demostrar de la forma más brutal que se me ocurre 😈"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from scipy import stats\n",
    "\n",
    "URL = 'https://missyera.com/static/datasets/ventas-miss-yera.csv'\n",
    "\n",
    "\n",
    "def carga_limpia(url):\n",
    "    \"\"\"La misma del capítulo 2.\"\"\"\n",
    "    v = pd.read_csv(url).drop_duplicates()\n",
    "    v['ciudad'] = (v['ciudad'].str.strip().str.lower()\n",
    "                   .str.normalize('NFKD')\n",
    "                   .str.encode('ascii', 'ignore').str.decode('utf-8'))\n",
    "    v['monto'] = pd.to_numeric(v['monto'].str.replace(',', '.'))\n",
    "    for col in ['fecha', 'fecha_ultima_compra']:\n",
    "        f = pd.to_datetime(v[col], format='%Y-%m-%d', errors='coerce')\n",
    "        falta = f.isna() & v[col].notna()\n",
    "        f[falta] = pd.to_datetime(v.loc[falta, col], format='%d/%m/%Y',\n",
    "                                  errors='coerce')\n",
    "        v[col] = f\n",
    "    return v\n",
    "\n",
    "\n",
    "v = carga_limpia(URL)\n",
    "\n",
    "horeca = v.loc[v['segmento'] == 'Horeca', 'compro'].values\n",
    "minimarket = v.loc[v['segmento'] == 'Minimarket', 'compro'].values\n",
    "\n",
    "\n",
    "def d_de_cohen(a, b):\n",
    "    \"\"\"Cuantas desviaciones separan a los dos grupos.\"\"\"\n",
    "    na, nb = len(a), len(b)\n",
    "    juntas = np.sqrt(((na - 1) * a.var(ddof=1) + (nb - 1) * b.var(ddof=1))\n",
    "                     / (na + nb - 2))\n",
    "    return (a.mean() - b.mean()) / juntas\n",
    "\n",
    "\n",
    "for veces in [1, 2, 5, 10]:\n",
    "    a = np.tile(horeca, veces)\n",
    "    b = np.tile(minimarket, veces)\n",
    "    print('copiando los datos x%2d (n=%5d) -> diferencia %.4f | d %.4f | p %.3g'\n",
    "          % (veces, len(a), a.mean() - b.mean(), d_de_cohen(a, b),\n",
    "             stats.ttest_ind(a, b, equal_var=False).pvalue))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Léelo columna por columna 👀\n",
    "\n",
    "La diferencia: **0,0628 siempre**. El tamaño del efecto:\n",
    "**0,1284 siempre**. Y el valor p se derrumba de 0,0118 a\n",
    "0,0000000000000016.\n",
    "\n",
    "**No hay ni un dato nuevo.** Son las mismas 742 ventas copiadas\n",
    "y pegadas. Y aun así el resultado pasa de \"significativo, apenas\" a \"significativo\n",
    "más allá de toda duda\" 🤯\n",
    "\n",
    "Eso es todo lo que hay que saber sobre el valor p: mide\n",
    "*evidencia*, no *importancia*. Y la evidencia crece con la\n",
    "cantidad de datos aunque el efecto sea el mismo.\n",
    "\n",
    "Y hazte esta pregunta cada vez que veas un \"significativo\": **¿cuánto es la diferencia en soles, en clientes o en pedidos?** Si nadie sabe contestarla, ese resultado todavía no sirve para decidir nada 📐"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El tamaño del efecto, que es lo que sí hay que reportar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La **d de Cohen** mide la diferencia en desviaciones estándar,\n",
    "así que no depende ni de las unidades ni de cuántos datos tengas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "d=x¯1−x¯2sjuntas\n",
    "\n",
    "la diferencia medida en desviaciones, que no depende ni de las unidades ni de cuántos datos tengas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compara(a, b, nombre):\n",
    "    d = d_de_cohen(a, b)\n",
    "    if abs(d) < 0.2:\n",
    "        etiqueta = 'insignificante'\n",
    "    elif abs(d) < 0.5:\n",
    "        etiqueta = 'chico'\n",
    "    elif abs(d) < 0.8:\n",
    "        etiqueta = 'mediano'\n",
    "    else:\n",
    "        etiqueta = 'grande'\n",
    "    print('%-28s d = %.4f (%s) | p = %.3g'\n",
    "          % (nombre, d, etiqueta, stats.ttest_ind(a, b, equal_var=False).pvalue))\n",
    "\n",
    "\n",
    "may = v.loc[v['segmento'] == 'Mayorista', 'compro'].values\n",
    "bod = v.loc[v['segmento'] == 'Bodega', 'compro'].values\n",
    "sat_si = v.loc[v['compro'] == 1, 'satisfaccion'].dropna().values\n",
    "sat_no = v.loc[v['compro'] == 0, 'satisfaccion'].dropna().values\n",
    "\n",
    "compara(may, bod, 'Mayorista vs Bodega')\n",
    "compara(sat_si, sat_no, 'satisfaccion, compro vs no')\n",
    "compara(horeca, minimarket, 'Horeca vs Minimarket')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora sí se pueden ordenar los hallazgos del libro 🏆\n",
    "\n",
    "Mira el segundo: la satisfacción de quien compra es **medio punto más\n",
    "alta** que la de quien no compra, con un p de 10 elevado a -21. Suena\n",
    "enorme. Y el tamaño del efecto es 0,3682, o sea chico: las dos distribuciones se\n",
    "solapan muchísimo.\n",
    "\n",
    "Y el tercero es el que más duele. Horeca contra Minimarket salió significativo\n",
    "en el capítulo 12, aguantó (por poco) la conversación del 11, y su efecto es\n",
    "**0,1283, insignificante**.\n",
    "\n",
    "Los cortes de Cohen (0,2, 0,5 y 0,8) son convenciones suyas y hay que usarlas\n",
    "con cabeza. Pero para ordenar tres hallazgos en una tabla, van perfectas 📋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La potencia, que es la pregunta de antes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hasta ahora todo era después de recoger datos. La potencia es la pregunta que\n",
    "había que hacerse *antes*: **si el efecto existiera, ¿con cuántos\n",
    "datos lo vería?**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "generador = np.random.default_rng(7)\n",
    "\n",
    "\n",
    "def potencia(p1, p2, n, repeticiones=2000):\n",
    "    \"\"\"De cuantos experimentos con este efecto real saldria p < 0.05.\"\"\"\n",
    "    aciertos = 0\n",
    "    for _ in range(repeticiones):\n",
    "        a = generador.binomial(1, p1, n)\n",
    "        b = generador.binomial(1, p2, n)\n",
    "        aciertos += stats.ttest_ind(a, b).pvalue < 0.05\n",
    "    return aciertos / repeticiones\n",
    "\n",
    "\n",
    "print('efecto real: 63.21% contra 56.93%')\n",
    "for n in [50, 100, 300, 800, 2000]:\n",
    "    print('  con %4d por grupo -> potencia %.3f' % (n, potencia(0.6321, 0.5693, n)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esto hay que leerlo despacio porque es incómodo 😬\n",
    "\n",
    "Con 100 casos por grupo, **y siendo el efecto real**, solo lo\n",
    "detectarías el 14,5% de las veces. O sea que 85 de cada 100 estudios honestos con\n",
    "ese tamaño concluirían \"no hay diferencia\".\n",
    "\n",
    "Con 300 sigues fallando 6 de cada 10 veces. Hacen falta unos 800 para llegar a\n",
    "la potencia del 80%, que es la convención de \"aceptable\".\n",
    "\n",
    "Y ahora date cuenta de una cosa: la comparación del capítulo 12 se hizo con\n",
    "742 y 801 casos. **Estaba justo en el filo.** Con esa muestra, si\n",
    "hubiera repetido el experimento, tres de cada diez veces no habría encontrado\n",
    "nada 🎲"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La fórmula, para no simular"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "n≈2(zα+zβ)2d2\n",
    "\n",
    "cuántos casos por grupo hacen falta, y como el efecto va al cuadrado abajo, la mitad de efecto cuesta cuatro veces más gente"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = d_de_cohen(horeca, minimarket)\n",
    "n_necesario = 2 * (1.96 + 0.84) ** 2 / d ** 2\n",
    "\n",
    "print('efecto observado: d = %.4f' % d)\n",
    "print('para potencia 0.80 hacen falta %.0f casos por grupo' % n_necesario)\n",
    "print()\n",
    "for objetivo, etiqueta in [(0.2, 'insignificante'), (0.5, 'chico'), (0.8, 'mediano')]:\n",
    "    print('para detectar un efecto de %.1f (%s): %.0f por grupo'\n",
    "          % (objetivo, etiqueta, 2 * (1.96 + 0.84) ** 2 / objetivo ** 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "952 por grupo, que cuadra con la simulación (800 daba 0,713) 🎯\n",
    "\n",
    "Y mira la segunda tabla, que es la que hay que tener a mano: para cazar un\n",
    "efecto mediano bastan **24 por grupo**. Para uno chico, 63. Para uno\n",
    "insignificante, 392.\n",
    "\n",
    "Ese 1,96 es el del 95% de siempre, y el 0,84 es el que corresponde a una\n",
    "potencia del 80%. Los dos se pueden cambiar si quieres otro nivel."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El problema al revés: cuando sobra potencia"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con muestras enormes pasa lo contrario, y también es un problema 🐘\n",
    "\n",
    "Ya lo vimos en el capítulo 5: la prueba de normalidad rechazaba cualquier\n",
    "columna en cuanto había 1.000 filas. La causa es esta misma."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for n in [100, 1000, 10000, 100000]:\n",
    "    a = generador.binomial(1, 0.500, n)\n",
    "    b = generador.binomial(1, 0.505, n)\n",
    "    pv = stats.ttest_ind(a, b).pvalue\n",
    "    print('n=%6d -> diferencia real de 0.5 puntos | p = %.4f %s'\n",
    "          % (n, pv, '<-- significativa' if pv < 0.05 else ''))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una diferencia de **medio punto porcentual**, que a ningún\n",
    "negocio del mundo le cambia una decisión, sale significativa con 100.000 casos\n",
    "por grupo.\n",
    "\n",
    "Por eso las plataformas grandes que hacen pruebas A/B con millones de usuarios\n",
    "no reportan valores p: reportan el efecto con su intervalo, y tienen un umbral\n",
    "de *relevancia práctica* decidido antes 📏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error del capítulo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    stats.binomtest(5, 3)\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "ValueError: k (5) must not be greater than n (3).\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cinco éxitos de tres intentos, imposible, y te frena 👍\n",
    "\n",
    "Y el silencioso de siempre, que en este capítulo es especialmente traicionero:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('varianza de un solo dato:', np.array([1.0]).var(ddof=1))\n",
    "print('d de Cohen con un dato:  ', d_de_cohen(np.array([1.0]), np.array([0.0, 1.0])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`nan`, sin aviso. Y en una tabla de resultados con veinte\n",
    "comparaciones, un `nan` perdido entre números se lee como \"no hubo\n",
    "efecto\" 😑\n",
    "\n",
    "La costumbre que salva: **pon el n al lado de cada efecto**. Si\n",
    "ves un `nan` con n = 1, ya sabes que no es un resultado."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cómo se reporta bien, en una línea"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Así no | Así sí |\n",
    "|---|---|\n",
    "| \"La diferencia es significativa (p < 0,05)\" | \"Horeca cierra 6,28 puntos más que Minimarket (IC 95%: 1,40 a 11,16; d = 0,13; n = 742 y 801)\" |\n",
    "| \"No hay diferencia entre ciudades\" | \"No encontramos diferencia entre ciudades; con esta muestra habríamos detectado efectos de 4 puntos o más\" |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La segunda columna se puede discutir. La primera solo se puede creer o no\n",
    "creer 🤝"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y dos más, de la función que escribimos arriba"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    d_de_cohen(horeca, minimarket, corregido=True)\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "TypeError: d_de_cohen() got an unexpected keyword argument 'corregido'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La función que escribimos recibe dos cosas y ya. Este error sale mucho trabajando con librerías: uno da por hecho que existe una opción porque sería razonable que existiera. La *g* de Hedges, que es la d corregida para muestras chicas, sí existe, y está en otra librería 🧰"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    d_de_cohen(horeca, [])\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "AttributeError: 'list' object has no attribute 'var'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y aquí el que revienta es un detalle del código: `d_de_cohen` llama a `.var()`, que tienen las Series de pandas y no las listas de Python. Es un recordatorio de que las funciones que escribes suponen cosas de lo que reciben, aunque no las digas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Practica 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Ordena todos los hallazgos del libro por tamaño\n",
    "\n",
    "Calcula la d de Cohen de las comparaciones que hemos ido\n",
    "haciendo y ponlas en orden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "comparaciones = [\n",
    "    ('Mayorista vs Bodega (compra)', may, bod),\n",
    "    ('Horeca vs Minimarket (compra)', horeca, minimarket),\n",
    "    ('satisfaccion segun compra', sat_si, sat_no),\n",
    "    ('monto Mayorista vs Bodega',\n",
    "     v.loc[v['segmento'] == 'Mayorista', 'monto'].values,\n",
    "     v.loc[v['segmento'] == 'Bodega', 'monto'].values),\n",
    "    ('compra Trujillo vs Piura',\n",
    "     v.loc[v['ciudad'] == 'trujillo', 'compro'].values,\n",
    "     v.loc[v['ciudad'] == 'piura', 'compro'].values),\n",
    "]\n",
    "\n",
    "for nombre, a, b in sorted(comparaciones,\n",
    "                           key=lambda c: -abs(d_de_cohen(c[1], c[2]))):\n",
    "    print('%-30s d = %+.4f | n = %d y %d' % (nombre, d_de_cohen(a, b), len(a), len(b)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "monto Mayorista vs Bodega      d = +3.2354 | n = 750 y 707\n",
    "Mayorista vs Bodega (compra)   d = +0.7498 | n = 750 y 707\n",
    "satisfaccion segun compra      d = +0.3682 | n = 1590 y 1179\n",
    "Horeca vs Minimarket (compra)  d = +0.1283 | n = 742 y 801\n",
    "compra Trujillo vs Piura       d = +0.0469 | n = 471 y 506\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está el libro entero ordenado por lo que de verdad importa 🥇\n",
    "\n",
    "El primero es apabullante: d = 3,2354. Un mayorista y una bodega no se\n",
    "parecen en nada en cuanto a monto, y eso ya lo veníamos viendo desde el\n",
    "capítulo 3.\n",
    "\n",
    "Y fíjate en el último: Trujillo contra Piura tiene d = 0,0469. Es\n",
    "prácticamente cero, y por eso su p era 0,4634. **Cuando el efecto es de\n",
    "verdad cero, la muestra chica no es la culpable**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. ¿Qué habría hecho falta para ver la diferencia entre ciudades?\n",
    "\n",
    "Trujillo cierra 2,32 puntos más que Piura y salió p =\n",
    "0,4634. ¿Cuántas ventas harían falta para que ese efecto, si es real, se\n",
    "detectara?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tru = v.loc[v['ciudad'] == 'trujillo', 'compro'].values\n",
    "piu = v.loc[v['ciudad'] == 'piura', 'compro'].values\n",
    "d_ciudad = d_de_cohen(tru, piu)\n",
    "\n",
    "print('diferencia: %.4f puntos' % (tru.mean() - piu.mean()))\n",
    "print('d de Cohen: %.4f' % d_ciudad)\n",
    "print('para potencia 0.80 harian falta %.0f por ciudad'\n",
    "      % (2 * (1.96 + 0.84) ** 2 / d_ciudad ** 2))\n",
    "print('tenemos: %d y %d' % (len(tru), len(piu)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "diferencia: 0.0232 puntos\n",
    "d de Cohen: 0.0469\n",
    "para potencia 0.80 harian falta 7114 por ciudad\n",
    "tenemos: 471 y 506\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "7.114 ventas por ciudad. Tenemos 471 y 506 😅\n",
    "\n",
    "O sea que con estos datos **no podríamos haber detectado ese efecto ni\n",
    "aunque fuera completamente real**. Quince veces más datos harían falta.\n",
    "\n",
    "Y eso cambia cómo se cuenta el resultado. No es \"las ciudades cierran igual\":\n",
    "es **\"si hay diferencia entre ciudades, es más chica de lo que esta\n",
    "muestra puede ver\"**.\n",
    "\n",
    "Esa frase es más larga y es la honesta. Además protege: si el año que viene\n",
    "alguien encuentra la diferencia con más datos, tu informe no queda desmentido 🛡️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. El efecto que se encoge al mirarlo mejor\n",
    "\n",
    "La satisfacción tenía d = 0,3682 respecto a la compra.\n",
    "Mira si ese efecto sobrevive dentro de cada segmento."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for seg, g in v.groupby('segmento'):\n",
    "    a = g.loc[g['compro'] == 1, 'satisfaccion'].dropna().values\n",
    "    b = g.loc[g['compro'] == 0, 'satisfaccion'].dropna().values\n",
    "    print('%-11s d = %+.4f | p = %.4g | n = %d y %d'\n",
    "          % (seg, d_de_cohen(a, b),\n",
    "             stats.ttest_ind(a, b, equal_var=False).pvalue, len(a), len(b)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Bodega      d = +0.4383 | p = 9.611e-08 | n = 245 y 410\n",
    "Horeca      d = +0.3460 | p = 1.404e-05 | n = 437 y 255\n",
    "Mayorista   d = +0.2871 | p = 0.0005704 | n = 489 y 195\n",
    "Minimarket  d = +0.4589 | p = 1.064e-09 | n = 419 y 319\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aguanta 💪 Los cuatro segmentos dan entre 0,3172 y 0,4315, muy cerca del\n",
    "0,3682 global.\n",
    "\n",
    "Y esto es importante porque es lo **contrario** de lo que pasó\n",
    "con la media en el capítulo 3, donde el resultado global era un artefacto de\n",
    "mezclar segmentos.\n",
    "\n",
    "Aquí no: el efecto está dentro de cada grupo, con el mismo tamaño. Eso lo\n",
    "hace mucho más creíble 🌟\n",
    "\n",
    "Comprobar si un hallazgo se repite en cada subgrupo es la forma más barata que\n",
    "conozco de saber si es de verdad. Y cuando no se repite, ya sabes que había una\n",
    "mezcla escondida."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Potencia para una prueba A/B de verdad\n",
    "\n",
    "Quieres probar un cambio en la web que suba la tasa de\n",
    "cierre de 57,77% a 60%. ¿Cuántos visitantes necesitas por variante?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "base = v['compro'].mean()\n",
    "\n",
    "for objetivo in [0.60, 0.62, 0.65]:\n",
    "    d_ab = 2 * (np.arcsin(np.sqrt(objetivo)) - np.arcsin(np.sqrt(base)))\n",
    "    n = 2 * (1.96 + 0.84) ** 2 / d_ab ** 2\n",
    "    print('de %.2f%% a %.0f%% (subida de %.2f puntos) -> %.0f por variante'\n",
    "          % (100 * base, 100 * objetivo, 100 * (objetivo - base), n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "de 57.77% a 60% (subida de 2.23 puntos) -> 7610 por variante\n",
    "de 57.77% a 62% (subida de 4.23 puntos) -> 2100 por variante\n",
    "de 57.77% a 65% (subida de 7.23 puntos) -> 709 por variante\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para detectar una mejora de 2,23 puntos hacen falta **7.610 visitantes\n",
    "por variante**, o sea más de 15.000 en total 😵\n",
    "\n",
    "Y esto es lo que hay que calcular *antes* de lanzar la prueba, no\n",
    "después. Si tu web tiene 500 visitas al mes, esa prueba dura dos años y medio, y\n",
    "más vale saberlo antes de empezar.\n",
    "\n",
    "Ese `arcsin` raro es la transformación estándar para tamaños de\n",
    "efecto de proporciones. Se llama h de Cohen y se usa igual que la d.\n",
    "\n",
    "La conclusión práctica de todo esto: **las mejoras chiquitas son\n",
    "carísimas de demostrar**. Por eso conviene probar cambios grandes 🎯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Cuánta gente hace falta para una encuesta de satisfacción\n",
    "\n",
    "Quieres saber si la satisfacción media subió de 3,0 a 3,2.\n",
    "Con la desviación que ya conoces, calcula el tamaño."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s = v['satisfaccion'].dropna()\n",
    "\n",
    "for mejora in [0.1, 0.2, 0.3, 0.5]:\n",
    "    d_enc = mejora / s.std()\n",
    "    n = 2 * (1.96 + 0.84) ** 2 / d_enc ** 2\n",
    "    print('para detectar +%.1f puntos (d = %.4f) -> %.0f respuestas por grupo'\n",
    "          % (mejora, d_enc, n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "para detectar +0.1 puntos (d = 0.0704) -> 3165 respuestas por grupo\n",
    "para detectar +0.2 puntos (d = 0.1408) -> 791 respuestas por grupo\n",
    "para detectar +0.3 puntos (d = 0.2112) -> 352 respuestas por grupo\n",
    "para detectar +0.5 puntos (d = 0.3519) -> 127 respuestas por grupo\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para ver una mejora de dos décimas hacen falta 791 respuestas por grupo 📋\n",
    "\n",
    "Y ahí tienes por qué casi todas las encuestas internas de satisfacción no\n",
    "sirven para nada: se mandan a cincuenta personas, vuelven treinta, y con eso solo\n",
    "se detectaría una mejora de medio punto largo.\n",
    "\n",
    "Fíjate en la pieza que manda: la desviación de la satisfacción es 1,42, que es\n",
    "enorme para una escala de 1 a 5. Cuanto más dispersas las respuestas, más gente\n",
    "hace falta. Y con esa dispersión, incluso media escala entera de mejora (+0,5)\n",
    "pide 127 respuestas por grupo.\n",
    "\n",
    "Y con la escala de 1 a 5 no puedes hacer nada, pero sí puedes preguntar\n",
    "mejor: una pregunta que la gente entienda igual reduce la dispersión y con ella\n",
    "el tamaño necesario 🎤"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Tu informe completo, en una función\n",
    "\n",
    "Junta valor p, tamaño del efecto, intervalo y n en una sola\n",
    "salida que se pueda pegar en un correo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def informe(a, b, nombre_a, nombre_b, unidad=''):\n",
    "    d = d_de_cohen(a, b)\n",
    "    dif = a.mean() - b.mean()\n",
    "    ee = np.sqrt(a.var(ddof=1) / len(a) + b.var(ddof=1) / len(b))\n",
    "    pv = stats.ttest_ind(a, b, equal_var=False).pvalue\n",
    "    juicio = 'insignificante' if abs(d) < 0.2 else (\n",
    "        'chico' if abs(d) < 0.5 else ('mediano' if abs(d) < 0.8 else 'grande'))\n",
    "    return ('%s supera a %s en %.4f%s (IC 95%%: %.4f a %.4f), '\n",
    "            'efecto %s (d = %.2f), p = %.3g, n = %d y %d'\n",
    "            % (nombre_a, nombre_b, dif, unidad,\n",
    "               dif - 1.96 * ee, dif + 1.96 * ee, juicio, d, pv, len(a), len(b)))\n",
    "\n",
    "\n",
    "print(informe(may, bod, 'Mayorista', 'Bodega'))\n",
    "print()\n",
    "print(informe(horeca, minimarket, 'Horeca', 'Minimarket'))\n",
    "print()\n",
    "print(informe(v.loc[v['ciudad'] == 'trujillo', 'compro'].values,\n",
    "              v.loc[v['ciudad'] == 'piura', 'compro'].values,\n",
    "              'Trujillo', 'Piura'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "Mayorista supera a Bodega en 0.3492 (IC 95%: 0.3012 a 0.3971), efecto mediano (d = 0.75), p = 2.73e-43, n = 750 y 707\n",
    "\n",
    "Horeca supera a Minimarket en 0.0628 (IC 95%: 0.0140 a 0.1116), efecto insignificante (d = 0.13), p = 0.0118, n = 742 y 801\n",
    "\n",
    "Trujillo supera a Piura en 0.0232 (IC 95%: -0.0388 a 0.0851), efecto insignificante (d = 0.05), p = 0.463, n = 471 y 506\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres frases, y cada una se puede discutir con datos 🙌\n",
    "\n",
    "Fíjate en la tercera: el intervalo va de -0,0388 a +0,0851, o sea que\n",
    "**contiene el cero**. Eso es exactamente lo mismo que dice el p de\n",
    "0,463, pero contado de forma que además te dice cuánto podría ser el efecto como\n",
    "mucho: 8,5 puntos por arriba en el peor de los casos.\n",
    "\n",
    "Esta función es lo que yo pegaría en un correo. Tiene las cuatro cosas: el\n",
    "tamaño en unidades de negocio, la incertidumbre, el juicio sobre si importa, y el\n",
    "número de casos 📧"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. La d de las cuatro categóricas\n",
    "\n",
    "Calcula la d de Cohen entre el mejor y el peor grupo de cada columna."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for col in ['ciudad', 'canal', 'categoria', 'segmento']:\n",
    "    t = v.groupby(col)['monto']\n",
    "    peor, mejor = t.mean().idxmin(), t.mean().idxmax()\n",
    "    d2 = d_de_cohen(v[v[col] == mejor]['monto'], v[v[col] == peor]['monto'])\n",
    "    print('%-11s d = %6.3f   (%s contra %s)' % (col, d2, mejor, peor))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ciudad      d =  0.081   (trujillo contra cusco)\n",
    "canal       d =  0.034   (Tienda contra WhatsApp)\n",
    "categoria   d =  0.117   (Abarrotes contra Cuidado personal)\n",
    "segmento    d =  3.235   (Mayorista contra Bodega)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tres por debajo de 0,2 y uno en 3,235 😱\n",
    "\n",
    "La tabla de Cohen dice que 0,8 ya es un efecto grande, así que un 3,235 se sale de la escala. Y las otras tres están por debajo de *insignificante*, aunque las cuatro comparan al mejor contra el peor.\n",
    "\n",
    "Esto es lo que hay que llevar a una reunión en vez de cuatro valores p: **una sola de las cuatro columnas importa, y por un margen ridículo**. Con la d se ve de un vistazo y con el p no 📊"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. Copia los datos y mira las dos columnas\n",
    "\n",
    "Duplica los datos 1, 2, 5 y 10 veces y mira qué le pasa al valor p y qué le pasa a la d."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for veces in [1, 2, 5, 10]:\n",
    "    a2 = np.tile(horeca, veces)\n",
    "    b2 = np.tile(minimarket, veces)\n",
    "    print('copiando los datos %2d veces: p = %.3e   d = %.4f'\n",
    "          % (veces, stats.ttest_ind(a2, b2).pvalue, d_de_cohen(a2, b2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "copiando los datos  1 veces: p = 1.189e-02   d = 0.1283\n",
    "copiando los datos  2 veces: p = 3.727e-04   d = 0.1284\n",
    "copiando los datos  5 veces: p = 1.821e-08   d = 0.1284\n",
    "copiando los datos 10 veces: p = 1.719e-15   d = 0.1284\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El valor p se derrumba trece órdenes de magnitud y la d no se mueve del cuarto decimal 🤯\n",
    "\n",
    "Y aquí no hay ni un dato nuevo: son **las mismas filas copiadas**. Cero información añadida. El valor p se lo cree igual porque solo mira cuántas filas hay.\n",
    "\n",
    "Esta celda es la demostración más limpia que conozco de por qué el tamaño del efecto es obligatorio en un informe. Y si alguna vez ves un p imposible en un estudio con pocos sujetos, esto es lo primero que hay que sospechar 🚩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Significativo, y ocho décimas de punto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y ahora la trampa que le da título a este capítulo, con números de la base 🤏"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Sale significativo. Con eso en la mano propones cambiar la política de descuentos de un segmento entero.\n",
    "\n",
    "```\n",
    "from scipy import stats\n",
    "\n",
    "# descuento medio\n",
    "# Bodega     0.130   (n = 567)\n",
    "# Mayorista  0.122   (n = 607)\n",
    "\n",
    "print(round(stats.ttest_ind(bodega, mayorista,\n",
    "                            equal_var=False).pvalue, 4))\n",
    "# 0.0449\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Significativo, sí. Y la diferencia son **ocho décimas de punto de descuento** 🤏\n",
    "\n",
    "En un pedido de 500 soles eso son cuatro soles. Nadie va a cambiar una política por cuatro soles, y nadie del negocio va a notar la diferencia si la cambias.\n",
    "\n",
    "El valor p contesta una sola pregunta: **¿esta diferencia podría ser casualidad?**. No contesta si es grande, ni si conviene, ni si vale la pena. Y como el p baja al crecer la muestra, con datos suficientes cualquier diferencia por chiquita que sea termina saliendo significativa.\n",
    "\n",
    "Por eso el p nunca va solo. Al lado va el tamaño del efecto (aquí una d de Cohen de 0,117, de las que se consideran despreciables) y va la diferencia **en las unidades del negocio**: soles, pedidos, clientes. Un número que la gerencia pueda pesar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Copiando los mismos datos diez veces, el valor p baja de 0,0118 a 1,6e-15 y la d de Cohen se queda en 0,1284. ¿Qué demuestra?\n",
    "\n",
    "a) Que el valor p mide evidencia y el tamaño del efecto mide importancia\n",
    "\n",
    "b) Que copiar datos mejora el modelo\n",
    "\n",
    "c) Que la d de Cohen no es sensible\n",
    "\n",
    "d) Que hay que usar siempre muestras grandes\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* No hay ni un dato nuevo. Piensa en qué cambió y qué no.\n",
    "\n",
    "*c)* Justamente por eso sirve: no debería cambiar si el efecto es el mismo.\n",
    "\n",
    "*d)* Con muestras grandes todo sale significativo, incluso lo que no importa.\n",
    "\n",
    "Significativo no quiere decir importante."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 💥 Copiando los mismos datos diez veces, el p pasa de 0,0118 a 1,6e-15 y el\n",
    "efecto se queda en 0,1284. El p mide evidencia, no importancia.\n",
    "\n",
    "- 📏 La d de Cohen mide en desviaciones y no depende del tamaño de muestra.\n",
    "Menos de 0,2 insignificante, 0,5 chico, 0,8 mediano.\n",
    "\n",
    "- 🔦 La potencia es la pregunta de antes. Con 100 por grupo, un efecto real de\n",
    "6,28 puntos se detecta el 14,5% de las veces.\n",
    "\n",
    "- ⚖️ El estudio del capítulo 12 tenía 742 y 801 casos, o sea potencia de\n",
    "alrededor del 70%: tres de cada diez veces no habría encontrado nada.\n",
    "\n",
    "- 🐘 Con muestras enormes todo sale significativo. Medio punto porcentual de\n",
    "diferencia sale con p = 0,0304 usando 100.000 por grupo.\n",
    "\n",
    "- 🧮 Para un efecto mediano bastan 25 por grupo; para uno insignificante,\n",
    "392. Las mejoras chiquitas son carísimas de demostrar.\n",
    "\n",
    "- 📧 Se reporta efecto, intervalo, p y n. Los cuatro, siempre.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Significativo quiere decir \"probablemente no es casualidad\". No quiere\n",
    "decir \"importa\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y traducir un efecto a soles, que es la parte que casi nadie enseña, es\n",
    "exactamente lo que practicamos sobre los datos de cada quien en el\n",
    "[Full Day IA](https://missyera.com/cursos/full-day-ia/) 💜"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué viene ahora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 15 vamos a la relación entre dos variables numéricas: la\n",
    "correlación. Con la frase que hay que decir en voz alta cada vez que aparece\n",
    "una, y con una correlación de este archivo que resultó ser exactamente cero 📉"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 14 de **Estadística desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/estadistica-desde-cero/tamano-del-efecto/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
