¡Con gusto! A continuación te presento el Notebook de Google Colab completo, estructurado para que puedas copiarlo y pegarlo directamente en celdas de Colab. Está organizado según tus requerimientos: integra las 4 semanas, usa ambos datasets (Soils y Acero), incluye equivalencias en R, instrucciones para GitHub, prompts sugeridos, y la guía final para PDF.


📊 NOTEBOOK: Estadística Aplicada con Python y R

Integración Semanas 1-4: EDA Estructurado y Visualización

Universidad de Sucre - Facultad de Ingeniería


🔧 CELDA 1: Configuración Inicial

Prompt sugerido para IA: “Explica línea por línea qué hace este código de configuración inicial y por qué instalamos estos paquetes específicos”

# Configuración inicial del entorno
# Instalación de paquetes necesarios para análisis estadístico
!pip install -q pandas numpy matplotlib seaborn scipy statsmodels

# Importación de librerías esenciales
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import statsmodels.api as sm
from scipy.stats import skew, kurtosis, pearsonr, spearmanr
import warnings
warnings.filterwarnings('ignore')

# Configuración de visualización
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
print("✅ Entorno configurado correctamente")

📝 Equivalente en R (para celda de texto previa):

# Configuración inicial en R
install.packages(c("tidyverse", "carData", "moments", "corrplot", "ggplot2", "dplyr"))
library(tidyverse)
library(carData)
library(moments)
library(corrplot)

# Cargar datos Soils
data(Soils)

# Para acero.csv (subir manualmente o desde URL)
acero <- read.csv("acero.csv", sep=",", dec=",", stringsAsFactors = TRUE)

📚 SEMANA 1: Fundamentos de Estadística Aplicada

🎯 Idea Central: Comprensión de tipos de datos, escalas de medición, población/muestra y estadísticos descriptivos básicos.

Celda de texto: Instrucciones GitHub - Versión 1

💾 GUARDAR COPIA EN GITHUB: Al finalizar esta sección (Semana 1), guarda una copia de este notebook como:
"Estadistica_Aplicada_Semana1_Fundamentos.ipynb"
En tu repositorio GitHub con el commit message: "Versión 1: Fundamentos de Estadística - Tipos de datos y Estadísticos básicos"

Carga de Datasets y Clasificación de Variables

Prompt sugerido: “Explica cada línea del código de carga y clasificación de variables, diferenciando qué es población y muestra en este contexto, y qué escalas de medición aplican a cada variable”

# CARGA DEL DATASET SOILS desde carData (R) usando statsmodels
soils_data = sm.datasets.get_rdataset("Soils", "carData")
soils = soils_data.data

print("=== DATASET SOILS (carData) ===")
print(f"Dimensiones: {soils.shape} (muestra de suelos)")
print(f"Variables: {list(soils.columns)}")
print("\nTipos de datos identificados:")
print(soils.dtypes)

# CARGA DEL DATASET ACERO desde archivo local
# Nota: En Colab, subir el archivo acero.csv primero
acero = pd.read_csv("acero.csv", sep=",", decimal=",")

print("\n=== DATASET ACERO (Producción Industrial) ===")
print(f"Dimensiones: {acero.shape} (muestra de producción)")
print(f"Variables: {list(acero.columns)}")
print("\nPrimeras filas:")
print(acero.head())

📝 Equivalente en R:

# Carga de datos
data(Soils)  # Dataset carData
acero <- read.csv("acero.csv", sep=",", dec=",", stringsAsFactors = TRUE)

# Inspección estructural
str(Soils)
str(acero)
summary(Soils)
summary(acero)

Identificación de Tipos de Datos y Escalas

Prompt sugerido: “Clasifica cada variable de ambos datasets según su escala de medición (nominal, ordinal, intervalo, razón) y explica por qué es importante esta clasificación para elegir métodos estadísticos”

