La lliçó anterior va acabar amb una pregunta incòmoda: la correlació de 0,35 entre inactivitat i churn, és real o és un miratge de la mostra? La inferència estadística és la disciplina que respon aquest tipus de preguntes: com passar del que veiem en una mostra a afirmacions fiables sobre la població (distinció que vam definir a 02-01), quantificant la incertesa a cada pas. En aquesta lliçó aprendràs a estimar amb intervals de confiança, entendràs el teorema central del límit mitjançant simulació, i faràs contrastos d'hipòtesis (t-test i khi quadrat) amb scipy, aplicant-los al test A/B de la nova pàgina de MercaFresh. Aquestes eines t'acompanyaran durant tot el curs: comparar dos models de ML és, en el fons, comparar dues mostres de resultats.

Contingut

  1. Estimació puntual i per intervals
  2. El teorema central del límit (amb simulació)
  3. Contrastos d'hipòtesis: la lògica
  4. p-valor i errors tipus I i II
  5. El t-test a la pràctica: test A/B de MercaFresh
  6. El test khi quadrat per a variables categòriques
  7. Connexió amb Machine Learning

Estimació puntual i per intervals

Una estimació puntual resumeix un paràmetre poblacional amb un únic nombre calculat de la mostra: "la despesa mitjana per comanda és de 47,20 €". És útil però enganyosament precisa: una altra mostra hauria donat 46,80 o 47,90.

Una estimació per interval hi afegeix la incertesa: "la despesa mitjana és entre 45,90 i 48,50 € amb un 95 % de confiança". Aquest rang és l'interval de confiança (IC).

La peça clau és l'error estàndard (SE): la desviació estàndard de l'estimador, no de les dades. Per a la mitjana mostral:

SE = s / √n

on s és la desviació estàndard mostral i n la mida de la mostra. Fixa't en el √n: per reduir l'error a la meitat calen quatre vegades més dades. L'IC al 95 % per a la mitjana (amb n raonablement gran) és aproximadament:

x̄ ± 1,96 · SE

import numpy as np
from scipy import stats

rng = np.random.default_rng(42)

# Mostra: imports de 400 comandes de MercaFresh
imports_comanda = rng.gamma(shape=4.0, scale=12.0, size=400)

mitjana = imports_comanda.mean()
se = imports_comanda.std(ddof=1) / np.sqrt(len(imports_comanda))

# IC al 95% amb la distribucio t de Student (correcta per a qualsevol n)
ic = stats.t.interval(0.95, df=len(imports_comanda) - 1, loc=mitjana, scale=se)
print(f"Mitjana mostral: {mitjana:.2f} EUR")
print(f"IC 95%: ({ic[0]:.2f}, {ic[1]:.2f}) EUR")

Sortida aproximada:

Mitjana mostral: 48.06 EUR
IC 95%: (45.72, 50.41) EUR
  • std(ddof=1) força la desviació mostral (n−1) perquè imports_comanda és un array de NumPy (recorda la diferència de valors per defecte vista a 02-01).
  • stats.t.interval fa servir la distribució t de Student, una campana amb cues una mica més pesades que la normal que corregeix la incertesa extra d'estimar s; amb n = 400 és gairebé idèntica a fer servir ±1,96.

Interpretació correcta del 95 %: si repetíssim el mostreig moltes vegades i calculéssim un IC cada vegada, el 95 % d'aquests intervals contindria la veritable mitjana poblacional. No és exactament "hi ha un 95 % de probabilitat que μ sigui dins aquest interval" (μ és fixa; l'aleatori és l'interval), tot i que a la pràctica es llegeixi així de manera informal.

El teorema central del límit (amb simulació)

Per què podem fer servir campanes (normal o t) per a la mitjana, si els imports de MercaFresh són asimètrics amb cua dreta? Pel resultat més important de l'estadística:

Teorema central del límit (TCL): la distribució de la mitjana de n observacions independents s'aproxima a una normal quan n creix, sigui quina sigui la distribució original de les dades (mentre tingui variància finita). El seu centre és μ i la seva desviació és σ/√n.

En lloc de demostrar-ho, vegem-ho:

