lección 3
Seaborn: cuatro gráficos para explorar antes de decidir
Los cuatro gráficos que usas antes de presentar nada: histograma, boxplot, scatter y heatmap. Seaborn los hace en una línea para que explores rápido y decidas que investigar.
⏱ 50 min
Matplotlib te da control total: puedes mover cada etiqueta, cada línea, cada pixel. Pero ese control tiene un coste. Hacer un histograma con densidad superpuesta, un boxplot comparando cinco grupos, o un heatmap de correlaciones te cuesta entre diez y veinte líneas de código cada vez. Seaborn resuelve ese tradeoff: una sola línea donde matplotlib necesita diez, con decisiones estadísticas ya tomadas por defecto. Es el mismo compromiso que en cocina: hacerlo todo desde cero te da libertad absoluta, usar una receta semi-preparada te da velocidad. Y cuando estás explorando datos — intentando entender qué pasa antes de presentar nada — la velocidad gana.
Seaborn no sustituye a matplotlib. Se monta encima, como un piloto automático sobre los mandos manuales. Todo lo que genera seaborn ES un objeto de matplotlib, y puedes retocarlo después con las mismas herramientas de siempre (ax.set_title, ax.spines, fig.savefig). La diferencia es el punto de partida: donde matplotlib te da un lienzo en blanco y dice "dibuja lo que quieras", seaborn te da un gráfico estadístico completo y dice "retoca lo que no te guste". Para exploracion — el momento en que TU quieres entender los datos, no en que se los presentas a otros — esa diferencia vale oro.
## Explorar vs presentar: dos momentos, dos herramientas
Un análisis tiene dos fases visuales. La primera es privada: tu frente al dato, intentando entender qué pasa. Generas diez gráficos, tiras ocho, te quedas con dos que revelan algo. La segunda es publica: eliges el gráfico que mejor cuenta la historia y lo pules para que un director lo entienda en diez segundos. Seaborn es la herramienta de la primera fase. Matplotlib puro (con todo su control de ejes, colores, anotaciones) es la herramienta de la segunda. Mezclar las dos fases es el error más común: o pierdes una hora puliendo un gráfico que vas a tirar, o presentas un histograma sin título ni etiquetas porque "ya me lo entiendo yo".
Consejo de senior: cuando explores, no pierdas ni un segundo en títulos ni en colores. Genera el gráfico, miralo, decide si te dice algo. Si la respuesta es "no", borralo y prueba otro. Si la respuesta es "si, aquí hay algo", entonces y solo entonces abres matplotlib y lo pules para la reunion. He visto juniors perder una hora formateando un scatter plot que al final no aporta nada.
## Instalacion y primer contacto
Seaborn se instala con pip, igual que cualquier librería. La convención es importarla como sns — las iniciales de Samuel Norman Seaborn, un personaje de la serie "El Ala Oeste" que le gustaba a Waskom. No es intuitivo, pero es universal: en cualquier tutorial, curso o empresa, sns significa seaborn.
1# Instalacion (una sola vez, en tu entorno virtual)2# pip install seaborn34import seaborn as sns5import matplotlib.pyplot as plt6import pandas as pd78# seaborn trabaja directamente con DataFrames de pandas.9# Esa es una de sus ventajas: le pasas la columna por nombre,10# no tienes que extraerla como lista.
La convención sns es universal: no la cambies
## Gráfico 1: Histograma + KDE — la forma de tus datos
La primera pregunta que te haces con una columna numérica es: como se reparte. No la media, no la suma — la forma. Si los tiempos de respuesta de tu web tienen una campana centrada en 200ms con una cola larga a la derecha, eso te dice que la mayoria de usuarios va bien, pero hay un grupo que sufre. Si los importes de pedido se concentran en dos picos (uno en 15 EUR y otro en 45 EUR), puede que tengas dos segmentos de cliente distintos. El histograma te cuenta esa historia de un vistazo.
Ya viste la distribución como concepto en estadística. Ahora vas a verla con código. Un histograma divide el rango de la variable en intervalos (bins, barras) y cuenta cuantos valores caen en cada uno. El KDE (Kernel Density Estimation, estimación de densidad por núcleo) es la curva suave que aproxima esa forma sin el "escalonado" de las barras. Juntos, te dan la mejor foto de una sola variable.
1import seaborn as sns2import matplotlib.pyplot as plt3import pandas as pd45# Datos inline: tiempos de carga de una web (milisegundos)6tiempos = pd.DataFrame({7 'ms': [120, 135, 142, 155, 160, 162, 170, 175, 180, 185,8 190, 195, 200, 205, 210, 215, 220, 230, 250, 280,9 310, 350, 420, 480, 510, 550, 620, 780, 950, 1200]10})1112fig, ax = plt.subplots(figsize=(8, 5))13sns.histplot(data=tiempos, x='ms', kde=True, bins=12,14 color='#a3e635', edgecolor='#1f291c', ax=ax)1516ax.set_title('Distribucion de tiempos de carga')17ax.set_xlabel('Milisegundos')18ax.set_ylabel('Frecuencia')19ax.axvline(x=500, color='#f59e0b', linestyle='--', label='Umbral SLA (500ms)')20ax.legend()21plt.tight_layout()22plt.savefig('histograma_tiempos.png', dpi=150)23plt.close()
Un histograma con KDE revela la cola larga: la mayoria va bien, pero hay usuarios sufriendo
Que te dice este gráfico: la mayoria de cargas están entre 120 y 250ms (bien), pero hay una cola que se estira hasta 1200ms. Esos son los usuarios que se van. Un promedio de 300ms parece aceptable — pero el histograma te dice que ese promedio esconde dos mundos distintos. Exactamente el problema de la media que miente, ahora visible de un solo vistazo.
## Gráfico 2: Boxplot — dispersiones y outliers de un vistazo
El boxplot (diagrama de caja y bigotes) es el grafico que responde: hay valores raros que arrastran la media, y cuanto se dispersan los datos. Es una radiografia de cinco numeros: el extremo del bigote inferior, el primer cuartil (Q1, percentil 25), la mediana (Q2, percentil 50), el tercer cuartil (Q3, percentil 75) y el extremo del bigote superior. Ojo con esos dos extremos, porque es el malentendido mas comun del grafico: NO son el minimo y el maximo de tus datos. Son el dato mas lejano que aun cae dentro de 1,5 veces el rango intercuartilico, y de hecho tienen que ser eso: si los bigotes llegaran al minimo y al maximo reales, por definicion no podria quedar ningun punto fuera, y entonces el boxplot no podria mostrar outliers. Los puntos que caen fuera de los bigotes son precisamente los outliers, los valores atipicos.
El IQR (Interquartile Range, rango intercuartílico) es la anchura de la caja: Q3 menos Q1. Los bigotes se extienden hasta 1.5 veces ese IQR por cada lado. Todo lo que cae fuera es un outlier — no necesariamente un error, pero si un valor que merece investigación. Si ves un boxplot con muchos puntos por encima del bigote superior, tienes una cola derecha pesada: pocos valores muy altos están arrastrando tu media hacia arriba.
1import seaborn as sns2import matplotlib.pyplot as plt3import pandas as pd45# Datos inline: importe de pedidos por canal6pedidos = pd.DataFrame({7 'importe': [12, 15, 18, 20, 22, 25, 28, 30, 32, 35,8 38, 40, 42, 45, 48, 50, 55, 60, 65, 70,9 85, 120, 250, 15, 18, 22, 25, 30, 35, 40,10 42, 45, 50, 55, 58, 60, 62, 65, 68, 72,11 75, 80, 90, 95, 100, 110, 130, 160, 200, 350],12 'canal': ['web']*23 + ['app']*2713})1415fig, ax = plt.subplots(figsize=(8, 5))16sns.boxplot(data=pedidos, x='canal', y='importe', hue='canal',17 palette={'web': '#a3e635', 'app': '#22d3ee'},18 legend=False, ax=ax)1920ax.set_title('Importe de pedido por canal: app tiene outliers mas extremos')21ax.set_ylabel('Importe (EUR)')22ax.set_xlabel('')23plt.tight_layout()24plt.savefig('boxplot_canales.png', dpi=150)25plt.close()
Comparar dos boxplots lado a lado revela si un grupo tiene más dispersión que otro
Un outlier no es un error. Es un valor que merece una pregunta. Antes de eliminarlo, investiga: es un pedido real de un cliente corporativo? Es un dato mal introducido? Es un caso de fraude? El boxplot te dice "mira esto"; tu decides que hacer después. El peor hábito al empezar es borrar outliers sin preguntar por qué están ahi.
## Gráfico 3: Scatter plot — relación entre dos variables
El scatter plot (gráfico de dispersión) responde una pregunta distinta: cuando una variable sube, la otra también sube? Es decir: hay correlación? Ya viste en estadística que correlación no implica causalidad. Pero ANTES de preguntarte si hay causa, necesitas ver si hay patron. El scatter es el primer paso: pones una variable en X, otra en Y, y miras si los puntos forman algo parecido a una línea, una nube, o una forma rara.
Seaborn anade un extra: puede superponer una línea de regresión con su intervalo de confianza (la banda sombreada que dice "si la relación es lineal, los datos estarian más o menos aquí"). Eso lo hace sns.regplot o sns.lmplot. Pero cuidado: que seaborn te pinte una línea no significa que la relación sea lineal ni que sea real. La línea es una herramienta visual, no una prueba estadística.
1import seaborn as sns2import matplotlib.pyplot as plt3import pandas as pd45# Datos inline: horas de soporte al mes vs satisfaccion del cliente6soporte = pd.DataFrame({7 'horas_soporte': [0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6,8 7, 8, 1, 1.5, 2, 2.5, 3, 4, 5, 6,9 0.5, 1, 3, 4, 5, 7, 8, 9, 10, 12],10 'satisfaccion': [9.1, 8.8, 8.5, 8.2, 7.9, 7.5, 7.2, 6.8, 6.2, 5.5,11 5.0, 4.5, 9.0, 8.6, 8.0, 7.8, 7.3, 6.5, 5.8, 5.2,12 9.2, 8.9, 7.4, 6.9, 6.0, 5.1, 4.8, 4.2, 3.8, 3.5]13})1415fig, ax = plt.subplots(figsize=(8, 5))16sns.scatterplot(data=soporte, x='horas_soporte', y='satisfaccion',17 color='#22d3ee', s=80, alpha=0.7, ax=ax)1819# Linea de tendencia para visualizar la direccion20sns.regplot(data=soporte, x='horas_soporte', y='satisfaccion',21 scatter=False, color='#f59e0b', ax=ax)2223ax.set_title('Mas horas de soporte = menor satisfaccion (correlacion negativa)')24ax.set_xlabel('Horas de soporte / mes')25ax.set_ylabel('Satisfaccion (1-10)')26plt.tight_layout()27plt.savefig('scatter_soporte.png', dpi=150)28plt.close()
El scatter revela una relación negativa clara: a más soporte necesitado, menos contento esta el cliente
Consejo de senior: un scatter con muchos puntos se vuelve un manchurron ilegible. Si tienes más de 500 puntos, usa alpha=0.3 para ver donde se concentra la masa, o sns.jointplot(kind="hex") que agrupa puntos en celdas hexagonales con color por densidad. Si tienes más de 10.000, el scatter ya no es tu herramienta: necesitas un heatmap de densidad o un muestreo.
## Gráfico 4: Heatmap de correlación — la visión panoramica
El scatter te dice si DOS variables están relacionadas. Pero cuando tienes un DataFrame con diez columnas numéricas, hacer un scatter de cada par son 45 combinaciones. Necesitas una vista de pájaro que te diga: cuales son los pares interesantes. Eso es el heatmap de correlacion: una tabla coloreada donde cada celda muestra el coeficiente de correlación entre dos variables (de -1 a +1), y el color te dice de un vistazo donde hay relaciones fuertes.
1import seaborn as sns2import matplotlib.pyplot as plt3import pandas as pd4import numpy as np56# Datos inline: metricas de una app de suscripcion7np.random.seed(42)8n = 509datos = pd.DataFrame({10 'sesiones_semana': np.random.poisson(5, n),11 'minutos_sesion': np.random.normal(12, 4, n).clip(1),12 'funciones_usadas': np.random.poisson(8, n),13 'dias_desde_registro': np.random.randint(30, 365, n),14 'tickets_soporte': np.random.poisson(1.5, n),15 'nps': np.random.normal(7, 1.5, n).clip(1, 10).round(1),16})17# Crear correlacion artificial para el ejemplo18datos['minutos_sesion'] = datos['sesiones_semana'] * 2 + np.random.normal(0, 2, n)19datos['nps'] = (10 - datos['tickets_soporte'] * 1.2 + np.random.normal(0, 1, n)).clip(1, 10)2021corr = datos.corr()2223fig, ax = plt.subplots(figsize=(8, 6))24sns.heatmap(corr, annot=True, fmt='.2f', cmap='coolwarm',25 center=0, vmin=-1, vmax=1,26 linewidths=0.5, linecolor='#1f291c', ax=ax)2728ax.set_title('Mapa de correlaciones: donde estan las relaciones fuertes?')29plt.tight_layout()30plt.savefig('heatmap_correlacion.png', dpi=150)31plt.close()
El heatmap de correlacion te dice donde mirar: las celdas con color intenso son las relaciones que merecen un scatter
El heatmap es un mapa del tesoro: te dice donde cavar. Si ves que nps y tickets_soporte tienen correlación -0.90, eso te dice "ve a hacer un scatter de esos dos y confirma la relación". No hagas 45 scatter plots a ciegas: deja que el heatmap te diga cuales merecen la pena.
## Cuando usar cada gráfico: el mapa rápido
## Seaborn vs matplotlib: cuando uso cada uno
La pregunta no es cual es mejor — es cual es mejor para ESTE momento. Seaborn brilla cuando estás explorando: quieres ver muchos gráficos rápido, la estética por defecto ya es decente, y no necesitas controlar cada pixel. Matplotlib brilla cuando presentas: quieres un título con el mensaje exacto, colores corporativos, anotaciones que guien el ojo del director, y cada elemento colocado donde tu decides. En la practica, un análisis empieza con seaborn y termina con matplotlib. Y muchas veces, el gráfico final es un hibrido: empiezas con seaborn y luego retocas el axes con métodos de matplotlib.
- Seaborn -- Explorar distribuciones, comparar grupos, ver correlaciones. Una línea de código por gráfico. Poca personalizacion necesaria. Output: tu cuaderno de notas.
- Matplotlib -- Presentar un hallazgo concreto. Control total sobre cada pixel. Título que dice la conclusión, no la métrica. Anotaciones, colores corporativos. Output: la reunion del lunes.
- Ambos juntos -- El patron más profesional: sns.histplot(..., ax=ax) genera el gráfico base, y después ax.set_title() / ax.axvline() / ax.legend() lo retoca para la presentación. Es posible porque seaborn devuelve objetos de matplotlib.
1import seaborn as sns2import matplotlib.pyplot as plt3import pandas as pd45# El patron hibrido: seaborn genera, matplotlib retoca6tiempos = pd.DataFrame({7 'ms': [120, 135, 142, 155, 160, 170, 180, 200, 220, 250,8 280, 310, 350, 420, 510, 620, 780, 950, 1200, 190],9 'pagina': ['home']*10 + ['checkout']*1010})1112fig, ax = plt.subplots(figsize=(8, 5))1314# Paso 1: seaborn genera el grafico estadistico15sns.boxplot(data=tiempos, x='pagina', y='ms', hue='pagina',16 palette={'home': '#a3e635', 'checkout': '#f59e0b'},17 legend=False, ax=ax)1819# Paso 2: matplotlib lo retoca para la reunion20ax.set_title('Checkout tarda casi 3x mas que Home — priorizar optimizacion',21 fontsize=13, fontweight='bold')22ax.set_ylabel('Tiempo de carga (ms)')23ax.set_xlabel('')24ax.axhline(y=500, color='#f59e0b', linestyle='--', alpha=0.5)25ax.text(1.3, 510, 'SLA: 500ms', color='#f59e0b', fontsize=9)26ax.spines['top'].set_visible(False)27ax.spines['right'].set_visible(False)2829plt.tight_layout()30plt.savefig('boxplot_presentacion.png', dpi=150)31plt.close()
El patron hibrido: seaborn para la forma, matplotlib para el mensaje. El título dice la conclusión, no la métrica.
Consejo de senior: el título de un gráfico de presentación nunca es la métrica ("Tiempo de carga por pagina"). El título ES la conclusión ("Checkout tarda casi 3x más que Home — priorizar optimización"). Si el director solo lee el título — y muchos solo leen el título — tiene que entender la decisión. La métrica va en los ejes.
## El flujo completo: de los datos crudos al hallazgo
Vamos a juntar los cuatro gráficos en una secuencia real. Imagina que te pasan un dataset de clientes de un SaaS y te dicen "el churn (la tasa de baja) ha subido un 3%. Averigua por qué". Tu no sabes nada del dato todavia. El flujo de exploracion seria:
- 01.Heatmap de correlacion: que variables se mueven con churn? Ves que "tickets_soporte" y "días_sin_login" tienen correlación alta con churn.
- 02.Scatter de las sospechosas: scatter de tickets_soporte vs días_sin_login, coloreado por churn/no-churn. Confirmas que los que se van están en la esquina alta-alta.
- 03.Boxplot comparativo: boxplot de tickets_soporte separando churn=si vs churn=no. Confirmas que los que se van tienen mediana 5 tickets/mes vs 1.5 de los que se quedan.
- 04.Histograma del hallazgo: histograma de tickets_soporte solo para los churners. Ves un pico en las semanas 3-4 tras el registro. La cola de soporte se carga a los nuevos.
- 05.Conclusión: el churn no es de precio (la hipotesis del stakeholder). Es de experiencia en las primeras semanas. Los que necesitan mucho soporte al principio se van. La accion: mejorar el onboarding, no bajar el precio.
Fíjate en que cada gráfico te lleva al siguiente. El heatmap dice "mira aquí". El scatter confirma la dirección. El boxplot cuantifica la diferencia. El histograma encuentra el cuando. No son cuatro gráficos sueltos: son cuatro pasos de una investigación. Y el último paso no es un gráfico — es una frase que cambia una decisión.
## Errores comunes con gráficos estadísticos
- Histograma con demasiados bins -- Si pones 50 barras para 100 datos, cada barra tiene 2 valores y el gráfico parece ruido. Regla de oro: empieza con bins=20 y ajusta.
- Confundir correlación con causalidad en el scatter -- "Los helados causan ahogamientos" porque ambos suben en verano. El scatter muestra co-movimiento, no causa.
- Borrar outliers del boxplot sin investigar -- Un outlier puede ser un error de carga, pero también el mejor cliente de la empresa. Antes de tirarlo, pregunta por qué esta ahi.
- Heatmap con variables redundantes -- Si metes "ingresos" e "ingresos_netos" (que son la misma cosa menos un porcentaje fijo), la correlación será 0.99 y no dice nada útil.
- Interpretar un scatter de 20 puntos -- Con menos de 30-50 observaciones, cualquier patron puede ser ruido. No declares correlación con un puñado de puntos.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...
Regístrate para guardar tu progreso.