# Clasificación de variables en Soils
clasificacion_soils = {
    "Contour": {"tipo": "Categórica", "escala": "Nominal", "descripcion": "Topografía (Depression, Slope, Top)"},
    "Depth": {"tipo": "Categórica", "escala": "Ordinal", "descripcion": "Profundidad del suelo ordenada (0-10, 10-30, 30-60, 60-90)"},
    "Block": {"tipo": "Categórica", "escala": "Nominal", "descripcion": "Bloque experimental (1-4)"},
    "pH": {"tipo": "Numérica", "escala": "Intervalo", "descripcion": "Acidez/alcalinidad (escala logarítmica)"},
    "N": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Nitrógeno total (%)"},
    "Dens": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Densidad aparente (g/cm³)"},
    "P": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Fósforo total (ppm)"},
    "Ca": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Calcio (me/100g)"},
    "Mg": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Magnesio (me/100g)"},
    "K": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Potasio (me/100g)"},
    "Na": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Sodio (me/100g)"},
    "Conduc": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Conductividad eléctrica"}
}

# Clasificación de variables en Acero
clasificacion_acero = {
    "consumo": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Consumo energético (MWh)"},
    "pr.tbc": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Producción tren bandas calientes (Toneladas)"},
    "pr.cc": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Producción colada continua (Toneladas)"},
    "linea": {"tipo": "Categórica", "escala": "Nominal", "descripcion": "Línea de producción (A, B, C)"},
    "hora": {"tipo": "Categórica", "escala": "Ordinal", "descripcion": "Hora del turno (1ª-8ª)"},
    "temperatura": {"tipo": "Categórica", "escala": "Ordinal", "descripcion": "Alta, Media, Baja (orden inherente)"},
    "averias": {"tipo": "Categórica", "escala": "Nominal", "descripcion": "Presencia de averías (Sí/No)"},
    "naverias": {"tipo": "Numérica", "escala": "Discreta", "descripcion": "Número de averías (contaje)"},
    "sistema": {"tipo": "Categórica", "escala": "Nominal", "descripcion": "Sistema ON/OFF"},
    "prodtotal": {"tipo": "Numérica", "escala": "Razón", "descripcion": "Producción total (Toneladas)"}
}

print("=== CLASIFICACIÓN DE VARIABLES ===")
print("\nDataset SOILS - Ejemplos:")
for var, info in list(clasificacion_soils.items())[:5]:
    print(f"• {var}: {info['tipo']} | Escala: {info['escala']} | {info['descripcion']}")

print("\nDataset ACERO - Ejemplos:")
for var, info in clasificacion_acero.items():
    print(f"• {var}: {info['tipo']} | Escala: {info['escala']} | {info['descripcion']}")

Estadísticos Descriptivos Básicos

Prompt sugerido: “Interpreta cada estadístico calculado (media, mediana, desviación estándar) en contexto de ingeniería agrícola para Soils y en contexto de producción industrial para Acero”

# Estadísticos descriptivos para variables numéricas de Soils
print("=== ESTADÍSTICOS DESCRIPTIVOS - SOILS ===")
vars_numericas_soils = ['pH', 'N', 'Dens', 'P', 'Ca', 'Mg']
desc_soils = soils[vars_numericas_soils].describe()
print(desc_soils)

# Estadísticos descriptivos para Acero (variables clave)
print("\n=== ESTADÍSTICOS DESCRIPTIVOS - ACERO ===")
vars_acero = ['consumo', 'pr.tbc', 'prodtotal', 'naverias']
desc_acero = acero[vars_acero].describe()
print(desc_acero)

# Cálculo manual de varianza y desviación estándar (concepto poblacional vs muestral)
print("\n=== COMPARACIÓN POBLACIÓN vs MUESTRA (Dataset Acero - Consumo) ===")
consumo = acero['consumo']

# Muestra (ddof=1) - estándar en inferencia estadística
media_muestra = np.mean(consumo)
var_muestra = np.var(consumo, ddof=1)  # n-1
std_muestra = np.std(consumo, ddof=1)

print(f"Media muestral: {media_muestra:.2f} MWh")
print(f"Varianza muestral (n-1): {var_muestra:.2f}")
print(f"Desviación estándar muestral: {std_muestra:.2f} MWh")
print(f"Coeficiente de variación: {(std_muestra/media_muestra)*100:.1f}%")

📝 Equivalente en R:

# Estadísticos descriptivos
summary(Soils[, c("pH", "N", "Dens", "P", "Ca", "Mg")])
summary(acero[, c("consumo", "pr.tbc", "prodtotal", "naverias")])

# Cálculo manual
media <- mean(acero$consumo)
var_muestral <- var(acero$consumo)  # Usa n-1 por defecto
desv_est <- sd(acero$consumo)