import matplotlib.pyplot as plt

rng = np.random.default_rng(0)

# Poblacio MOLT asimetrica: imports exponencials amb mitjana 40 EUR
poblacio = lambda size: rng.exponential(scale=40, size=size)

fig, axes = plt.subplots(1, 4, figsize=(14, 3.2), sharey=False)

# Panell 0: la distribucio original (res de campana)
axes[0].hist(poblacio(10_000), bins=60, color="gray")
axes[0].set_title("Dades originals\n(exponencial)")

# Panells 1-3: distribucio de la MITJANA de mostres de mida n
for ax, n in zip(axes[1:], [5, 30, 200]):
    mitjanes = np.array([poblacio(n).mean() for _ in range(5_000)])
    ax.hist(mitjanes, bins=60, color="steelblue")
    ax.set_title(f"Mitjanes de mostres\nn = {n}")

plt.tight_layout()
plt.show()

Què fa el codi: extreu 5.000 mostres de mida n d'una població exponencial (asimètrica total) i dibuixa l'histograma de les seves 5.000 mitjanes. El resultat:

  • Amb n = 5, l'histograma de mitjanes encara hereta asimetria.
  • Amb n = 30, ja és una campana força decent.
  • Amb n = 200, és una normal de llibre, molt més estreta (efecte σ/√n).

Aquest és el fonament que legitima els intervals de confiança i els tests d'aquesta lliçó fins i tot amb dades no normals, sempre que n no sigui minúscula. També explica el paper estel·lar de la distribució normal anunciat a 02-02.

Contrastos d'hipòtesis: la lògica

Un contrast d'hipòtesis és un procediment per decidir, amb regles explícites, si les dades aporten prou evidència contra una afirmació per defecte.

  • Hipòtesi nul·la (H₀): l'afirmació "avorrida", de no-efecte. "La nova pàgina de producte no canvia la despesa mitjana."
  • Hipòtesi alternativa (H₁): el que sospitem. "La nova pàgina canvia la despesa mitjana."

La lògica és anàloga a un judici: H₀ és innocent fins que es demostri el contrari. Calculem com d'estranyes serien les nostres dades si H₀ fos certa; si són prou estranyes, rebutgem H₀.

graph TD
    A["Formular H0 i H1"] --> B["Triar test i nivell alfa (p.ex. 0.05)"]
    B --> C["Recollir dades i calcular estadistic"]
    C --> D["Calcular p-valor"]
    D --> E{"p-valor < alfa?"}
    E -- "Si" --> F["Rebutjar H0:<br>efecte estadisticament significatiu"]
    E -- "No" --> G["No rebutjar H0:<br>evidencia insuficient"]

Important: "no rebutjar H₀" no és "demostrar H₀". L'absència d'evidència no és evidència d'absència; potser l'efecte existeix però la mostra és petita.

p-valor i errors tipus I i II

El p-valor és la probabilitat d'observar dades tan extremes com les nostres (o més) suposant que H₀ és certa. Un p-valor de 0,03 significa: "si la nova pàgina no tingués cap efecte, només en un 3 % dels experiments veuríem una diferència com aquesta per pur atzar".

El que el p-valor no és:

  • No és la probabilitat que H₀ sigui certa.
  • No mesura la mida de l'efecte: amb una n gegant, una diferència irrisòria de 0,02 € pot donar p < 0,001.

El llindar α (típicament 0,05) fixa quant de risc de falsa alarma acceptem, i d'aquí surten els dos errors possibles:

H₀ és certa (no hi ha efecte) H₀ és falsa (hi ha efecte)
Rebutgem H₀ Error tipus I (falsa alarma), prob. α Encert (potència del test)
No rebutgem H₀ Encert Error tipus II (efecte no detectat), prob. β

A MercaFresh: un error tipus I seria redissenyar tota la web per una millora que no existia; un error tipus II, descartar una pàgina que realment venia més. Abaixar α redueix les falses alarmes però augmenta els efectes perduts: és un compromís, no una veritat revelada. (Aquesta mateixa tensió reapareixerà al mòdul 6 com l'equilibri entre falsos positius i falsos negatius d'un classificador.)

