Grammar of Graphics vs. Imperative Plotting
Las bibliotecas de visualización de Python parecen un montón de herramientas no relacionadas: matplotlib, seaborn, plotly, altair; pero en realidad se dividen en dos modelos mentales para describir un gráfico.
Un modelo es imperativo: dibujas una figura, añades ejes, colocas elementos gráficos en ella y modificas ese estado hasta que se vea bien.
El otro es declarativo, siguiendo la gramática de gráficos: describes una correspondencia entre columnas de datos y propiedades visuales, y un motor de renderizado decide cómo dibujarlo.
Fundamentos de Visualización muestra la sintaxis concreta para elegir un tipo de gráfico; esta página trata sobre qué modelo sigue una biblioteca y por qué esa elección importa más que qué biblioteca tiene el tema predeterminado más bonito.
Resumen
- Un gráfico es fundamentalmente una correspondencia de columnas de datos a codificaciones visuales (posición, color, tamaño, forma); las bibliotecas difieren en si construyes esa correspondencia de forma declarativa (gramática de gráficos) o la dibujas de forma imperativa (figura/ejes/elementos gráficos).
- Por Qué Importa: Elegir el modelo incorrecto para una tarea significa luchar contra la biblioteca: colocar a mano cada elemento en una herramienta creada para codificación declarativa, o luchar contra una gramática rígida cuando necesitas control imperativo único.
- Conceptos Clave: codificación, datos ordenados (formato largo), estado de figura/ejes, especificación declarativa, facetado.
- Cuándo Usar: Utiliza el modelo declarativo para gráficos exploratorios, multivariables y facetados construidos a partir de datos ordenados; utiliza el modelo imperativo cuando necesites control a nivel de píxel, anotaciones personalizadas o diseño específico para publicaciones.
- Limitaciones / Compensaciones: Las gramáticas declarativas son rápidas para formas de gráficos estándar pero resisten diseños únicos y personalizados; las bibliotecas imperativas ofrecen control total pero cada elemento (leyendas, marcas, espaciado) es tu responsabilidad colocarlo correctamente.
- Temas Relacionados: El modelo de figura/ejes de matplotlib, la capa estadística de seaborn sobre matplotlib, la gramática Vega-Lite de Altair, el híbrido interactivo/declarativo de plotly.
Fundamentos
Cada gráfico, sin importar la biblioteca, responde a la misma pregunta subyacente: ¿qué propiedad visual representa qué columna de datos?
La gramática de gráficos (la teoría detrás de ggplot2, y en Python detrás de Altair y plotly express) nombra explícitamente esta correspondencia: una columna de datos se codifica en una posición x, una posición y, un color, un tamaño o una forma, y la biblioteca se encarga del resto.
Dado que la correspondencia se declara en lugar de dibujarse, describir "color por categoría, faceta por región" produce una cuadrícula completa de pequeños múltiplos sin que tengas que escribir un bucle para colocar cada subgráfico.
Este modelo funciona mejor con datos ordenados (formato largo), donde cada fila es una observación y cada columna es una variable; los datos anchos, con forma de hoja de cálculo, deben reorganizarse primero.
matplotlib, por el contrario, sigue un modelo imperativo construido alrededor de una figura (el lienzo) y uno o más ejes (una región de trazado en ese lienzo): creas explícitamente estos objetos, luego llamas a métodos sobre ellos — ax.plot(), ax.set_xlabel(), ax.legend() — para construir el gráfico elemento por elemento.
Una analogía simple: el modelo declarativo es como describir una receta a un chef ("una ensalada con tomate, aderezada ligeramente") y confiar en que él la emplatará; el modelo imperativo es emplatar la ensalada tú mismo, ingrediente por ingrediente, en la posición exacta que elijas.
Ningún modelo es estrictamente "mejor": intercambian automatización por control, y la mayor parte del trabajo real de visualización en Python en una sección como esta se mueve entre ambos dependiendo de la tarea.
Mecánicas e Interacciones
La diferencia mecánica se manifiesta más claramente en lo que sucede cuando agregas una nueva variable a un gráfico.
En el modelo declarativo, agregar una codificación color= o facet= a una especificación de gráfico existente es un cambio de una línea, porque el renderizador (Vega-Lite, bajo Altair; el propio renderizador de plotly, bajo plotly express) recalcula escalas, leyendas y diseño a partir de la especificación actualizada.
En el modelo imperativo, el mismo cambio generalmente significa escribir un bucle sobre los valores del grupo, llamar a ax.plot() una vez por grupo con un color elegido manualmente y construir manualmente una leyenda; la biblioteca no tiene el concepto de "una variable mapeada a color" para actualizar en tu nombre.
seaborn se sitúa en el medio: está construido directamente sobre los ejes imperativos de matplotlib, pero sus funciones (sns.scatterplot, sns.relplot) aceptan una interfaz hue=/col=/row= que imita la codificación de la gramática de gráficos, generando los bucles por grupo y la construcción de leyendas por ti, mientras que aún devuelve objetos de matplotlib que puedes modificar posteriormente de forma imperativa.
Aquí es también donde difieren el rendimiento y la entrega: matplotlib renderiza a una imagen ráster o vectorial estática sin tiempo de ejecución, ideal para informes impresos y PDF; plotly y Altair renderizan a JavaScript interactivo (Plotly.js y Vega-Lite respectivamente) que envía la funcionalidad de pasar el ratón por encima, zoom y desplazamiento a un navegador, a costa de un artefacto de salida mucho más pesado.
import seaborn as sns
# Codificación de estilo declarativo sobre el motor imperativo de matplotlib:
# hue= y col= describen correspondencias; seaborn genera el bucle y la leyenda
sns.relplot(
data=df, x="date", y="value",
hue="category", col="region", # faceta por región, un subgráfico por valor
kind="line",
)Consideraciones Avanzadas y Aplicaciones
Elegir una biblioteca es realmente elegir primero una audiencia y un medio de entrega, y luego un tipo de gráfico.
Un informe estático o un artículo académico favorece la salida PNG/SVG/PDF de matplotlib o seaborn, donde cada píxel es reproducible y no se requiere un tiempo de ejecución de JavaScript para verlo.
Un panel interno o un cuaderno exploratorio favorece plotly o Altair, donde las sugerencias al pasar el ratón y el zoom permiten a un lector interrogar los datos sin un segundo gráfico.
El trabajo exploratorio facetado y multivariable —"muéstrame esta métrica desglosada por tres variables categóricas"— es donde la gramática declarativa demuestra su valía, porque agregar una faceta es un cambio de especificación de una línea en lugar de un bucle reestructurado.
La escala es una restricción real en ambos modelos: renderizar decenas de miles de puntos como marcas individuales de SVG o rastreadas por JavaScript ralentiza o bloquea los renderizadores basados en navegador, por lo que los conjuntos de datos grandes generalmente se submuestrean, se agrupan (hexbin/histograma 2D) o se rasterizan antes de llegar a cualquiera de los tipos de bibliotecas.
Los paneles creados con Streamlit, Dash o Gradio no reemplazan esta elección: son marcos de aplicaciones que incrustan figuras de matplotlib, plotly o Altair dentro de una interfaz web, por lo que la decisión subyacente de gramática frente a imperativo aún debe tomarse por gráfico.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Imperativo (matplotlib) | Control total a nivel de píxel, salida de impresión/PDF madura, no se necesita tiempo de ejecución para ver | Cada leyenda/faceta/correspondencia de color es código manual | Figuras de publicación, gráficos estáticos anotados personalizados |
| Capa estadística (seaborn) | Codificaciones tipo gramática hue/col con estimadores estadísticos incorporados | Todavía devuelve objetos de matplotlib: heredas sus escapes imperativos | Exploración estadística rápida (distribuciones, regresiones) en datos ordenados |
| Gramática declarativa (Altair) | Especificación concisa basada en codificación, leyendas/facetas/escalas automáticas | Vega-Lite tiene que poder expresar lo que quieres, o te quedas atascado | Gráficos exploratorios multivariables, informes basados en especificaciones reproducibles |
| Interactivo/híbrido (plotly) | Interactivo por defecto (zoom/pasar el ratón/desplazamiento), funciona tanto como express (declarativo) como graph_objects (imperativo) | Artefactos de salida más grandes; puede ser excesivo para un gráfico único estático | Paneles, gráficos interactivos incrustados en la web |
Conceptos Erróneos Comunes
- "seaborn es un modelo totalmente diferente de matplotlib." — seaborn es una capa estadística construida directamente sobre los objetos de figura/ejes imperativos de matplotlib; su API
hue=/col=imita la codificación de la gramática de gráficos, pero el objeto subyacente que obtienes sigue siendo unAxesde matplotlib que puedes modificar posteriormente. - "Las bibliotecas declarativas no se pueden personalizar." — Altair y plotly express permiten ambos anulaciones de estilo imperativo sobre la especificación declarativa (escalas personalizadas, ajustes de diseño manuales); la gramática es el camino predeterminado, no el único.
- "Los gráficos interactivos son siempre la mejor opción." — La interactividad agrega una dependencia de tiempo de ejecución de JavaScript y un artefacto de salida mucho más grande, lo que es activamente peor para la impresión, incrustado en correos electrónicos o entregas con restricciones de accesibilidad donde una imagen estática es más simple y portátil.
- "Cualquier forma de DataFrame funciona con cualquier llamada de trazado." — Las bibliotecas de estilo de gramática de gráficos esperan datos ordenados (largos) con una fila por observación; los datos anchos y pivotados generalmente necesitan
.melt()o un redimensionamiento similar antes de que una llamada basada en codificación produzca el gráfico deseado. - "Mostrar más tipos de gráficos significa entregar más información." — Elegir un gráfico es una decisión de comunicación sobre la pregunta de la audiencia, no una decisión técnica sobre qué biblioteca admite la mayor cantidad de tipos de gráficos; un gráfico de barras que un interesado puede leer en cinco segundos a menudo supera a un gráfico técnicamente más rico que nadie analiza correctamente.
Preguntas Frecuentes
¿Qué significa realmente "gramática de gráficos"?
- Es una teoría que describe cualquier gráfico como una correspondencia de columnas de datos a codificaciones visuales: x, y, color, tamaño, forma.
- En lugar de dibujar formas directamente, declaras la correspondencia y un renderizador decide el diseño, las escalas y las leyendas.
- En Python, Altair (a través de Vega-Lite) y plotly express siguen ambos este modelo; ggplot2 en R es la implementación más famosa del modelo.
¿Es matplotlib "peor" porque es imperativo?
No, el control imperativo es exactamente lo que necesitan las figuras estáticas de calidad de publicación y con anotaciones precisas; la compensación es que cada elemento (colocación de leyenda, formato de marcas, espaciado) debe codificarse explícitamente en lugar de inferirse de una correspondencia de datos.
¿Por qué seaborn se siente más como una herramienta de gramática de gráficos a pesar de estar construido sobre matplotlib?
Las funciones de seaborn aceptan argumentos hue=, col= y row= que describen codificaciones como lo haría una gramática declarativa, y genera internamente los bucles por grupo, la asignación de color y la leyenda que de otro modo escribirías a mano en matplotlib puro; pero el objeto que devuelve sigue siendo un Axes de matplotlib, por lo que puedes volver a usar llamadas imperativas en cualquier momento.
¿Por qué mi gráfico de Altair o plotly express se ve mal hasta que reorganizo mis datos?
Ambas bibliotecas esperan datos ordenados y en formato largo —una fila por observación, una columna por variable— porque el paso de codificación mapea directamente las columnas a los canales visuales; los datos anchos/pivotados generalmente necesitan .melt() (o un redimensionamiento equivalente) antes de que una codificación color=/hue= se agrupe correctamente.
¿Cuándo debería elegir una biblioteca interactiva en lugar de una estática?
Elige interactiva (plotly, Altair con sus selecciones interactivas, o un framework de panel) cuando la audiencia necesite explorar los datos por sí misma —hacer zoom en un rango de tiempo, pasar el ratón para obtener valores exactos— y elige estática (matplotlib, seaborn) cuando el gráfico sea un entregable fijo como un informe, artículo o diapositiva de presentación donde la interactividad no aporta valor y una dependencia de tiempo de ejecución es una desventaja.
¿Las bibliotecas de gráficos declarativas escalan a grandes conjuntos de datos de la misma manera?
No muy bien por defecto: tanto los renderizadores de estilo gramática de gráficos como los interactivos rastrean cada punto de datos como una marca individual u objeto DOM/lienzo, por lo que decenas de miles de puntos ralentizan la renderización; los datos grandes generalmente se submuestrean, se agrupan o se rasterizan antes de pasarlos a cualquiera de los tipos de bibliotecas.
¿Cuál es la diferencia entre plotly express y plotly graph_objects?
- plotly express (
px.scatter,px.line) es el punto de entrada declarativo, de estilo gramática de gráficos: pasas un DataFrame y nombres de columnas para las codificaciones. - graph_objects (
go.Figure,go.Scatter) es la API de nivel inferior e imperativa sobre la que se construye el propio express. - Normalmente empiezas con express y solo recurres a graph_objects para personalizaciones que express no expone.
¿Por qué los paneles (Streamlit/Dash/Gradio) no reemplazan esta elección de modelo mental?
Esas herramientas son marcos de aplicaciones para organizar widgets y reactividad en un navegador; los gráficos incrustados dentro de ellas todavía se construyen con matplotlib, plotly o Altair, por lo que la decisión de gramática frente a imperativo se toma por gráfico, no es reemplazada por el marco del panel.
¿Cómo decido qué tipo de gráfico usar para una pregunta dada?
Haz coincidir la codificación con el tipo de pregunta: la comparación entre categorías favorece las barras, una tendencia a lo largo del tiempo favorece una línea, una relación entre dos variables numéricas favorece un dispersograma, y una distribución favorece un histograma o un gráfico de caja/violín; la biblioteca y el modelo que uses son una decisión separada de esta elección del tipo de gráfico.
¿El modelo declarativo de Altair está limitado en comparación con el control imperativo completo?
Altair (a través de Vega-Lite) cubre la gran mayoría de los tipos de gráficos estadísticos estándar y composiciones en capas/facetadas de forma declarativa, y admite superposiciones y anulaciones de configuración para un control más fino, pero los diseños verdaderamente personalizados y específicos de píxeles todavía se logran más fácilmente con una herramienta imperativa como matplotlib.
¿Por qué el mismo conjunto de datos a veces necesita dos bibliotecas de trazado diferentes en un proyecto?
El trabajo exploratorio se beneficia de una herramienta rápida y basada en codificación (seaborn o Altair) para iterar rápidamente a través de muchas combinaciones de variables, mientras que la entrega del informe o panel final a menudo necesita el control estático preciso de matplotlib o la interactividad de plotly; por lo tanto, un proyecto comúnmente explora en un modelo y entrega en otro.
¿Cuál es el mayor error relacionado con la audiencia al elegir un gráfico o una biblioteca?
Optimizar para lo que parece impresionante para el autor en lugar de lo que una audiencia específica puede leer correcta y rápidamente: un gráfico plotly 3D interactivo rara vez es más útil para un interesado que un gráfico de barras claramente etiquetado que responde a su pregunta real.
Relacionados
- Fundamentos de Visualización - la sintaxis concreta de tipos de gráficos y bibliotecas sobre la que se asienta el modelo mental de esta página.
- matplotlib - el modelo imperativo de figura/ejes en la práctica.
- Seaborn - la capa estadística de estilo de codificación construida sobre matplotlib.
- Altair - la biblioteca declarativa de gramática de gráficos en la práctica.
- Plotly - la biblioteca interactiva, híbrida declarativa/imperativa.
- Trazado de Grandes Datos - qué sucede con ambos modelos a escala.
Versiones de la pila: Esta página fue escrita para Python 3.14 (estable) / 3.13 (mantenimiento); por lo demás, es conceptual y no está ligada a una versión específica de la biblioteca de trazado.