cat("Media:", media, "\nVarianza:", var_muestral, "\nDE:", desv_est)

📊 SEMANA 2: Visualización Básica en Estadística

🎯 Idea Central: La visualización es herramienta de análisis diagnóstico, no solo presentación. Selección correcta de gráficos según tipo de variable.

Celda de texto: Instrucciones GitHub - Versión 2

💾 GUARDAR COPIA EN GITHUB: Guardar como:
"Estadistica_Aplicada_Semana2_Visualizacion.ipynb"
Commit: "Versión 2: Visualización Básica - Histogramas, Boxplots y Scatterplots"

Histogramas y Densidad (Variables Continuas)

Prompt sugerido: “Explica qué representa la forma, centro y dispersión en estos histogramas, y por qué usamos diferentes números de bins para cada dataset”

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Histograma pH en Soils
axes[0,0].hist(soils['pH'], bins=8, color='forestgreen', edgecolor='black', alpha=0.7, density=True)
axes[0,0].axvline(soils['pH'].mean(), color='red', linestyle='--', label=f'Media: {soils["pH"].mean():.2f}')
axes[0,0].axvline(soils['pH'].median(), color='blue', linestyle='-', label=f'Mediana: {soils["pH"].median():.2f}')
axes[0,0].set_title('Distribución de pH en Suelos (Soils)', fontsize=12, fontweight='bold')
axes[0,0].set_xlabel('pH')
axes[0,0].set_ylabel('Densidad')
axes[0,0].legend()
axes[0,0].grid(True, alpha=0.3)

# Histograma Consumo en Acero
axes[0,1].hist(acero['consumo'], bins=15, color='steelblue', edgecolor='black', alpha=0.7)
axes[0,1].axvline(acero['consumo'].mean(), color='red', linestyle='--', label=f'Media: {acero["consumo"].mean():.1f}')
axes[0,1].axvline(acero['consumo'].median(), color='blue', linestyle='-', label=f'Mediana: {acero["consumo"].median():.1f}')
axes[0,1].set_title('Distribución Consumo Energético (Acero)', fontsize=12, fontweight='bold')
axes[0,1].set_xlabel('Consumo (MWh)')
axes[0,1].set_ylabel('Frecuencia')
axes[0,1].legend()

# Densidad Kernel para pH
sns.kdeplot(data=soils, x='pH', ax=axes[1,0], fill=True, color='green', alpha=0.5)
axes[1,0].set_title('Densidad Kernel - pH (Soils)', fontsize=12, fontweight='bold')

# Densidad Kernel para Producción Total
sns.kdeplot(data=acero, x='prodtotal', ax=axes[1,1], fill=True, color='blue', alpha=0.5)
axes[1,1].set_title('Densidad Kernel - Prod. Total (Acero)', fontsize=12, fontweight='bold')

plt.tight_layout()
plt.show()

# Interpretación de forma
print("=== INTERPRETACIÓN DE FORMA ===")
print(f"Skewness pH: {skew(soils['pH']):.2f} (simetría)")
print(f"Skewness Consumo: {skew(acero['consumo']):.2f} (asimetría derecha)")

📝 Equivalente en R:

# Histogramas con ggplot2
ggplot(Soils, aes(x=pH)) + 
  geom_histogram(bins=8, fill="forestgreen", color="black", alpha=0.7) +
  geom_vline(aes(xintercept=mean(pH)), color="red", linetype="dashed") +
  labs(title="Distribución pH en Suelos")

ggplot(acero, aes(x=consumo)) + 
  geom_histogram(bins=15, fill="steelblue", color="black", alpha=0.7) +
  geom_vline(aes(xintercept=mean(consumo)), color="red", linetype="dashed") +
  labs(title="Consumo Energético")

# Gráficos de densidad
ggplot(Soils, aes(x=pH)) + geom_density(fill="green", alpha=0.5)
ggplot(acero, aes(x=prodtotal)) + geom_density(fill="blue", alpha=0.5)

Boxplots (Detección de Outliers y Comparación de Grupos)

Prompt sugerido: “Explica qué representa cada elemento del boxplot (median, IQR, bigotes, outliers) y qué decisiones de ingeniería podemos tomar al ver estas diferencias entre grupos”

fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# Boxplot pH por Contour (Soils) - Variable categórica nominal vs numérica
sns.boxplot(data=soils, x='Contour', y='pH', ax=axes[0], palette='Set2')
axes[0].set_title('pH del Suelo por Tipo de Topografía', fontsize=12, fontweight='bold')
axes[0].set_xlabel('Tipo de Contorno')
axes[0].set_ylabel('pH')

# Boxplot Consumo por Línea (Acero)
sns.boxplot(data=acero, x='linea', y='consumo', ax=axes[1], palette='Set3')
axes[1].set_title('Consumo Energético por Línea de Producción', fontsize=12, fontweight='bold')
axes[1].set_xlabel('Línea de Producción (A, B, C)')
axes[1].set_ylabel('Consumo (MWh)')

plt.tight_layout()
plt.show()

print("=== INTERPRETACIÓN DE BOXPLOTS ===")
print("Soils: Se observa diferencia de medianas entre Depression vs Top")
print("Acero: La línea C parece tener mayor variabilidad y posibles outliers")

📝 Equivalente en R:

# Boxplots
ggplot(Soils, aes(x=Contour, y=pH, fill=Contour)) + geom_boxplot()
ggplot(acero, aes(x=linea, y=consumo, fill=linea)) + geom_boxplot()

Scatterplots (Relaciones Bivariadas Iniciales)

Prompt sugerido: “Interpreta la dirección, forma y fuerza de la relación visual entre estas variables. ¿Qué indica la dispersión de los puntos?”

fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# Scatterplot Ca vs pH en Soils (relación química esperada)
axes[0].scatter(soils['pH'], soils['Ca'], c=soils['Block'].astype('category').cat.codes, 
                cmap='viridis', s=100, alpha=0.7, edgecolors='black')
axes[0].set_xlabel('pH del Suelo')
axes[0].set_ylabel('Calcio (me/100g)')
axes[0].set_title('Relación pH vs Calcio (Colores por Bloque)', fontweight='bold')
z = np.polyfit(soils['pH'], soils['Ca'], 1)
p = np.poly1d(z)
axes[0].plot(soils['pH'], p(soils['pH']), "r--", alpha=0.8)

# Scatterplot Consumo vs Producción Total en Acero
axes[1].scatter(acero['prodtotal'], acero['consumo'], c=acero['linea'].astype('category').cat.codes,
                cmap='coolwarm', s=80, alpha=0.7, edgecolors='black')
axes[1].set_xlabel('Producción Total (Toneladas)')
axes[1].set_ylabel('Consumo Energético (MWh)')
axes[1].set_title('Consumo vs Producción Total (Colores por Línea)', fontweight='bold')

# Línea de tendencia
z2 = np.polyfit(acero['prodtotal'], acero['consumo'], 1)
p2 = np.poly1d(z2)
axes[1].plot(acero['prodtotal'], p2(acero['prodtotal']), "r--", alpha=0.8, linewidth=2)

plt.tight_layout()
plt.show()

🔍 SEMANA 3: EDA Estructurado I (Análisis Univariado)

🎯 Idea Central: Proceso sistemático de inspección estructural, manejo de valores faltantes, análisis de forma (sesgo y curtosis).

Celda de texto: Instrucciones GitHub - Versión 3

💾 GUARDAR COPIA EN GITHUB: Guardar como:
"Estadistica_Aplicada_Semana3_EDA_Univariado.ipynb"
Commit: "Versión 3: EDA Estructurado I - Análisis Univariado y Detección de Datos Faltantes"

Paso 1: Inspección Estructural y Valores Faltantes

Prompt sugerido: “Explica la importancia de detectar valores faltantes antes de cualquier análisis y qué estrategias podríamos aplicar según el tipo de variable”

print("=== INSPECCIÓN ESTRUCTURAL COMPLETA ===")

# Dataset Soils
print("\n1. ESTRUCTURA SOILS:")
print(f"   Filas: {soils.shape[0]} | Columnas: {soils.shape[1]}")
print(f"   Variables categóricas: {soils.select_dtypes(include=['object', 'category']).shape[1]}")
print(f"   Variables numéricas: {soils.select_dtypes(include=[np.number]).shape[1]}")

# Verificación de valores faltantes en Soils
missing_soils = soils.isnull().sum()
print(f"\n   Valores faltantes: {missing_soils.sum()} (Dataset completo)")

# Dataset Acero
print("\n2. ESTRUCTURA ACERO:")
print(f"   Filas: {acero.shape[0]} | Columnas: {acero.shape[1]}")

