import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
metros_cuadrados = [
40, 42, 45, 48, 50, 52, 55, 58, 60, 62,
65, 68, 70, 72, 75, 78, 80, 82, 85, 88,
90, 92, 95, 98, 100, 102, 105, 108, 110, 112,
115, 118, 120, 122, 125, 128, 130, 132, 135, 138,
140, 142, 145, 148, 150, 152, 155, 158, 160, 165
]
precio_miles_euros = [
120, 125, 128, 135, 140, 145, 150, 158, 160, 168,
175, 180, 185, 190, 198, 205, 210, 215, 223, 230,
235, 240, 248, 255, 260, 265, 273, 280, 285, 290,
298, 305, 310, 315, 323, 330, 335, 340, 348, 355,
360, 365, 373, 380, 385, 390, 398, 405, 410, 425
]
x = np.array(metros_cuadrados).reshape(-1, 1)
y = np.array(precio_miles_euros)
# Datos de entrenamiento y pruebas (split)
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
# Crear modelo y entrenar (fit)
model = LinearRegression()
model.fit(X_train, y_train)
print(f"Pendiente {model.coef_} intercepto {model.intercept_}")
# Predecir datos de test
y_pred = model.predict(X_test)
print(y_test)
print(y_pred.round(0))
# Métricas de errores
print(mean_absolute_error(y_test, y_pred))
print(mean_squared_error(y_test, y_pred))
print(r2_score(y_test, y_pred))
Ejercicio regresión
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
metros_cuadrados = [
40, 42, 45, 48, 50, 52, 55, 58, 60, 62,
65, 68, 70, 72, 75, 78, 80, 82, 85, 88,
90, 92, 95, 98, 100, 102, 105, 108, 110, 112,
115, 118, 120, 122, 125, 128, 130, 132, 135, 138,
140, 142, 145, 148, 150, 152, 155, 158, 160, 165
]
precio_miles_euros = [
120, 125, 128, 135, 140, 145, 150, 158, 160, 168,
175, 180, 185, 190, 198, 205, 210, 215, 223, 230,
235, 240, 248, 255, 260, 265, 273, 280, 285, 290,
298, 305, 310, 315, 323, 330, 335, 340, 348, 355,
360, 365, 373, 380, 385, 390, 398, 405, 410, 425
]
x = np.array(metros_cuadrados).reshape(-1, 1)
y = np.array(precio_miles_euros)
# Datos de entrenamiento y pruebas (split)
# Crear modelo y entrenar (fit)
# Predecir datos de test
# Métricas de errores
Ejemplo regresión
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
anuncios=[1,2,3,4,5]
# Datos perfectos
ventas=[5,8,11,14,17]
# Datos más reales
ventas=[5,7,12,14,16]
x = np.array(anuncios).reshape(-1, 1)
y = np.array(ventas)
modelo = LinearRegression()
modelo.fit(x, y)
print(f"Coeficiente {modelo.coef_} intercepto {modelo.intercept_}")
fig, axes = plt.subplots(1, 1, figsize=(10, 8))
axes.scatter(anuncios, ventas, color="steelblue", label="Datos")
axes.plot(x, modelo.predict(x), "r-", label="Modelo")
axes.set_title("Relación entre anuncios y ventas")
axes.set_xlabel("Anuncios"); axes.set_ylabel("Ventas")
axes.legend()
plt.tight_layout()
plt.show()
Probar regresión
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# DATOS
x = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30
]
y = [
96, 92, 91, 87, 86, 82, 80, 78, 74, 72,
69, 68, 64, 61, 60, 56, 54, 51, 49, 45,
43, 40, 38, 35, 33, 29, 28, 24, 23, 19
]
x = np.array(x).reshape(-1, 1)
y = np.array(y)
# SPLIT
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 42)
# ENTRENAMIENTO
modelo=LinearRegression()
modelo.fit(X_train, y_train)
# PREDICCION
y_pred = modelo.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"\nResultados en el conjunto de TEST:")
print(f" MAE = {mae:.3f} (error medio absoluto)")
print(f" RMSE = {rmse:.3f} (raiz error cuadratico)")
print(f" R2 = {r2:.3f} (coef. determinacion)")
Ejemplo regresión lineal
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# 1. DATOS: horas de estudio y notas de 20 estudiantes
horas = np.array([[ 1. ], [ 1.47368421], [ 1.94736842], [ 2.42105263], [ 2.89473684], [ 3.36842105], [ 3.84210526], [ 4.31578947], [ 4.78947368], [ 5.26315789], [ 5.73684211], [ 6.21052632]
, [ 6.68421053], [ 7.15789474], [ 7.63157895], [ 8.10526316], [ 8.57894737], [ 9.05263158], [ 9.52631579], [10. ]])
notas = np.array([ 2.84835708, 2.95718364, 3.77647585, 4.6404623, 4.18818647, 4.61451047
, 5.94750114, 5.96792789, 5.77578912, 6.70812212, 6.63144905, 7.05660881
, 7.83677061, 7.18546514, 7.70596214, 8.71359308, 8.91463707, 10.00449209
, 9.81967217, 9.99384815])
# 2. DIVISION TRAIN / TEST (70% entrenamiento, 30% test)
X_train, X_test, y_train, y_test = train_test_split(
horas, notas, test_size=0.3,random_state=42
)
print(f"Datos de entrenamiento: {len(X_train)} muestras")
print(f"Datos de test: {len(X_test)} muestras")
# 3. ENTRENAMIENTO: el modelo aprende con X_train
modelo = LinearRegression()
modelo.fit(X_train, y_train) # Aqui ocurre el aprendizaje
print(f"\nEcuacion aprendida:")
print(f" nota = {modelo.coef_[0]:.3f} * horas + {modelo.intercept_:.3f}")
# 4. PREDICCION en datos de test (datos nunca vistos)
y_pred = modelo.predict(X_test)
# 5. EVALUACION con metricas
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"\nResultados en el conjunto de TEST:")
print(f" MAE = {mae:.3f} (error medio absoluto)")
print(f" RMSE = {rmse:.3f} (raiz error cuadratico)")
print(f" R2 = {r2:.3f} (coef. determinacion)")
# 6. VISUALIZACION: entrenamiento vs test
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].scatter(X_train, y_train, color="steelblue", label="Train")
axes[0].plot(horas, modelo.predict(horas), "r-", label="Modelo")
axes[0].set_title("Fase de Entrenamiento")
axes[0].set_xlabel("Horas"); axes[0].set_ylabel("Nota")
axes[0].legend()
axes[1].scatter(X_test, y_test, color="orange", label="Test")
axes[1].plot(horas, modelo.predict(horas), "r-", label="Modelo")
axes[1].set_title(f"Fase de Test (R2={r2:.2f})")
axes[1].set_xlabel("Horas"); axes[1].set_ylabel("Nota")
axes[1].legend()
plt.tight_layout()
plt.show()
Ejemplos pandas
import pandas as pd
df = pd.read_csv("ventas_alimentacion.csv", index_col=0)
print(df)
# Estadísticas por ciudad
print(df.sum()) # total ventas por ciudad
print(df.mean()) # media por ciudad
print(df.max()) # máximo por ciudad
datos = {
"Madrid": [12450, 8760, 11230, 9340, 14560, 10870, 6540, 7890],
"Barcelona": [9870, 7540, 8650, 7120, 11230, 12340, 5120, 6540],
"Valencia": [7340, 5980, 6780, 8450, 8760, 7650, 4870, 5230],
"Sevilla": [8920, 6120, 7450, 9210, 9870, 8430, 5340, 4980],
"Zaragoza": [4120, 3870, 4560, 3980, 5120, 3760, 2980, 3240],
"Bilbao": [5630, 4920, 6120, 4760, 7340, 9120, 3870, 4560],
"Málaga": [6780, 4560, 5340, 7890, 6540, 8760, 4120, 3870],
"Murcia": [3940, 3210, 4120, 5340, 4870, 4230, 2760, 2980],
}
indice = [
"Aceite de oliva",
"Pan y bollería",
"Leche y lácteos",
"Frutas y verduras",
"Carne y charcutería",
"Pescado y marisco",
"Pasta, arroz y legumbres",
"Conservas y enlatados",
]
df = pd.DataFrame(datos, index=indice)
print(df)
import pandas as pd
# ─────────────────────────────────────────────
# DATAFRAME DE EJEMPLO
# ─────────────────────────────────────────────
data = {
"Madrid": [12450, 8760, 11230, 9340, 14560, 10870],
"Barcelona": [ 9870, 7540, 8650, 7120, 11230, 12340],
"Valencia": [ 7340, 5980, 6780, 8450, 8760, 7650],
"Sevilla": [ 8920, 6120, 7450, 9210, 9870, 8430],
"Bilbao": [ 5630, 4920, 6120, 4760, 7340, 9120],
"Murcia": [ 3940, 3210, 4120, 5340, 4870, 4230],
}
indice = [
"Aceite de oliva",
"Pan y bollería",
"Leche y lácteos",
"Frutas y verduras",
"Carne y charcutería",
"Pescado y marisco",
]
df = pd.DataFrame(data, index=indice)
print(df)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 1 — .loc[] → celda exacta por nombre
# ══════════════════════════════════════════════════════════════════
# .loc usa ETIQUETAS (nombres reales de filas y columnas).
# Sintaxis: df.loc["nombre_fila", "nombre_columna"]
resultado = df.loc["Leche y lácteos", "Madrid"]
print("1) .loc celda exacta:")
print(resultado) # 11230
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 2 — .iloc[] → celda exacta por posición numérica
# ══════════════════════════════════════════════════════════════════
# .iloc usa POSICIONES enteras (0-based, igual que las listas).
# Fila 1 = "Pan y bollería", columna 2 = "Valencia"
resultado = df.iloc[1, 2]
print("2) .iloc celda exacta:")
print(resultado) # 5980
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 3 — df["columna"] → columna completa (una Serie)
# ══════════════════════════════════════════════════════════════════
# Con un solo corchete y el nombre de columna obtienes una Serie.
# La Serie conserva el índice original del DataFrame.
resultado = df["Barcelona"]
print("3) Columna completa con df['col']:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 4 — .loc["fila"] → fila completa por nombre
# ══════════════════════════════════════════════════════════════════
# Al pasar solo un nombre de fila, .loc devuelve una Serie
# donde el índice son los nombres de las columnas.
resultado = df.loc["Frutas y verduras"]
print("4) Fila completa con .loc:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 5 — .iloc[:, col] → columna completa por posición
# ══════════════════════════════════════════════════════════════════
# El ":" significa "todas las filas".
# La columna 3 corresponde a "Sevilla" (0=Madrid, 1=Barcelona, ...)
resultado = df.iloc[:, 3]
print("5) Columna por posición con .iloc[:, 3]:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 6 — .loc[inicio:fin] → rango de filas por nombre
# ══════════════════════════════════════════════════════════════════
# Con .loc los rangos son INCLUSIVOS en ambos extremos,
# es decir, se incluyen tanto "Pan y bollería" como "Frutas y verduras".
resultado = df.loc["Pan y bollería":"Frutas y verduras"]
print("6) Rango de filas con .loc (inclusivo):")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 7 — .iloc[inicio:fin] → rango de filas por posición
# ══════════════════════════════════════════════════════════════════
# Con .iloc los rangos son EXCLUSIVOS en el extremo derecho,
# igual que los slices de Python. iloc[2:5] devuelve filas 2, 3 y 4.
resultado = df.iloc[2:5]
print("7) Rango de filas con .iloc (exclusivo en el extremo derecho):")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 8 — .iloc[f1:f2, c1:c2] → submatriz por posición
# ══════════════════════════════════════════════════════════════════
# Selecciona filas 0, 1, 2 (0:3 → exclusivo en 3)
# y columnas 1, 2, 3 (1:4 → exclusivo en 4)
resultado = df.iloc[0:3, 1:4]
print("8) Submatriz con .iloc[0:3, 1:4]:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 9 — .loc[rango_filas, lista_columnas]
# ══════════════════════════════════════════════════════════════════
# Combina un rango de filas (por nombre) con una lista de columnas
# específicas. Muy útil para extraer subconjuntos concretos.
resultado = df.loc["Aceite de oliva":"Leche y lácteos", ["Madrid", "Bilbao"]]
print("9) Rango de filas + columnas específicas con .loc:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 10 — df[df["col"] > valor] → filtrado booleano
# ══════════════════════════════════════════════════════════════════
# La expresión df["Madrid"] > 10000 genera una Serie de True/False.
# Al pasarla al DataFrame, devuelve solo las filas donde es True.
resultado = df[df["Madrid"] > 10000]
print("10) Máscara booleana — ventas en Madrid > 10.000 €:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 11 — .loc[condición, columnas] → filtro + selección
# ══════════════════════════════════════════════════════════════════
# Combina un filtro booleano con selección de columnas concretas.
# Primero filtra las filas y luego escoge qué columnas mostrar.
resultado = df.loc[df["Barcelona"] > 9000, ["Barcelona", "Madrid"]]
print("11) .loc con condición y selección de columnas:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 12 — df[["c1", "c2", ...]] → varias columnas a la vez
# ══════════════════════════════════════════════════════════════════
# Con DOBLE corchete se pasa una lista de nombres.
# El resultado es un DataFrame (no una Serie), aunque elijas 1 columna.
resultado = df[["Madrid", "Sevilla", "Murcia"]]
print("12) Varias columnas con doble corchete:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 13 — .iat[] → celda por posición (versión rápida)
# ══════════════════════════════════════════════════════════════════
# .iat es equivalente a .iloc para UN ÚNICO valor escalar.
# Es más eficiente que .iloc en DataFrames grandes porque
# no construye objetos intermedios.
resultado = df.iat[4, 0]
print("13) .iat — celda por posición (rápido):")
print(resultado) # 14560 (fila 4 = "Carne", col 0 = "Madrid")
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 14 — .at[] → celda por etiqueta (versión rápida)
# ══════════════════════════════════════════════════════════════════
# .at es equivalente a .loc para UN ÚNICO valor escalar.
# Más rápido que .loc cuando solo necesitas un dato concreto.
resultado = df.at["Pescado y marisco", "Bilbao"]
print("14) .at — celda por etiqueta (rápido):")
print(resultado) # 9120
import pandas as pd
# ─────────────────────────────────────────────
# DATAFRAME DE EJEMPLO
# ─────────────────────────────────────────────
data = {
"Madrid": [12450, 8760, 11230, 9340, 14560, 10870],
"Barcelona": [ 9870, 7540, 8650, 7120, 11230, 12340],
"Valencia": [ 7340, 5980, 6780, 8450, 8760, 7650],
"Sevilla": [ 8920, 6120, 7450, 9210, 9870, 8430],
"Bilbao": [ 5630, 4920, 6120, 4760, 7340, 9120],
"Murcia": [ 3940, 3210, 4120, 5340, 4870, 4230],
}
indice = [
"Aceite de oliva",
"Pan y bollería",
"Leche y lácteos",
"Frutas y verduras",
"Carne y charcutería",
"Pescado y marisco",
]
df = pd.DataFrame(data, index=indice)
print(df)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 1 — df["col"] > valor → máscara booleana simple
# ══════════════════════════════════════════════════════════════════
# Una comparación sobre una columna devuelve una Serie de True/False.
# Al pasarla al DataFrame, actúa como "filtro de filas".
# Solo se muestran las filas donde la condición es verdadera.
mascara = df["Madrid"] > 10000
resultado = df[mascara]
print("1) Máscara booleana simple — Madrid > 10 000 €:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 2 — & (AND) → dos condiciones simultáneas
# ══════════════════════════════════════════════════════════════════
# Para combinar condiciones usa & (AND) o | (OR).
# IMPORTANTE: cada condición debe ir entre paréntesis porque
# & tiene mayor precedencia que > y <.
resultado = df[(df["Madrid"] > 9000) & (df["Barcelona"] > 9000)]
print("2) Dos condiciones con & (AND) — Madrid > 9 000 y Barcelona > 9 000:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 3 — | (OR) → al menos una condición verdadera
# ══════════════════════════════════════════════════════════════════
# Con | basta con que UNA de las condiciones sea True.
# Aquí: filas donde Bilbao supera 8 000 O Murcia supera 5 000.
resultado = df[(df["Bilbao"] > 8000) | (df["Murcia"] > 5000)]
print("3) Dos condiciones con | (OR) — Bilbao > 8 000 o Murcia > 5 000:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 4 — ~ → negación de una máscara
# ══════════════════════════════════════════════════════════════════
# El operador ~ invierte una máscara booleana (True → False y viceversa).
# Equivale al NOT lógico: devuelve las filas que NO cumplen la condición.
resultado = df[~(df["Valencia"] > 7000)]
print("4) Negación con ~ — filas donde Valencia NO supera 7 000 €:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 5 — .isin() → filtrar por lista de valores
# ══════════════════════════════════════════════════════════════════
# .isin(lista) devuelve True en las filas cuyo valor está en la lista.
# Muy útil cuando tienes un conjunto de etiquetas permitidas.
categorias_interes = ["Aceite de oliva", "Carne y charcutería", "Pescado y marisco"]
resultado = df[df.index.isin(categorias_interes)]
print("5) .isin() — solo categorías de interés:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 6 — .query() → filtrar con expresión de texto
# ══════════════════════════════════════════════════════════════════
# .query() acepta una cadena de texto con la condición en lenguaje
# casi natural. Más legible para condiciones complejas.
# Nota: si el nombre de columna tiene espacios, usa backticks (`col`).
resultado = df.query("Madrid > 10000 and Sevilla > 8000")
print("6) .query() — Madrid > 10 000 y Sevilla > 8 000:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 7 — .filter(items=) → seleccionar columnas por nombre
# ══════════════════════════════════════════════════════════════════
# .filter() selecciona columnas (o filas con axis=0) por nombre exacto.
# No filtra por valores, sino por NOMBRE de columna.
resultado = df.filter(items=["Madrid", "Barcelona", "Murcia"])
print("7) .filter(items=) — columnas Madrid, Barcelona y Murcia:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 8 — .filter(like=) → columnas cuyo nombre contiene texto
# ══════════════════════════════════════════════════════════════════
# like= selecciona columnas cuyo nombre CONTIENE la subcadena dada.
# axis=1 indica que buscamos en columnas; axis=0 buscaría en el índice.
resultado = df.filter(like="a", axis=1) # columnas que contienen "a"
print("8) .filter(like='a') — columnas que contienen la letra 'a':")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 9 — .filter(regex=) → columnas por expresión regular
# ══════════════════════════════════════════════════════════════════
# regex= permite filtrar columnas cuyo nombre cumple un patrón regex.
# Aquí seleccionamos columnas que empiezan por 'M', 'B' o 'S'.
resultado = df.filter(regex="^(M|B|S)", axis=1)
print("9) .filter(regex=) — columnas que empiezan por M, B o S:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 10 — .loc[condición, columnas] → filtro fila + columnas
# ══════════════════════════════════════════════════════════════════
# .loc admite una máscara booleana como selector de filas Y una lista
# de columnas como segundo argumento. El resultado es un sub-DataFrame.
resultado = df.loc[df["Bilbao"] > 6000, ["Bilbao", "Madrid", "Murcia"]]
print("10) .loc con condición + columnas — Bilbao > 6 000, ver Bilbao/Madrid/Murcia:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 11 — .where() → mantiene forma, enmascara con NaN
# ══════════════════════════════════════════════════════════════════
# A diferencia de los filtros anteriores, .where() conserva el shape
# original del DataFrame. Las celdas que NO cumplen la condición
# se sustituyen por NaN (o por el valor que indiques en `other=`).
resultado = df.where(df > 8000)
print("11) .where(df > 8000) — valores < 8 000 sustituidos por NaN:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 12 — .mask() → inverso de .where()
# ══════════════════════════════════════════════════════════════════
# .mask() es el opuesto de .where(): enmascara con NaN las celdas
# que SÍ cumplen la condición, manteniendo las que no la cumplen.
resultado = df.mask(df > 8000)
print("12) .mask(df > 8000) — valores > 8 000 sustituidos por NaN:")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 13 — filtro por índice con .str → texto en el índice
# ══════════════════════════════════════════════════════════════════
# Si el índice es de tipo cadena, puedes usar .str para filtrarlo.
# .str.contains() busca una subcadena; .str.startswith() un prefijo.
resultado = df[df.index.str.contains("y")]
print("13) Filtro de texto en el índice — categorías que contienen 'y':")
print(resultado)
print()
# ══════════════════════════════════════════════════════════════════
# MÉTODO 14 — .nlargest() / .nsmallest() → top N por columna
# ══════════════════════════════════════════════════════════════════
# .nlargest(n, columna) devuelve las n filas con mayor valor en
# la columna indicada, ordenadas de mayor a menor.
# .nsmallest(n, columna) hace lo mismo para los menores valores.
resultado = df.nlargest(3, "Madrid")
print("14) .nlargest(3, 'Madrid') — top 3 categorías en Madrid:")
print(resultado)
print()
resultado = df.nsmallest(3, "Murcia")
print(" .nsmallest(3, 'Murcia') — bottom 3 categorías en Murcia:")
print(resultado)
print()
Ejercicio coches
"""
╔══════════════════════════════════════════════════════════════════╗
║ EJERCICIO: "NOS HAN DICHO QUE LOS COCHES MÁS POTENTES ║
║ GASTAN MÁS COMBUSTIBLE... ¿ES CIERTO?" ║
╚══════════════════════════════════════════════════════════════════╝
LA SITUACIÓN
────────────
Un amigo mecánico te dice:
"Oye, está clarísimo: cuantos más caballos tiene un coche,
más combustible gasta. Siempre ha sido así."
¿Tiene razón? ¿Podemos demostrarlo (o refutarlo) con datos?
En este ejercicio vas a:
1. Cargar un dataset real de coches
2. Entender qué contiene
3. Limpiarlo correctamente
4. Calcular si realmente existe esa relación
5. Visualizarla
6. Responder preguntas para interpretar los resultados
EL DATASET: MPG (Miles Per Gallon)
────────────────────────────────────
Contiene información de 398 coches fabricados entre 1970 y 1982.
Fue recopilado originalmente por la revista Consumer Reports y
es uno de los datasets clásicos del aprendizaje automático.
Columnas principales:
mpg → millas por galón (eficiencia: más = gasta MENOS)
cylinders → número de cilindros del motor
displacement → cilindrada (pulgadas cúbicas)
horsepower → caballos de potencia (HP)
weight → peso del coche (libras)
acceleration → aceleración (segundos de 0 a 60 mph)
model_year → año del modelo (70 = 1970, 82 = 1982)
origin → origen (1=EEUU, 2=Europa, 3=Japón)
name → nombre del modelo
PREGUNTA A INVESTIGAR:
¿Existe relación entre los caballos de potencia (horsepower)
y el consumo de combustible (mpg)?
Librerías necesarias:
pip install pandas matplotlib seaborn
"""
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
import numpy as np
from scipy import stats
# ═══════════════════════════════════════════════════════════════════
# PASO 1 — CARGA DE DATOS
# ═══════════════════════════════════════════════════════════════════
# Cargamos el dataset "mpg" directamente desde seaborn.
# seaborn incluye varios datasets de ejemplo listos para usar;
# no hace falta descargar ningún archivo manualmente.
print("\n" + "═"*60)
print(" PASO 1 — CARGA DE DATOS")
print("═"*60)
coches = sns.load_dataset("mpg")
# Comprobamos que se ha cargado correctamente: cuántas filas y columnas
print(f"\n Filas : {coches.shape[0]}")
print(f" Columnas : {coches.shape[1]}")
print(f" Nombres : {coches.columns.tolist()}")
# ═══════════════════════════════════════════════════════════════════
# PASO 2 — PRIMER VISTAZO
# ═══════════════════════════════════════════════════════════════════
# Antes de analizar nada, siempre hay que MIRAR los datos.
# Es como abrir una caja antes de saber qué hay dentro.
print("\n" + "═"*60)
print(" PASO 2 — PRIMER VISTAZO")
print("═"*60)
print("\n── .head(): primeras 5 filas ──────────────────────────────")
print(coches.head())
print("\n── .tail(): últimas 5 filas ───────────────────────────────")
print(coches.tail())
print("\n── .dtypes: tipo de dato de cada columna ──────────────────")
print(coches.dtypes)
# ATENCIÓN: fíjate en el tipo de 'horsepower'. ¿Es numérico?
# Si aparece como 'object' en lugar de 'float64', significa que
# pandas lo ha interpretado como texto.
print("\n── .describe(): estadísticas rápidas ──────────────────────")
print(coches.describe().round(2))
# describe() solo muestra columnas numéricas por defecto.
# Si 'horsepower' no aparece aquí, confirma que es texto.
# ═══════════════════════════════════════════════════════════════════
# PASO 3 — ANÁLISIS DE CALIDAD: ¿ESTÁN BIEN LOS DATOS?
# ═══════════════════════════════════════════════════════════════════
# Los datos reales casi nunca son perfectos. Hay que buscar:
# a) Valores nulos (celdas vacías)
# b) Tipos de datos incorrectos
# c) Valores extraños o imposibles
print("\n" + "═"*60)
print(" PASO 3 — ANÁLISIS DE CALIDAD DE LOS DATOS")
print("═"*60)
# ── a) Valores nulos ────────────────────────────────────────────
print("\n── a) ¿Cuántos valores nulos hay por columna? ─────────────")
nulos = coches.isnull().sum()
print(nulos)
print(f"\n Total de celdas nulas: {nulos.sum()}")
print("\n── c) Rango de 'mpg' ──────────────────────────────────────")
print(f" Mínimo: {coches['mpg'].min():.1f} | Máximo: {coches['mpg'].max():.1f}")
# ═══════════════════════════════════════════════════════════════════
# PASO 4 — LIMPIEZA DE DATOS
# ═══════════════════════════════════════════════════════════════════
# Ahora que sabemos los problemas, los corregimos uno a uno.
# Es importante hacerlo en un paso separado y documentado.
print("\n" + "═"*60)
print(" PASO 4 — LIMPIEZA DE DATOS")
print("═"*60)
# Hacemos una copia para no modificar el dataset original
# (buena práctica: siempre trabajar sobre una copia)
df = coches.copy()
# ── Corrección 2: eliminar filas con NaN en las columnas clave ───
# Solo nos importan 'mpg' y 'horsepower' para este análisis.
# dropna(subset=[...]) elimina solo las filas con NaN en esas columnas.
filas_antes = len(df)
df = df.dropna(subset=["mpg", "horsepower"])
filas_despues = len(df)
print(f" ✓ Filas eliminadas con NaN: {filas_antes - filas_despues}")
print(f" ✓ Filas disponibles para el análisis: {filas_despues}")
# ── Verificación final ───────────────────────────────────────────
print(f"\n Nulos restantes en 'mpg' : {df['mpg'].isnull().sum()}")
print(f" Nulos restantes en 'horsepower': {df['horsepower'].isnull().sum()}")
print("\n Dataset limpio y listo para analizar.")
# ═══════════════════════════════════════════════════════════════════
# PASO 5 — ESTADÍSTICAS DESCRIPTIVAS DE LAS VARIABLES CLAVE
# ═══════════════════════════════════════════════════════════════════
# Antes de calcular la correlación, conoce bien cada variable
# por separado. ¿En qué rango están? ¿Hay mucha variación?
print("\n" + "═"*60)
print(" PASO 5 — ESTADÍSTICAS DE LAS VARIABLES CLAVE")
print("═"*60)
for col, nombre in [("mpg", "Eficiencia (mpg)"),
("horsepower", "Potencia (HP)")]:
s = df[col]
print(f"\n {nombre}:")
print(f" Media : {s.mean():.1f}")
print(f" Mediana : {s.median():.1f}")
print(f" Desv. típica : {s.std():.1f}")
print(f" Mínimo : {s.min():.1f}")
print(f" Máximo : {s.max():.1f}")
# ═══════════════════════════════════════════════════════════════════
# PASO 6 — CALCULAR LA CORRELACIÓN
# ═══════════════════════════════════════════════════════════════════
# Ahora sí: calculamos el coeficiente de Pearson entre
# 'horsepower' y 'mpg'.
#
# RECORDATORIO:
# r cercano a +1 → relación positiva fuerte
# r cercano a -1 → relación negativa fuerte
# r cercano a 0 → sin relación lineal clara
#
# OJO: mpg mide eficiencia (más mpg = MENOS consumo).
# Si el amigo tiene razón, esperamos r NEGATIVO:
# más HP → menos mpg (peor eficiencia = más consumo).
print("\n" + "═"*60)
print(" PASO 6 — CORRELACIÓN DE PEARSON")
print("═"*60)
r,p=stats.pearsonr(df["mpg"], df["horsepower"])
print(f"La correlación entre potencia y eficiencia es {r:.2f} con una significación de {p:.2f}.")
# ═══════════════════════════════════════════════════════════════════
# PASO 7 — PREGUNTAS INTERPRETATIVAS
# ═══════════════════════════════════════════════════════════════════
# Lee los resultados con atención antes de responder.
print("\n" + "═"*60)
print(" PASO 8 — PREGUNTAS PARA REFLEXIONAR")
print("═"*60)
print(f"""
Antes de responder, fíjate en:
• El valor de r y su signo
──────────────────────────────────────────────────────────
BLOQUE A — Sobre los datos
1. ¿Cuántos coches tenía el dataset original?
¿Cuántos se han eliminado en la limpieza y por qué?
¿Crees que esa pérdida afecta al análisis?
──────────────────────────────────────────────────────────
BLOQUE B — Sobre la correlación
4. El coeficiente r
¿Es positivo o negativo? ¿Qué significa eso en este contexto?
(Recuerda: mpg alto = gasta POCO; mpg bajo = gasta MUCHO)
5. ¿Tenía razón el amigo mecánico?
Formula la respuesta con datos concretos.
6. R²
Eso significa que la potencia (HP) explica el ___% de las
diferencias de consumo entre coches.
¿Qué otros factores podrían explicar el resto?
7. El p-valor es
¿Podemos confiar en este resultado o podría ser coincidencia?
──────────────────────────────────────────────────────────
BLOQUE D — Reflexión final
11. "Correlación no implica causalidad."
¿Puedes pensar en una variable que esté relacionada tanto
con la potencia como con el consumo y que pudiera estar
"confundiendo" la relación? (Pista: mira las columnas del
dataset)
12. Si quisieras PREDECIR el consumo de un coche nuevo del que
solo sabes los HP, ¿usarías este modelo?
¿Qué columnas añadirías para mejorar la predicción?
""")
Solución pingüinos
"""
╔══════════════════════════════════════════════════════════════════╗
║ EJERCICIO: ¿QUÉ VARIABLES DE LOS PINGÜINOS SE ║
║ RELACIONAN MÁS ENTRE SÍ? ║
╚══════════════════════════════════════════════════════════════════╝
Dataset: Palmer Penguins 🐧
Contiene medidas físicas de 344 pingüinos de 3 especies distintas
recogidas en las islas Palmer (Antártida).
Variables numéricas disponibles:
• bill_length_mm → longitud del pico (mm)
• bill_depth_mm → profundidad del pico (mm)
• flipper_length_mm → longitud de la aleta (mm)
• body_mass_g → masa corporal (gramos)
Variable categórica:
• species → especie (Adelie, Chinstrap, Gentoo)
Objetivo del ejercicio:
Descubrir qué par de variables numéricas tiene la correlación
más fuerte (positiva o negativa) usando pandas y stats.
Librerías necesarias:
pip install pandas matplotlib seaborn
Estructura del ejercicio:
PASO 0 — Cargar los datos
PASO 1 — Explorar el dataset
PASO 2 — Limpiar valores nulos
PASO 3 — Calcular la correlación entre TODAS las variables
PASO 4 — Encontrar el par con mayor correlación
★ RETO EXTRA — Escribe tus propias conclusiones
"""
# pandas: manejo de tablas de datos (DataFrames)
import pandas as pd
# seaborn: visualización estadística; también incluye datasets de ejemplo
import seaborn as sns
# numpy: operaciones numéricas con arrays (importado por convención,
# aunque en este script no se usa directamente)
import numpy as np
# stats de scipy: funciones estadísticas avanzadas;
# aquí la usamos para calcular la correlación de Pearson con su valor p
from scipy import stats
# ===========================================================================
# PASO 0 — CARGAR LOS DATOS
# ===========================================================================
# seaborn incluye el dataset de pingüinos de forma gratuita.
# load_dataset() lo descarga automáticamente la primera vez.
print("\n" + "="*60)
print(" PASO 0 — CARGAR LOS DATOS")
print("="*60)
# Carga el dataset "penguins" directamente desde seaborn.
# El resultado es un DataFrame de pandas con 344 filas y 7 columnas.
pinguinos = sns.load_dataset("penguins")
# Confirmamos cuántas filas y columnas tiene el DataFrame
# .shape devuelve una tupla (filas, columnas)
print(f"\n Dataset cargado con {pinguinos.shape[0]} filas y {pinguinos.shape[1]} columnas.")
print(f" Columnas: {pinguinos.columns.tolist()}")
# ===========================================================================
# PASO 1 — EXPLORAR EL DATASET
# ===========================================================================
# Antes de cualquier análisis, siempre hay que entender qué contiene
# la tabla. Usa .head(), .info() y .describe() para hacerte una idea.
print("\n" + "="*60)
print(" PASO 1 — EXPLORAR EL DATASET")
print("="*60)
# .head() muestra las 5 primeras filas del DataFrame.
# Muy útil para ver rápidamente la estructura y los valores reales.
print("\n── Primeras 5 filas (.head()) ──")
print(pinguinos.head())
# .info() muestra un resumen técnico: nombre de columna, tipo de dato
# (int, float, object…) y cuántos valores NO nulos tiene cada columna.
# Si una columna tiene menos valores que el total de filas, hay nulos.
print("\n── Información general (.info()) ──")
pinguinos.info()
# .describe() calcula automáticamente estadísticas básicas para cada
# columna numérica: media, desviación típica, mínimo, máximo y cuartiles.
# .round(1) redondea a 1 decimal para que sea más legible.
print("\n── Estadísticas descriptivas (.describe()) ──")
print(pinguinos.describe().round(1))
# .value_counts() cuenta cuántas veces aparece cada valor único en la columna.
# Aquí lo usamos para saber cuántos pingüinos hay de cada especie.
print("\n── Pingüinos por especie (.value_counts()) ──")
print(pinguinos["species"].value_counts())
# ===========================================================================
# PASO 2 — LIMPIAR VALORES NULOS
# ===========================================================================
# Algunos pingüinos tienen medidas incompletas (NaN).
# La correlación no funciona bien con valores nulos, así que los eliminamos.
print("\n" + "="*60)
print(" PASO 2 — LIMPIAR VALORES NULOS")
print("="*60)
# Eliminamos la columna "sex" porque no la vamos a usar en el análisis
# y además contiene nulos que nos complicarían el recuento.
# inplace=True modifica el DataFrame original sin necesidad de reasignarlo.
pinguinos.drop(columns=["sex"], inplace=True)
# .isnull() devuelve un DataFrame de True/False indicando dónde hay nulos.
# .sum() suma los True de cada columna (True = 1, False = 0),
# dando el total de nulos por columna.
print("\n── ¿Cuántos nulos hay en cada columna? ──")
print(pinguinos.isnull().sum())
# .dropna() elimina todas las filas que tengan AL MENOS un valor nulo
# en cualquier columna. Devuelve un nuevo DataFrame sin modificar el original.
pinguinos_limpio = pinguinos.dropna()
# Mostramos cuántas filas hemos perdido en la limpieza
print(f"\n Filas antes de limpiar : {len(pinguinos)}")
print(f" Filas después de limpiar: {len(pinguinos_limpio)}")
print(f" Filas eliminadas : {len(pinguinos) - len(pinguinos_limpio)}")
# ===========================================================================
# PASO 3 — CALCULAR LA CORRELACIÓN ENTRE LAS VARIABLES
# ===========================================================================
# El coeficiente de Pearson (r) mide la fuerza de la relación lineal
# entre dos variables numéricas. Siempre está entre -1 y +1:
# r = 1.0 → correlación perfecta positiva (cuando X sube, Y sube)
# r = -1.0 → correlación perfecta negativa (cuando X sube, Y baja)
# r = 0.0 → sin correlación lineal
# |r| > 0.7 se considera correlación FUERTE
print("\n" + "="*60)
print(" PASO 3 — MATRIZ DE CORRELACIÓN")
print("="*60)
# Usamos un alias corto para no repetir "pinguinos_limpio" en cada línea
df = pinguinos_limpio
# stats.pearsonr(x, y) devuelve dos valores:
# r → el coeficiente de correlación de Pearson (de -1 a +1)
# p → el valor p (p-value): probabilidad de obtener esta correlación
# por puro azar. Si p < 0.05, la correlación es estadísticamente
# significativa (muy poco probable que sea casualidad).
# Correlación entre masa corporal y longitud del pico
r1, p1 = stats.pearsonr(df["body_mass_g"], df["bill_length_mm"])
print(r1, p1)
# Correlación entre masa corporal y profundidad del pico
r2, p2 = stats.pearsonr(df["body_mass_g"], df["bill_depth_mm"])
print(r2, p2)
# Correlación entre masa corporal y longitud de la aleta
r3, p3 = stats.pearsonr(df["body_mass_g"], df["flipper_length_mm"])
print(r3, p3)
# --- PARA EXPERTOS: calcular TODAS las combinaciones posibles ---
# En lugar de comparar solo contra body_mass_g, calculamos el coeficiente
# entre CADA par posible de variables numéricas.
columnas_numericas = ["bill_length_mm", "bill_depth_mm",
"flipper_length_mm", "body_mass_g"]
# Lista donde iremos acumulando los resultados de cada par
correlaciones = []
# Doble bucle anidado para generar todos los pares sin repetir.
# El truco está en que j empieza en i+1:
# cuando i=0 → j recorre 1, 2, 3 (3 pares con bill_length_mm)
# cuando i=1 → j recorre 2, 3 (2 pares con bill_depth_mm)
# cuando i=2 → j recorre 3 (1 par con flipper_length_mm)
# Total: 6 pares únicos sin duplicados ni la diagonal (variable consigo misma)
for i in range(len(columnas_numericas)):
for j in range(i + 1, len(columnas_numericas)):
var1 = columnas_numericas[i]
var2 = columnas_numericas[j]
# Calculamos r y p para este par concreto
r, p = stats.pearsonr(df[var1], df[var2])
# Guardamos la tupla (variable1, variable2, r redondeado, p redondeado)
# float() convierte los valores numpy a float nativo de Python
correlaciones.append((var1, var2, float(round(r, 2)), float(round(p, 2))))
# ===========================================================================
# PASO 4 — ENCONTRAR EL PAR CON MAYOR CORRELACIÓN
# ===========================================================================
# Buscamos el par de variables con |r| más alto (en valor absoluto,
# para tratar igual las correlaciones positivas y las negativas).
print("\n" + "="*60)
print(" PASO 4 — PAR CON MAYOR CORRELACIÓN")
print("="*60)
# Ordenamos la lista de menor a mayor según |r| (valor absoluto de r).
# key=lambda x: abs(x[2]) indica que el criterio de orden es el
# tercer elemento de cada tupla (el coeficiente r), tomado en valor absoluto.
# Con esto el último elemento de la lista será el par más correlacionado.
correlaciones.sort(key=lambda x: abs(x[2]))
# Mostramos todos los pares ordenados de menor a mayor correlación
for var1, var2, corr, p in correlaciones:
print(var1, var2, corr, p)
# ===========================================================================
# ★ RETO EXTRA — PREGUNTAS PARA REFLEXIONAR
# ===========================================================================
print("\n" + "="*60)
print(" ★ RETO EXTRA — RESPONDE ESTAS PREGUNTAS")
print("="*60)
print("""
Completa estas frases con lo que has descubierto:
1. El par de variables con MAYOR correlación es:
__________ y __________ con r = _______
2. El par de variables con MENOR correlación es:
__________ y __________ con r = _______
3. ¿La correlación más alta es positiva o negativa?
_______________________________________________________
4. ¿Qué significa en la práctica que esas dos variables
tengan una correlación tan alta?
_______________________________________________________
5. ¿La correlación cambia mucho entre especies?
¿Qué especie tiene la correlación más diferente al global?
_______________________________________________________
6. Correlación NO implica causalidad. ¿Puedes pensar en una
razón biológica que explique la relación encontrada?
_______________________________________________________
""")
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)
Cálculo correlación y pearson
import numpy as np from scipy.stats import pearsonr # 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] # 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] # 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] consumo_alcohol = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] rendimiento_academico = [90, 88, 85, 83, 80, 75, 70, 65, 60, 50] # 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]