El t-test a la pràctica: test A/B de MercaFresh

Cas: MercaFresh llança una nova pàgina de producte i vol saber si va millorar la despesa mitjana per comanda. Durant dues setmanes, la meitat dels visitants veu la pàgina antiga (grup A, control) i l'altra meitat la nova (grup B, tractament), assignats a l'atzar. L'assignació aleatòria és la clau que permet parlar de causalitat, tancant el cercle de "correlació no és causalitat" de 02-03.

El t-test de dues mostres independents contrasta H₀: "les mitjanes de tots dos grups són iguals".

rng = np.random.default_rng(7)

# Simulem l'experiment: la pagina nova apuja la despesa mitjana ~2 EUR
grup_A = rng.gamma(shape=4.0, scale=12.0, size=980)           # mitjana ~48 EUR
grup_B = rng.gamma(shape=4.0, scale=12.5, size=1020)          # mitjana ~50 EUR

print(f"Mitjana A: {grup_A.mean():.2f} | Mitjana B: {grup_B.mean():.2f}")
print(f"Diferencia observada: {grup_B.mean() - grup_A.mean():.2f} EUR")

# t-test de Welch (equal_var=False): no assumeix variancies iguals
t_stat, p_valor = stats.ttest_ind(grup_B, grup_A, equal_var=False)
print(f"t = {t_stat:.2f}, p-valor = {p_valor:.4f}")

Sortida aproximada:

Mitjana A: 47.66 | Mitjana B: 50.16
Diferencia observada: 2.50 EUR
t = 2.32, p-valor = 0.0203

Interpretació pas a pas:

  1. La diferència observada és d'uns 2,50 € per comanda a favor de la nova pàgina.
  2. El p-valor ≈ 0,02 < 0,05: si la pàgina nova no tingués efecte, una diferència així només apareixeria en ~2 % dels experiments. Rebutgem H₀: la millora és estadísticament significativa.
  3. Decisió de negoci: significativa i rellevant (2,50 € × milers de comandes/mes justifica el canvi). Comprova sempre totes dues coses: significació estadística i mida de l'efecte.

Notes tècniques:

  • stats.ttest_ind implementa el t-test per a mostres independents; equal_var=False (test de Welch) és l'opció segura per defecte perquè no exigeix variàncies iguals entre grups.
  • Dades asimètriques com aquestes? El TCL ens protegeix: amb ~1.000 observacions per grup, les mitjanes es comporten com a normals.
  • També existeix stats.ttest_rel per a mostres aparellades (el mateix client mesurat abans i després) i stats.ttest_1samp per comparar una mostra contra un valor fix ("el temps mitjà de lliurament supera els 40 min promesos?").

El test khi quadrat per a variables categòriques

El t-test compara mitjanes de variables numèriques. Quan totes dues variables són categòriques, el test adequat és el khi quadrat d'independència: contrasta H₀: "les dues variables són independents" a partir de la seva taula de contingència.

Cas MercaFresh: la taxa de conversió (comprar o no) depèn de la versió de la pàgina?

import pandas as pd

# Taula de contingencia observada del test A/B
#                 compra   no_compra
taula = pd.DataFrame({"compra": [312, 368],
                      "no_compra": [2688, 2632]},
                     index=["pagina_A", "pagina_B"])
print(taula)
print(f"Conversio A: {312/3000:.1%} | Conversio B: {368/3000:.1%}")

chi2, p_valor, gl, esperats = stats.chi2_contingency(taula)
print(f"chi2 = {chi2:.2f}, p-valor = {p_valor:.4f}, graus de llibertat = {gl}")

Sortida:

          compra  no_compra
pagina_A     312       2688
pagina_B     368       2632
Conversio A: 10.4% | Conversio B: 12.3%
chi2 = 5.15, p-valor = 0.0232, graus de llibertat = 1

Com funciona per dins (intuïció): el test calcula quina taula esperaríem si hi hagués independència (les mateixes proporcions a totes dues files: esperats la retorna) i mesura quant es desvien els recomptes observats dels esperats. Desviació gran → khi² gran → p-valor petit.