# Limpieza de datos: Acero tiene formato decimal con coma, verificar tipos
print(f"\n   Tipos de datos originales:")
print(acero.dtypes)

# Conversión de variables numéricas (por si quedaron como object por las comas)
cols_numericas = ['consumo', 'pr.tbc', 'pr.cc', 'pr.ca', 'pr.galv1', 'pr.galv2', 'pr.pint', 'prodtotal']
for col in cols_numericas:
    if acero[col].dtype == 'object':
        acero[col] = acero[col].str.replace(',', '.').astype(float)

print(f"\n   Valores faltantes por variable:")
missing_acero = acero.isnull().sum()
print(missing_acero[missing_acero > 0] if missing_acero.sum() > 0 else "   No hay valores faltantes")

📝 Equivalente en R:

# Inspección estructural
str(Soils)
str(acero)

# Valores faltantes
colSums(is.na(Soils))
colSums(is.na(acero))

# Resumen estadístico
summary(Soils)
summary(acero)

Paso 2: Análisis de Forma de Distribución (Sesgo y Curtosis)

Prompt sugerido: “Explica qué implica prácticamente un sesgo positivo o negativo en la producción industrial, y qué decisión tomaríamos si encontramos curtosis alta (datos muy concentrados o con colas pesadas)”

def analizar_forma(df, variables, nombre_dataset):
    """
    Análisis completo de forma de distribución
    """
    print(f"\n=== ANÁLISIS DE FORMA - {nombre_dataset} ===")
    resultados = []
    
    for var in variables:
        if var in df.columns:
            datos = df[var].dropna()
            s = skew(datos)
            k = kurtosis(datos)  # Exceso de curtosis (Fisher)
            
            # Interpretación del sesgo
            if abs(s) < 0.5:
                interp_sesgo = "Aprox. Simétrica"
            elif s > 0:
                interp_sesgo = "Asim. Derecha (cola hacia valores altos)"
            else:
                interp_sesgo = "Asim. Izquierda (cola hacia valores bajos)"
            
            # Interpretación de curtosis
            if k > 0:
                interp_curt = "Leptocúrtica (colas pesadas, pico agudo)"
            elif k < 0:
                interp_curt = "Platicúrtica (colas ligeras, pico plano)"
            else:
                interp_curt = "Mesocúrtica (similar a normal)"
            
            resultados.append({
                'Variable': var,
                'Sesgo': round(s, 3),
                'Interpretación_Sesgo': interp_sesgo,
                'Curtosis': round(k, 3),
                'Interpretación_Curtosis': interp_curt
            })
            
            print(f"\n{var}:")
            print(f"   Sesgo: {s:.3f} -> {interp_sesgo}")
            print(f"   Curtosis: {k:.3f} -> {interp_curt}")
    
    return pd.DataFrame(resultados)

# Análisis para Soils
vars_soils = ['pH', 'N', 'Dens', 'Ca', 'Conduc']
tabla_soils = analizar_forma(soils, vars_soils, "SOILS")

# Análisis para Acero
vars_acero = ['consumo', 'prodtotal', 'naverias']
tabla_acero = analizar_forma(acero, vars_acero, "ACERO")

# Visualización de sesgo
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# Q-Q plot para verificar normalidad (pH)
stats.probplot(soils['pH'], dist="norm", plot=axes[0,0])
axes[0,0].set_title('Q-Q Plot: pH (Soils) - ¿Normalidad?')

# Q-Q plot para consumo ( probablemente no normal)
stats.probplot(acero['consumo'], dist="norm", plot=axes[0,1])
axes[0,1].set_title('Q-Q Plot: Consumo (Acero)')

# Histograma con superposición de curva normal (pH)
axes[1,0].hist(soils['pH'], bins=10, density=True, alpha=0.7, color='green')
mu, sigma = stats.norm.fit(soils['pH'])
x = np.linspace(soils['pH'].min(), soils['pH'].max(), 100)
axes[1,0].plot(x, stats.norm.pdf(x, mu, sigma), 'r-', linewidth=2)
axes[1,0].set_title('pH vs Distribución Normal Teórica')

