Grammar of Graphics vs. Imperative Plotting
As bibliotecas de visualização do Python parecem uma pilha de ferramentas não relacionadas - matplotlib, seaborn, plotly, altair - mas na verdade se dividem em dois modelos mentais para descrever um gráfico.
Um modelo é imperativo: você desenha uma figura, adiciona eixos, coloca elementos visuais nela e muta esse estado até que pareça certo.
O outro é declarativo, seguindo a gramática de gráficos: você descreve um mapeamento de colunas de dados para propriedades visuais, e um motor de renderização decide como desenhá-lo.
Fundamentos de Visualização mostra a sintaxe concreta para escolher um tipo de gráfico; esta página é sobre qual modelo uma biblioteca segue e por que essa escolha importa mais do que qual biblioteca tem o tema padrão mais bonito.
Resumo
- Um gráfico é fundamentalmente um mapeamento de colunas de dados para codificações visuais (posição, cor, tamanho, forma); as bibliotecas diferem se você constrói esse mapeamento declarativamente (gramática de gráficos) ou o desenha imperativamente (figura/eixos/elementos visuais).
- Por Que Importa: Escolher o modelo errado para uma tarefa significa lutar contra a biblioteca - posicionar manualmente cada elemento em uma ferramenta criada para codificação declarativa, ou lutar contra uma gramática rígida quando você precisa de controle imperativo único.
- Conceitos Chave: codificação, dados organizados (formato longo), estado de figura/eixos, especificação declarativa, facetamento.
- Quando Usar: Opte pelo modelo declarativo para gráficos exploratórios, multivariáveis e facetados construídos a partir de dados organizados; opte pelo modelo imperativo quando precisar de controle pixel a pixel, anotações personalizadas ou layout específico para publicação.
- Limitações / Trocas: Gramáticas declarativas são rápidas para formas de gráfico padrão, mas resistem a layouts personalizados e únicos; bibliotecas imperativas dão controle total, mas cada elemento - legendas, ticks, espaçamento - é sua responsabilidade de posicionar corretamente.
- Tópicos Relacionados: modelo de figura/eixos do matplotlib, camada estatística do seaborn sobre o matplotlib, gramática Vega-Lite do Altair, híbrido interativo/declarativo do plotly.
Fundamentos
Todo gráfico, independentemente da biblioteca, responde à mesma pergunta subjacente: qual propriedade visual representa qual coluna de dados?
A gramática de gráficos (a teoria por trás do ggplot2, e em Python por trás do Altair e plotly express) nomeia explicitamente esse mapeamento: uma coluna de dados é codificada em uma posição x, uma posição y, uma cor, um tamanho ou uma forma, e a biblioteca cuida do resto.
Como o mapeamento é declarado em vez de desenhado, descrever "cor por categoria, facetar por região" produz uma grade completa de pequenos múltiplos sem que você precise escrever um loop para posicionar cada subgráfico.
Este modelo funciona melhor em dados organizados (formato longo), onde cada linha é uma observação e cada coluna é uma variável - a etapa de codificação precisa de colunas para mapear, e dados largos, em formato de planilha, precisam ser remodelados primeiro.
O matplotlib, por outro lado, segue um modelo imperativo construído em torno de uma figura (a tela) e um ou mais eixos (uma região de plotagem nessa tela): você cria explicitamente esses objetos e, em seguida, chama métodos neles - ax.plot(), ax.set_xlabel(), ax.legend() - para construir o gráfico elemento visual por elemento visual.
Uma analogia simples: o modelo declarativo é como descrever uma receita a um chef ("uma salada com tomate, levemente temperada") e confiar nele para montá-la; o modelo imperativo é montar a salada você mesmo, ingrediente por ingrediente, na posição exata que você escolher.
Nenhum modelo é estritamente "melhor" - eles trocam automação por controle, e a maior parte do trabalho real de visualização em Python em uma seção como esta transita entre ambos dependendo da tarefa.
Mecânicas e Interações
A diferença mecânica aparece mais claramente no que acontece quando você adiciona uma nova variável a um gráfico.
No modelo declarativo, adicionar uma codificação color= ou facet= a uma especificação de gráfico existente é uma alteração de uma linha, porque o renderizador (Vega-Lite, sob o Altair; o próprio renderizador do plotly, sob o plotly express) recalcula escalas, legendas e layout a partir da especificação atualizada.
No modelo imperativo, a mesma alteração geralmente significa escrever um loop sobre os valores do grupo, chamar ax.plot() uma vez por grupo com uma cor escolhida manualmente e construir manualmente uma legenda - a biblioteca não tem o conceito de "uma variável mapeada para cor" para atualizar em seu nome.
O seaborn fica no meio: ele é construído diretamente sobre os eixos imperativos do matplotlib, mas suas funções (sns.scatterplot, sns.relplot) aceitam uma interface hue=/col=/row= que imita a codificação da gramática de gráficos, gerando os loops por grupo e a construção da legenda para você, enquanto ainda retorna objetos matplotlib que você pode mutar posteriormente de forma imperativa.
É por isso também que o desempenho e a entrega diferem: o matplotlib renderiza para uma imagem raster ou vetorial estática sem tempo de execução, ideal para relatórios impressos e em PDF; o plotly e o Altair renderizam para JavaScript interativo (Plotly.js e Vega-Lite, respectivamente) que envia hover, zoom e pan para um navegador, ao custo de um artefato de saída muito mais pesado.
import seaborn as sns
# codificação em estilo declarativo sobre o motor imperativo do matplotlib:
# hue= e col= descrevem mapeamentos; seaborn gera o loop e a legenda
sns.relplot(
data=df, x="date", y="value",
hue="category", col="region", # facetar por região, um subgráfico por valor
kind="line",
)Considerações Avançadas e Aplicações
Escolher uma biblioteca é realmente escolher um público e um meio de entrega primeiro, e um tipo de gráfico em segundo lugar.
Um relatório estático ou artigo acadêmico favorece a saída PNG/SVG/PDF do matplotlib ou seaborn, onde cada pixel é reproduzível e nenhum tempo de execução JavaScript é necessário para visualizá-lo.
Um painel interno ou um notebook exploratório favorece plotly ou Altair, onde tooltips de hover e zoom permitem que um leitor interrogue os dados sem um segundo gráfico.
Trabalho exploratório facetado e multivariável - "mostre-me esta métrica dividida por três variáveis categóricas" - é onde a gramática declarativa mostra seu valor, porque adicionar um facet é uma mudança de especificação de uma linha em vez de um loop reestruturado.
A escala é uma restrição real em ambos os modelos: renderizar dezenas de milhares de pontos como marcas individuais de SVG ou rastreadas por JavaScript pode tornar os renderizadores baseados em navegador lentos ou travar, razão pela qual grandes conjuntos de dados geralmente são subamostrados, agrupados (hexbin/histograma 2D) ou rasterizados antes de chegarem a qualquer tipo de biblioteca.
Painéis construídos com Streamlit, Dash ou Gradio não substituem essa escolha - são frameworks de aplicativos que incorporam figuras matplotlib, plotly ou Altair dentro de uma interface web, portanto, a decisão subjacente de gramática versus imperativa ainda precisa ser tomada por gráfico.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Imperativo (matplotlib) | Controle total pixel a pixel, saída de impressão/PDF madura, nenhum tempo de execução necessário para visualizar | Cada mapeamento de legenda/facet/cor é código manual | Figuras de publicação, gráficos estáticos com anotações personalizadas |
| Camada estatística (seaborn) | Codificações hue/col semelhantes a gramática com estimadores estatísticos integrados | Ainda retorna objetos matplotlib - você herda seus "escape hatches" imperativos | Exploração estatística rápida (distribuições, regressões) em dados organizados |
| Gramática declarativa (Altair) | Especificação concisa baseada em codificação, legendas/facets/escalas automáticas | Vega-Lite precisa ser capaz de expressar o que você quer, ou você está preso | Gráficos exploratórios multivariáveis, relatórios baseados em especificações reproduzíveis |
| Interativo/híbrido (plotly) | Interativo por padrão (zoom/hover/pan), funciona tanto como express (declarativo) quanto como graph_objects (imperativo) | Artefatos de saída maiores; pode ser excessivo para um gráfico único e estático | Painéis, gráficos interativos incorporados na web |
Conceitos Errôneos Comuns
- "seaborn é um modelo totalmente diferente do matplotlib." - seaborn é uma camada estatística construída diretamente sobre os objetos figura/eixos imperativos do matplotlib; sua API
hue=/col=imita a codificação da gramática de gráficos, mas o objeto subjacente que você recebe de volta ainda é umAxesdo matplotlib que você pode mutar posteriormente. - "Bibliotecas declarativas não podem ser personalizadas." - Altair e plotly express permitem ambos os estilos de substituição imperativa sobre a especificação declarativa (escalas personalizadas, ajustes manuais de layout); a gramática é o caminho padrão, não o único.
- "Gráficos interativos são sempre a melhor escolha." - A interatividade adiciona uma dependência de tempo de execução JavaScript e um artefato de saída muito maior, o que é ativamente pior para entrega impressa, incorporada por e-mail ou restrita à acessibilidade, onde uma imagem estática é mais simples e portátil.
- "Qualquer formato de DataFrame funciona com qualquer chamada de plotagem." - Bibliotecas de estilo gramática de gráficos esperam dados organizados (longos) com uma linha por observação; dados largos e pivotados geralmente precisam de
.melt()ou remodelação semelhante antes que uma chamada baseada em codificação produza o gráfico pretendido. - "Mais tipos de gráficos exibidos significam mais insights entregues." - Escolher um gráfico é uma decisão de comunicação sobre a pergunta do público, não uma decisão técnica sobre qual biblioteca suporta o maior número de tipos de gráficos; um gráfico de barras que um stakeholder pode ler em cinco segundos geralmente supera um gráfico tecnicamente mais rico que ninguém analisa corretamente.
FAQs
O que "gramática de gráficos" realmente significa?
- É uma teoria que descreve qualquer gráfico como um mapeamento de colunas de dados para codificações visuais: x, y, cor, tamanho, forma.
- Em vez de desenhar formas diretamente, você declara o mapeamento, e um renderizador decide o layout, as escalas e as legendas.
- Em Python, Altair (via Vega-Lite) e plotly express seguem ambos esse modelo; ggplot2 em R é a implementação mais famosa do modelo.
O matplotlib é "pior" porque é imperativo?
Não - o controle imperativo é exatamente o que figuras estáticas de qualidade de publicação, precisamente anotadas, precisam; a troca é que cada elemento (posicionamento da legenda, formatação de ticks, espaçamento) tem que ser codificado explicitamente em vez de inferido de um mapeamento de dados.
Por que o seaborn parece mais uma ferramenta de gramática de gráficos, mesmo sendo construído sobre o matplotlib?
As funções do seaborn aceitam argumentos hue=, col= e row= que descrevem codificações como faria uma gramática declarativa, e ele gera internamente os loops por grupo, a atribuição de cores e a legenda que você escreveria manualmente em matplotlib puro - mas o objeto que ele retorna ainda é um Axes do matplotlib, então você pode voltar para chamadas imperativas a qualquer momento.
Por que meu gráfico Altair ou plotly express parece errado até eu remodelar meus dados?
Ambas as bibliotecas esperam dados organizados, em formato longo - uma linha por observação, uma coluna por variável - porque a etapa de codificação mapeia colunas diretamente em canais visuais; dados largos/pivotados geralmente precisam de .melt() (ou um remodelamento equivalente) antes que uma codificação color=/hue= agrupe corretamente.
Quando devo escolher uma biblioteca interativa em vez de uma estática?
Escolha interativa (plotly, Altair com suas seleções interativas, ou um framework de painel) quando o público precisar explorar os dados por si mesmo - ampliar um intervalo de tempo, passar o mouse para obter valores exatos - e escolha estática (matplotlib, seaborn) quando o gráfico for um entregável fixo como um relatório, artigo ou slide de apresentação onde a interatividade não agrega valor e uma dependência de tempo de execução é um passivo.
Bibliotecas de plotagem declarativas escalam para grandes conjuntos de dados da mesma forma?
Não muito bem por padrão - tanto os estilos de gramática de gráficos quanto os renderizadores interativos rastreiam cada ponto de dados como uma marca individual ou objeto DOM/canvas, então dezenas de milhares de pontos tornam a renderização lenta; grandes volumes de dados são tipicamente subamostrados, agrupados ou rasterizados antes de serem entregues a qualquer tipo de biblioteca.
Qual é a diferença entre plotly express e plotly graph_objects?
- plotly express (
px.scatter,px.line) é o ponto de entrada declarativo, estilo gramática de gráficos - você passa um DataFrame e nomes de colunas para codificações. - graph_objects (
go.Figure,go.Scatter) é a API de nível inferior e imperativa na qual o próprio express é construído. - Você normalmente começa com express e só recorre a graph_objects para personalizações que o express não expõe.
Por que os painéis (Streamlit/Dash/Gradio) não substituem essa escolha de modelo mental?
Essas ferramentas são frameworks de aplicativos para organizar widgets e reatividade em um navegador; os gráficos incorporados nelas ainda são construídos com matplotlib, plotly ou Altair, portanto, a decisão gramática versus imperativa é tomada por gráfico, não substituída pelo framework do painel.
Como decido qual tipo de gráfico usar para uma determinada pergunta?
Combine a codificação com o tipo de pergunta: comparação entre categorias favorece barras, uma tendência ao longo do tempo favorece uma linha, uma relação entre duas variáveis numéricas favorece um gráfico de dispersão, e uma distribuição favorece um histograma ou um gráfico de caixa/violino - a biblioteca e o modelo que você usa são uma decisão separada dessa escolha de tipo de gráfico.
O modelo declarativo do Altair é limitado em comparação com o controle imperativo total?
O Altair (via Vega-Lite) cobre a vasta maioria dos tipos de gráficos estatísticos padrão e composições em camadas e facetadas declarativamente, e suporta camadas e substituições de configuração para controle mais fino, mas layouts verdadeiramente personalizados e específicos de pixel ainda são mais fáceis de alcançar com uma ferramenta imperativa como o matplotlib.
Por que o mesmo conjunto de dados às vezes precisa de duas bibliotecas de plotagem diferentes em um projeto?
O trabalho exploratório se beneficia de uma ferramenta rápida e orientada por codificação (seaborn ou Altair) para iterar rapidamente por muitas combinações de variáveis, enquanto a entrega final do relatório ou painel geralmente precisa do controle estático preciso do matplotlib ou da interatividade do plotly - portanto, um projeto comumente explora em um modelo e entrega em outro.
Qual é o maior erro relacionado ao público na escolha de um gráfico ou biblioteca?
Otimizar para o que parece impressionante para o autor em vez do que um público específico pode ler corretamente e rapidamente - um gráfico plotly 3D interativo raramente é mais útil para um stakeholder do que um gráfico de barras claramente rotulado que responde à sua pergunta real.
Relacionados
- Fundamentos de Visualização - a sintaxe concreta de tipo de gráfico e biblioteca sob a qual o modelo mental desta página se assenta.
- matplotlib - o modelo imperativo de figura/eixos na prática.
- Seaborn - a camada estatística, de estilo de codificação, construída sobre o matplotlib.
- Altair - a biblioteca declarativa de gramática de gráficos na prática.
- Plotly - a biblioteca interativa, híbrida declarativa/imperativa.
- Plotagem de Grandes Dados - o que acontece com ambos os modelos em escala.
Versões da Stack: Esta página foi escrita para Python 3.14 (estável) / 3.13 (manutenção); de outra forma, é conceitual e não está vinculada a uma versão específica da biblioteca de plotagem.