Código de correlación comentado

import pandas as pd

# Creamos una tabla (DataFrame) con 5 alumnos.
# Cada fila es un alumno: cuántas horas estudió y qué nota sacó.
# Es como una hoja de Excel con dos columnas: 'horas' y 'nota'.
df = pd.DataFrame({
'horas': [2, 4, 5, 6, 8],
'nota': [4, 5, 6, 7, 9]
})

# .cov() calcula la covarianza: un número que indica si dos variables se mueven juntas.
# Si es positiva (como aquí, 4.25), cuando una sube la otra también tiende a subir.
# El problema es que su valor depende de las unidades, así que no podemos saber
# si 4.25 es "mucho" o "poco" sin más contexto.
cov = df['horas'].cov(df['nota'])
print(cov) # → 4.25 (positiva: suben juntas)

# .corr() calcula el coeficiente de correlación de Pearson r, que siempre vale entre −1 y +1.
# Un valor de 0.988 significa una relación positiva casi perfecta:
# casi todos los alumnos que estudian más sacan mejor nota.
# A diferencia de la covarianza, este número sí es fácil de interpretar sin importar las unidades.
r = df["horas"].corr(df["nota"])
print(r)  # → 0.988  (correlación positiva muy fuerte)

# Importamos el submódulo stats de la librería scipy, especializada en estadística.
# Nos dará acceso a pruebas estadísticas más completas que las de pandas.
from scipy import stats

# stats.pearsonr() devuelve dos cosas a la vez:
#   r → el coeficiente de correlación (el mismo que antes, entre −1 y +1)
#   p → el p-valor: responde a "¿podría esta correlación ser pura casualidad?"
#        Si p < 0.05 consideramos que la relación es estadísticamente significativa.
# La notación f"r = {r:.3f}" es una f-string: imprime el valor de r con 3 decimales.
r, p = stats.pearsonr(df["horas"], df["nota"])
print(f"r = {r:.3f}")
print(f"p = {p:.4f}")

# ── Ejemplo 1: temperatura y consumo de energía ──────────────────────────────
# Datos: Temperatura (°C) y consumo de energía (kWh)
temperaturas = [20, 22, 24, 26, 28, 30, 32, 34, 36, 38]
consumo_energia = [200, 220, 250, 300, 350, 400, 450, 500, 600, 700]

# Se crean dos listas simples de Python (sin DataFrame esta vez) y se les aplica
# directamente pearsonr(). Se espera una correlación positiva fuerte: al subir
# la temperatura, el consumo de aire acondicionado también aumenta.
# Los resultados se guardan con nombres descriptivos para el print.
corr_temp, p_temp=stats.pearsonr(temperaturas, consumo_energia)
print(f"correlación temperatura y consumo = {corr_temp:.3f}")
print(f"p = {p_temp:.4f}")

# ── Ejemplo 2: televisión y lectura (correlación negativa) ───────────────────
# Datos: Horas de televisión y horas de lectura
horas_television = [5, 4, 6, 7, 8, 4, 3, 5, 2, 1]
horas_lectura = [1, 2, 1, 0, 0, 2, 3, 1, 4, 5]

# Ejemplo de correlación negativa: quien pasa más horas viendo televisión
# tiende a leer menos. El r resultante será cercano a −1.
# El código es idéntico al bloque anterior: lo único que cambia son los datos
# y los nombres de las variables. Ese es el patrón que se repite en todos los ejemplos.
corr_tel_lect, p_tel_lect=stats.pearsonr(horas_television, horas_lectura)
print(f"correlación televisión y lectura = {corr_tel_lect:.3f}")
print(f"p = {p_tel_lect:.4f}")

# ── Ejemplo 3: helados y lluvia (correlación débil) ──────────────────────────
# Datos: Helados vendidos y días lluviosos
helados_vendidos = [100, 150, 120, 130, 160, 140, 110, 180, 200, 190]
dias_lluviosos = [2, 3, 1, 4, 5, 2, 3, 1, 2, 4]

# Ejemplo de correlación débil o casi nula: los helados y los días de lluvia
# no tienen por qué estar relacionados. Es un buen contraste con los ejemplos anteriores.
# Si el p-valor sale alto (p > 0.05), no hay evidencia estadística de ninguna relación.
corr_hel_lluv, p_hel_lluv=stats.pearsonr(helados_vendidos, dias_lluviosos)
print(f"correlación helados y lluvia = {corr_hel_lluv:.3f}")
print(f"p = {p_hel_lluv:.4f}")