# Histograma consumo
axes[1,1].hist(acero['consumo'], bins=15, density=True, alpha=0.7, color='blue')
mu2, sigma2 = stats.norm.fit(acero['consumo'])
x2 = np.linspace(acero['consumo'].min(), acero['consumo'].max(), 100)
axes[1,1].plot(x2, stats.norm.pdf(x2, mu2, sigma2), 'r-', linewidth=2)
axes[1,1].set_title('Consumo vs Distribución Normal Teórica (¡Diferente!)')

plt.tight_layout()
plt.show()

📝 Equivalente en R:

library(moments)

# Función de análisis
analizar_forma <- function(x, nombre) {
  s <- skewness(x, na.rm=TRUE)
  k <- kurtosis(x, na.rm=TRUE) - 3  # Convertir a exceso de curtosis
  
  cat("\nVariable:", nombre, "\n")
  cat("Sesgo:", s, "\n")
  cat("Curtosis (exceso):", k, "\n")
  
  # Q-Q plots
  qqnorm(x, main=paste("Q-Q Plot:", nombre))
  qqline(x, col="red")
}

# Aplicar
analizar_forma(Soils$pH, "pH")
analizar_forma(acero$consumo, "consumo")

# Tests de normalidad
shapiro.test(Soils$pH)
shapiro.test(acero$consumo)

🔗 SEMANA 4: EDA Estructurado II (Análisis Bivariado y Correlación)

🎯 Idea Central: Análisis de relaciones entre variables, correlación lineal (Pearson) y monótona (Spearman), matrices de correlación, advertencia: correlación ≠ causalidad.

Celda de texto: Instrucciones GitHub - Versión 4

💾 GUARDAR COPIA EN GITHUB: Guardar como:
"Estadistica_Aplicada_Semana4_EDA_Bivariado.ipynb"
Commit: "Versión 4: EDA Estructurado II - Correlaciones y Análisis Multivariado FINAL"

Correlación de Pearson vs Spearman

Prompt sugerido: “Explica la diferencia fundamental entre ambos coeficientes de correlación, cuándo usar cada uno según el tipo de relación (lineal vs monótona) y la presencia de outliers”

print("=== ANÁLISIS DE CORRELACIONES ===")

# Selección de variables numéricas para análisis
vars_soils_num = ['pH', 'N', 'P', 'Ca', 'Mg', 'K', 'Conduc']
vars_acero_num = ['consumo', 'pr.tbc', 'pr.cc', 'prodtotal', 'naverias']

# Dataset Soils - Correlaciones
print("\n--- SOILS: Pearson vs Spearman ---")
soils_num = soils[vars_soils_num].dropna()

for var1, var2 in [('pH', 'Ca'), ('N', 'P'), ('Ca', 'Mg')]:
    # Pearson (lineal)
    r_pearson, p_pearson = pearsonr(soils_num[var1], soils_num[var2])
    # Spearman (rangos, robusto a outliers)
    r_spearman, p_spearman = spearmanr(soils_num[var1], soils_num[var2])
    
    print(f"\n{var1} vs {var2}:")
    print(f"   Pearson r={r_pearson:.3f} (p={p_pearson:.3f}) - Relación LINEAL")
    print(f"   Spearman ρ={r_spearman:.3f} (p={p_spearman:.3f}) - Relación MONÓTONA")
    
    if abs(r_pearson - r_spearman) > 0.1:
        print(f"   ⚠️ Diferencia significativa: Posible outlier o relación no lineal")

# Dataset Acero - Correlaciones clave
print("\n--- ACERO: Consumo vs Variables de Producción ---")
acero_num = acero[vars_acero_num].dropna()

for var in ['pr.tbc', 'pr.cc', 'prodtotal']:
    r_pearson, _ = pearsonr(acero_num['consumo'], acero_num[var])
    r_spearman, _ = spearmanr(acero_num['consumo'], acero_num[var])
    print(f"\nConsumo vs {var}:")
    print(f"   Pearson: {r_pearson:.3f} | Spearman: {r_spearman:.3f}")

📝 Equivalente en R:

# Correlaciones
cor(Soils[, c("pH", "Ca", "N", "P")], method="pearson")
cor(Soils[, c("pH", "Ca", "N", "P")], method="spearman")

# Test de correlación
cor.test(acero$consumo, acero$prodtotal, method="pearson")
cor.test(acero$consumo, acero$prodtotal, method="spearman")

Matriz de Correlación y Multicolinealidad