Aquí p ≈ 0,023 < 0,05: la conversió sí que depèn de la pàgina; la versió B converteix significativament més (12,3 % vs. 10,4 %). El khi quadrat també serveix per a preguntes com "el mètode de pagament depèn de la província?" o "el churn depèn del nivell de fidelitat?".

Pregunta Variables Test
Difereix la despesa mitjana entre dos grups? Numèrica vs. binària t-test de dues mostres
Va canviar la mitjana després d'una intervenció (mateixos subjectes)? Numèrica, aparellada t-test aparellat
La mitjana supera un valor de referència? Numèrica vs. constant t-test d'una mostra
Dues categòriques són independents? Categòrica vs. categòrica Khi quadrat

Connexió amb Machine Learning

La inferència impregna la pràctica del ML més del que sembla:

  • Comparar models. "El model nou encerta el 84,1 % i el vell el 83,6 %": millora real o soroll de la mostra d'avaluació? La diferència entre dues accuracies és una diferència entre dues proporcions mostrals, exactament el terreny d'aquesta lliçó. Al mòdul 6 veurem com la validació creuada (06-03) genera diverses mesures per model, cosa que permet raonar sobre la variabilitat del rendiment en lloc de refiar-se d'un únic nombre.
  • Tests A/B de models en producció. Desplegar el model nou per al 50 % del trànsit i comparar mètriques de negoci és literalment l'experiment d'aquesta lliçó; ho reprendrem al mòdul 8.
  • Escepticisme quantificat. L'hàbit mental del p-valor —"podria ser atzar, això?"— és la millor vacuna contra conclusions precipitades en explorar dades, incloses les correlacions "prometedores" de la lliçó anterior.