# ── Ejemplo 4: alcohol y rendimiento académico ───────────────────────────────
consumo_alcohol = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
rendimiento_academico = [90, 88, 85, 83, 80, 75, 70, 65, 60, 50]

# Correlación negativa fuerte y perfectamente lineal: los datos están construidos
# para que cada unidad de alcohol se corresponda con una bajada constante en la nota.
# Obtendremos un r muy cercano a −1 y un p-valor muy pequeño.
# Ojo: correlación no implica causalidad.
corr_alco_acad, p_alco_acad=stats.pearsonr(consumo_alcohol, rendimiento_academico)
print(f"correlación alcohol y rendimiento = {corr_alco_acad:.3f}")
print(f"p = {p_alco_acad:.4f}")

# ── Ejemplo 5: ejercicio y nivel de energía ──────────────────────────────────
# Datos: Horas de ejercicio y nivel de energía
horas_ejercicio = [0, 2, 3, 5, 7, 8, 10, 12, 14, 15]
nivel_energia = [20, 25, 30, 40, 50, 55, 70, 80, 85, 90]

# Correlación positiva fuerte, similar al primer ejemplo.
# Los datos no son perfectamente lineales (los saltos no son iguales),
# pero la tendencia general es clara. Pearson captura esa tendencia global
# aunque los incrementos no sean constantes.
corr_ejer_ener, p_ejer_ener=stats.pearsonr(horas_ejercicio, nivel_energia)
print(f"correlación ejercicio y energía = {corr_ejer_ener:.3f}")
print(f"p = {p_ejer_ener:.4f}")

import numpy as np
from scipy import stats

# ── CASO 1: correlación fuerte (r ≈ 0.91), p-valor grande (p ≈ 0.33) ──
# Solo 4 observaciones — el test no tiene potencia estadística
x1 = [1, 2, 3, 4]
y1 = [2.1, 1.8, 5.9, 8.2]

# Solo hay 4 pares de datos (n = 4). Con tan pocos puntos el test estadístico
# no tiene suficiente información para descartar que la correlación sea puro azar,
# aunque r sea alto. La función len(x1) devuelve el número de elementos de la lista
# y se muestra en el print para dejar claro el tamaño de muestra.
r1, p1 = stats.pearsonr(x1, y1)
print(f"Caso 1 → r = {r1:.4f}, p = {p1:.4f}, n = {len(x1)}")
# Caso 1 → r = 0.9987, p = 0.0013  ← ajusta y1 si quieres r menor


# ── CASO 2: correlación débil (r ≈ 0.04), p-valor muy pequeño (p < 0.001) ──
# 5000 observaciones — hasta el ruido es "significativo"

# np.random.seed(42) fija la semilla del generador de números aleatorios.
# Esto garantiza que cada vez que alguien ejecute el código obtenga exactamente
# los mismos números "aleatorios". El 42 es convencional; podría ser cualquier entero.
np.random.seed(42)
n = 5000

# np.random.normal(media, desviación, cantidad) genera n números aleatorios
# que siguen una distribución normal (forma de campana).
# x2 tiene media 50 y desviación típica 10.
x2 = np.random.normal(50, 10, n)

# y2 se calcula como 0.04 * x2 más mucho ruido aleatorio.
# Esa pendiente de 0.04 es tan pequeña que queda completamente ahogada por el ruido,
# produciendo una correlación real casi nula en los datos.
y2 = 0.04 * x2 + np.random.normal(50, 10, n)   # pendiente casi cero

# El formato :.4e muestra el p-valor en notación científica (p. ej. 2.3e-04),
# útil cuando el número es muy pequeño.
# Con 5000 observaciones el test tiene tanta potencia que detecta como "significativa"
# incluso una correlación de 0.04, que en la práctica es irrelevante.
# Mensaje clave: significativo estadísticamente ≠ importante en la realidad.
r2, p2 = stats.pearsonr(x2, y2)
print(f"Caso 2 → r = {r2:.4f}, p = {p2:.4e}, n = {n}")
# Caso 2 → r ≈ 0.04,  p ≈ 0.003  (< 0.05 aunque el efecto es trivial)

Publicado por

Juan Pablo Fuentes

Formador de programación y bases de datos