Prompt sugerido: “Explica cómo detectar multicolinealidad en la matriz de correlación y por qué es problemático para modelos predictivos tener variables con correlación > 0.8”

fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# Matriz de correlación Soils
corr_soils = soils[vars_soils_num].corr()
mask = np.triu(np.ones_like(corr_soils, dtype=bool))  # Máscara triangular superior
sns.heatmap(corr_soils, mask=mask, annot=True, fmt='.2f', cmap='coolwarm', 
            center=0, square=True, ax=axes[0], cbar_kws={"shrink": .8})
axes[0].set_title('Matriz Correlación - Características del Suelo', fontweight='bold')

# Matriz de correlación Acero
corr_acero = acero[vars_acero_num].corr()
sns.heatmap(corr_acero, annot=True, fmt='.2f', cmap='RdBu_r', 
            center=0, square=True, ax=axes[1], vmin=-1, vmax=1)
axes[1].set_title('Matriz Correlación - Variables de Producción', fontweight='bold')

plt.tight_layout()
plt.show()

# Detección de multicolinealidad (pares altamente correlacionados)
print("\n=== DETECCIÓN DE MULTICOLINEALIDAD (|r| > 0.8) ===")

def detectar_multicolinealidad(corr_matrix, dataset_name):
    high_corr = []
    for i in range(len(corr_matrix.columns)):
        for j in range(i+1, len(corr_matrix.columns)):
            if abs(corr_matrix.iloc[i,j]) > 0.8:
                high_corr.append({
                    'Variable 1': corr_matrix.columns[i],
                    'Variable 2': corr_matrix.columns[j],
                    'Correlación': round(corr_matrix.iloc[i,j], 3)
                })
    
    if high_corr:
        print(f"\n{dataset_name} - ALERTAS DE MULTICOLINEALIDAD:")
        for item in high_corr:
            print(f"   ⚠️ {item['Variable 1']} vs {item['Variable 2']}: {item['Correlación']}")
    else:
        print(f"\n{dataset_name}: No se detectó multicolinealidad severa (r < 0.8)")

detectar_multicolinealidad(corr_soils, "SOILS")
detectar_multicolinealidad(corr_acero, "ACERO")

# Ejemplo de correlación espuria (ejemplo didáctico)
print("\n=== ADVERTENCIA: CORRELACIÓN ≠ CAUSALIDAD ===")
print("Ejemplo: Podría haber alta correlación entre 'naverias' y 'consumo'")
print("¿Significa que las averías CAUSAN mayor consumo? No necesariamente.")
print("Variable oculta posible: 'temperatura alta' podría causar ambas cosas.")

Análisis por Grupos (Estratificación)

Prompt sugerido: “Explica por qué es importante analizar correlaciones dentro de subgrupos (stratificación) y cómo el efecto Simpson puede cambiar nuestras conclusiones”

# Correlación Consumo vs Producción Total por Línea (Estratificación)
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

for idx, linea in enumerate(['A', 'B', 'C']):
    datos_linea = acero[acero['linea'] == linea]
    if len(datos_linea) > 5:
        axes[idx].scatter(datos_linea['prodtotal'], datos_linea['consumo'], 
                         s=100, alpha=0.7, label=f'Línea {linea}')
        
        # Línea de regresión por grupo
        z = np.polyfit(datos_linea['prodtotal'], datos_linea['consumo'], 1)
        p = np.poly1d(z)
        axes[idx].plot(datos_linea['prodtotal'], p(datos_linea['prodtotal']), "r--", alpha=0.8)
        
        # Calcular correlación por grupo
        r, pval = pearsonr(datos_linea['prodtotal'], datos_linea['consumo'])
        axes[idx].set_title(f'Línea {linea}\nr = {r:.3f}, p = {pval:.3f}', fontweight='bold')
        axes[idx].set_xlabel('Producción Total')
        axes[idx].set_ylabel('Consumo (MWh)')