Errors Comuns i Consells

  • Interpretar el p-valor com a P(H₀ certa). És P(dades així d'extremes | H₀ certa): condiciona en la direcció contrària. Aquesta distinció connecta directament amb la propera lliçó (Bayes).
  • Confondre significació amb rellevància. Amb una n enorme, tot és "significatiu". Reporta sempre la mida de l'efecte (la diferència en euros, en punts de conversió) juntament amb el p-valor.
  • Mirar els resultats cada dia i aturar el test quan p < 0,05. Aquest "peeking" infla dràsticament els falsos positius: fixa la durada o la mida mostral abans de començar.
  • Fer molts tests i quedar-se amb els que surten. Amb 20 tests a α = 0,05, el que cal esperar és ~1 fals positiu. Si fas comparacions múltiples, ajusta-ho (p. ex., correcció de Bonferroni: fer servir α/20) o com a mínim declara-ho.
  • Fer servir el t-test amb mostres minúscules i molt asimètriques. El TCL necessita una n suficient; amb n < 20-30 i cues fortes, desconfia (existeixen tests no paramètrics com Mann-Whitney, stats.mannwhitneyu, com a alternativa).
  • Oblidar l'assignació aleatòria en un A/B. Si el grup B són "els usuaris de l'app nova", ja no compares pàgines, compares tipus d'usuari: el confusor de 02-03 en acció.
  • Consell: abans de llançar un experiment, escriu H₀, H₁, α i la mida de mostra en un document. Els experiments definits a posteriori troben el que volen trobar.

Exercicis

Exercici 1

Amb una mostra de 100 temps de lliurament de MercaFresh (mitjana mostral 41,3 min, desviació estàndard mostral 9,8 min), calcula a mà l'error estàndard i un IC del 95 % aproximat per a la mitjana (fes servir ±1,96·SE). MercaFresh promet "lliurament mitjà per sota de 40 min": l'interval és compatible amb aquesta promesa?

Exercici 2

Simula amb NumPy el TCL per a una distribució uniforme entre 0 i 10: dibuixa l'histograma de les mitjanes de 5.000 mostres de mida n = 2 i n = 50. Què observes en la forma i en l'amplada? Quina amplada teòrica prediu el TCL per a n = 50? (σ d'una uniforme(0,10) ≈ 2,89).

Exercici 3

MercaFresh prova dos assumptes d'email per reactivar clients inactius. Assumpte A: 1.500 enviaments, 129 reactivacions. Assumpte B: 1.500 enviaments, 168 reactivacions. Planteja H₀ i H₁, construeix la taula de contingència, executa el khi quadrat amb scipy i conclou amb α = 0,05.

Solucions

Solució 1

  • SE = 9,8 / √100 = 0,98 min.
  • IC 95 % ≈ 41,3 ± 1,96 · 0,98 = 41,3 ± 1,92 → (39,4, 43,2) minuts.
  • L'interval conté valors per sota de 40, així que no podem descartar que la mitjana real compleixi la promesa... però la major part de l'interval és per sobre de 40 i l'estimació puntual també. Formalment: un t-test d'una mostra contra 40 no rebutjaria H₀ al 5 % (l'evidència d'incompliment no és concloent), tot i que operativament MercaFresh faria bé de vigilar el repartiment.

Solució 2

import numpy as np, matplotlib.pyplot as plt
rng = np.random.default_rng(3)
fig, axes = plt.subplots(1, 2, figsize=(9, 3))
for ax, n in zip(axes, [2, 50]):
    mitjanes = rng.uniform(0, 10, size=(5_000, n)).mean(axis=1)
    ax.hist(mitjanes, bins=50, color="steelblue")
    ax.set_title(f"n = {n}  (std = {mitjanes.std():.2f})")
plt.tight_layout(); plt.show()

Amb n = 2 la distribució de mitjanes és triangular (encara no normal); amb n = 50 és una campana clara i molt més estreta. El TCL prediu una desviació σ/√n = 2,89/√50 ≈ 0,41, que coincidirà amb la std empírica del panell dret. Truc del codi: generar una matriu 5.000×n i fer la mitjana per files (mean(axis=1)) evita el bucle.

Solució 3

  • H₀: la taxa de reactivació és independent de l'assumpte (mateixa taxa a A i B). H₁: les taxes difereixen.
import pandas as pd
from scipy import stats
taula = pd.DataFrame({"reactiva": [129, 168],
                      "no_reactiva": [1371, 1332]},
                     index=["assumpte_A", "assumpte_B"])
chi2, p, gl, esp = stats.chi2_contingency(taula)
print(f"Taxa A: {129/1500:.1%} | Taxa B: {168/1500:.1%}")
print(f"chi2 = {chi2:.2f}, p = {p:.4f}")

Resultat: taxes del 8,6 % vs. 11,2 %, khi² ≈ 5,4, p ≈ 0,020 < 0,05. Rebutgem H₀: l'assumpte B reactiva significativament més clients. Amb una mida d'efecte de +2,6 punts percentuals sobre 1.500 enviaments, a més de significatiu és accionable: MercaFresh hauria d'adoptar l'assumpte B (idealment confirmant-ho sense "peeking" en una segona onada).

Conclusió

Has recorregut el nucli de la inferència estadística: estimar amb intervals de confiança en lloc de nombres secs, entendre per què el teorema central del límit fa que les mitjanes es comportin com a normals, i executar contrastos d'hipòtesis complets —t-test per a mitjanes i khi quadrat per a categòriques— interpretant p-valors i vigilant els errors tipus I i II. El test A/B de MercaFresh ha mostrat el circuit complet, de la hipòtesi a la decisió de negoci, i has deixat anotada la connexió amb el ML: comparar models és comparar mostres. Però la inferència clàssica deixa una pregunta sense respondre de cara: com actualitzar el que ja crèiem en rebre evidència nova? Aquesta és exactament l'especialitat del teorema de Bayes, amb el qual tancarem el mòdul a la propera lliçó.

Curs de Machine Learning

Mòdul 1: Introducció al Machine Learning

Mòdul 2: Fonaments d'Estadística i Probabilitat

Mòdul 3: Preprocessament de Dades

Mòdul 4: Algorismes de Machine Learning Supervisat

Mòdul 5: Algorismes de Machine Learning No Supervisat

Mòdul 6: Avaluació i Validació de Models

Mòdul 7: Tècniques Avançades i Optimització

Mòdul 8: Implementació i Desplegament de Models

Mòdul 9: Projectes Pràctics

Mòdul 10: Recursos Addicionals

© Copyright 2026. Tots els drets reservats