plt.suptitle('Correlación por Subgrupos (Efecto de Interacción)', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()

# Correlación Soils por Contour
print("\n=== CORRELACIÓN pH vs Ca POR TIPO DE CONTORNO ===")
for contorno in soils['Contour'].unique():
    subset = soils[soils['Contour'] == contorno]
    if len(subset) > 3:
        r, p = pearsonr(subset['pH'], subset['Ca'])
        print(f"{contorno}: r = {r:.3f} (n={len(subset)})")

📝 Equivalente en R:

# Correlaciones por grupo con dplyr
library(dplyr)

acero %>%
  group_by(linea) %>%
  summarise(
    cor_pearson = cor(consumo, prodtotal),
    n = n()
  )

# Visualización facetada
ggplot(acero, aes(x=prodtotal, y=consumo)) +
  geom_point() +
  geom_smooth(method="lm") +
  facet_wrap(~linea) +
  labs(title="Correlación por Línea de Producción")

📋 CELDA FINAL: Guía Paso a Paso - Conversión a PDF y Alojamiento en GitHub

Instrucciones detalladas para el estudiante:

Paso 1: Preparar el Notebook para PDF

  1. Ejecutar todas las celdas (Runtime > Run all) y verificar que no haya errores
  2. Ajustar anchos de celdas: Asegurar que tablas y gráficos no se corten
  3. Ocultar código innecesario: Usar la opción de Colab para ocultar celdas de instalación si es necesario

Paso 2: Descargar como PDF desde Colab

# Celda de código para generar versión "limpia" antes de PDF
# Ocultar warnings y mensajes de instalación
print("✅ Notebook listo para exportación a PDF")
print("Instrucciones manuales:")
print("1. Archivo > Descargar > Descargar .ipynb (para respaldo)")
print("2. Para PDF: Archivo > Imprimir (Ctrl+P) > Guardar como PDF")
print("   - Configurar: Escala 'Ajustar al ancho'")
print("   - Habilitar: Gráficos de fondo")
print("   - Deshabilitar: Encabezados y pies de página (opcional)")
print("3. Alternativa: File > Download > Download .html, luego convertir HTML a PDF")

Paso 3: Subir a GitHub (Repositorio de Versiones)

  1. Crear repositorio en GitHub (ej: Estadistica-Aplicada-2026)
  2. Subir los 4 archivos del notebook con versionado:
    • Estadistica_Aplicada_Semana1_Fundamentos.ipynb
    • Estadistica_Aplicada_Semana2_Visualizacion.ipynb
    • Estadistica_Aplicada_Semana3_EDA_Univariado.ipynb
    • Estadistica_Aplicada_Semana4_EDA_Bivariado.ipynb
  3. Subir el PDF final: Estadistica_Aplicada_Integracion_Final.pdf
  4. Opcional: Crear carpeta /data y subir acero.csv para reproducibilidad

Paso 4: Publicación en RPubs (Para la versión en R/RMarkdown)

  1. Crear cuenta gratuita en RStudio Cloud (Posit Cloud)
  2. Nuevo proyecto > New File > R Markdown
  3. Copiar el código R equivalente (incluido en las celdas de texto de este notebook)
  4. Knit > HTML
  5. Click en “Publish” > RPubs (requiere cuenta gratuita)
  6. Compartir URL generada en el README del GitHub

Paso 5: Documentación Final en GitHub

Crear archivo README.md con:

# Estadística Aplicada con Python y R
## Universidad de Sucre - Ingeniería

### Contenido del Repositorio:
- **Semana 1:** Fundamentos de estadística, tipos de datos y escalas
- **Semana 2:** Visualización básica (histogramas, boxplots, scatterplots)
- **Semana 3:** EDA Univariado (sesgo, curtosis, valores faltantes)
- **Semana 4:** EDA Bivariado (correlaciones Pearson/Spearman)

### Datasets utilizados:
- Soils (carData de R) - Estadísticas de suelos
- Acero.csv - Producción industrial de acero (117 observaciones)

### Enlaces:
- [Ver publicación RPubs](URL_AQUI)
- [Descargar PDF completo](Estadistica_Aplicada_Integracion_Final.pdf)

🎓 Resumen de Prompts Sugeridos para Interacción con IA

Para cada celda de código, el estudiante puede usar estos prompts:

  1. “Explica línea por línea el código anterior y justifica por qué se usó ese método específico”
  2. “Interpreta los resultados numéricos obtenidos en contexto de ingeniería (agrícola o industrial)”
  3. “¿Qué errores comunes podrían ocurrir si se interpreta mal este análisis?”
  4. “Sugiere una aplicación práctica adicional para estos datos en el campo profesional”
  5. “Convierte este análisis a su equivalente en R y explica las diferencias sintácticas”