Vega-Altair: Uma Breve Introdução

vega-lite
altair
Published

1 January 2023

Vega-lite e Altair

Altair é uma biblioteca de gráficos estatísticos interativos para Python, que permite construir visualizações sofisticadas de maneira concisa. Existem outras bibliotecas para criação de gráficos interativos, como Bokeh e Plotly, porém Altair faz uso de uma linguagem declarativa para a construção dos gráficos, em oposição à linguagem imperativa utilizada por outras bibliotecas.

Em uma linguagem declarativa, o usuário fornece o que ele quer que a visualização inclua, em termos de dados, marcas gráficas e canais de codificação, ao invés de ter de especificar – através de código – como a visualização deve ser construída, o que muitas vezes envolve for-loops e outros comandos específicos, tornando o processo de exploração dos dados mais complicado, trabalhoso e pouco intuitivo.

Altair foi desenvolvida baseada em Vega-Lite, que é uma gramática de alto-nível para gráficos interativos. De uma maneira simplificada, os gráficos gerados são exibidos diretamente no navegador (Google Chrome, Safari, etc), então recomenda-se o uso de ambientes como JupyterLab, Kaggle or Colab.

A ideia chave é a de que o usuário deve declarar os links entre os dados que serão utilizados e os repectivos canais de codificação para os quais cada variável será direcionada. Esses termos ficarão mais claros nos exemplos que iremos trabalhar.

Bibliotecas

Vamos importar as bibliotecas necessárias: pandas para a leitura e manipulação dos dados, e altair para a visualização.

import pandas as pd
import altair as alt
A = 'O coração de Minas'
B = 'A cidade dos Profetas'
C = 'A mais charmosa de Minas'

dados = pd.DataFrame({
    'cidade': ['Ouro Preto', 'Ouro Preto', 'Ouro Preto', 'Congonhas', 'Congonhas', 'Congonhas', 'Tiradentes', 'Tiradentes', 'Tiradentes'],
    'descricao': [A, A, A, B, B, B, C, C, C],
    'mes': ['Jan', 'Mai', 'Set', 'Jan', 'Mai', 'Set', 'Jan', 'Mai', 'Set'],
    'temperatura': [24.5, 18.7, 21.3, 27.2, 16.4, 23.8, 32.5, 9.7, 26.3],
})

dados # exibir o conteúdo de dados
cidade descricao mes temperatura
0 Ouro Preto O coração de Minas Jan 24.5
1 Ouro Preto O coração de Minas Mai 18.7
2 Ouro Preto O coração de Minas Set 21.3
3 Congonhas A cidade dos Profetas Jan 27.2
4 Congonhas A cidade dos Profetas Mai 16.4
5 Congonhas A cidade dos Profetas Set 23.8
6 Tiradentes A mais charmosa de Minas Jan 32.5
7 Tiradentes A mais charmosa de Minas Mai 9.7
8 Tiradentes A mais charmosa de Minas Set 26.3

Utilizamos o método DataFrame da biblioteca pandas para criar uma tabela com 9 observações (linhas: 0 – 8) e quatro variáveis (colunas: cidade, descricao, mes e temperatura), e salvamos essa tabela em dados. Os números são fictícios, e não vamos nos preocupar com unidades de medida por enquanto.

O objeto Chart

O objeto fundamental em Altair é o Chart, e ele recebe como argumento os dados que serão utilizados.

grafico = alt.Chart(dados)

Nessa linha de código, nós definimos o objeto Chart e passamos para ele a tabela de dados que foi gerada anteriormente, mas não especificamos nada em relação ao que deve ser feito com esses dados.

Marcas Gráficas

Podemos agora especificar como gostaríamos de criar a visualização. O primeiro passo é definir a marca gráfica (tipo de gráfico) que queremos utilizar para representar os dados. Existem diversas possibilidades, mas vamos começar definindo a marca ponto, e para isso vamos utilizar o método mark_point.

alt.Chart(dados).mark_point()

Perceba que obtivemos exatamente o especificado, as 9 observações contidas em dados foram plotadas uma em cima da outra parecendo que temos apenas um ponto, porém os 9 pontos estão no mesmo lugar. Isso aconteceu porque nenhum canal de codificação foi especificado, ou seja, não especificamos qual variável deve ser codificada no eixo-x, qual deve ser no eixo-y, qual deve codificada pela cor, etc.

Canais de Codificação

Para especificar os diferentes canais de codificação, iremos utilizar o método encode.

alt.Chart(dados).mark_point().encode(
    y = 'cidade' # a variável cidade codificada no eixo-y
)

No gráfico acima, as observações (linhas da tabela) foram distribuídas ao longo do eixo-y, que codificou a variável cidade. Agora os pontos foram distribuídos, mas ainda estão parcialmente sobrepostos pois temos 9 observações, mas estamos exibindo apenas 3 pontos.

Iremos explorar aplicações de diferentes canais de codificação com algunas exemplos, veja uma breve lista com algumas das possibilidades:

  • y: codificação do eixo-y
  • x: codificação do eixo-x
  • color: codificação das cores
  • shape: codificação dos formatos
  • size: codificação dos tamanhos
  • tooltip: codificação para a interatividade

Vamos visualizar melhor as observações, codificando a variável temperatura no eixo-x:

alt.Chart(dados).mark_point().encode(
    y = 'cidade',
    x = 'temperatura'
)

Agora temos uma visualização mais adequada e podemos perceber que, entre as três cidades, Tiradentes exibe a menor e a maior temperatura! Perceba agora a vantagem de Altair como uma linguagem declarativa: vamos criar variações alterando apenas as especificações dos canais de codificação.

alt.Chart(dados).mark_point().encode(
    y = 'mes',
    x = 'temperatura',
    color = 'cidade' # a variável cidade codificada na cor
)
alt.Chart(dados).mark_point().encode(
    y = 'mes',         
    x = 'temperatura', 
    shape = 'cidade' # a variável cidade codificada no formato
)
alt.Chart(dados).mark_point().encode(
    y = 'mes',         
    x = 'temperatura',
    color = 'cidade',
    size = 'temperatura' # a variável temperatura codificada no tamanho
)

Neste último gráfico, a variável temperatura está codificada tanto no eixo-x quanto no tamanho, e as legendas são geradas de maneira automática, porém você pode ajustá-las como preferir. Não me parece a melhor escolha de codificações para essa caso, mas ajuda a ilustrar as possibilidades.

Interatividade

Vamos agora codificar uma lista de duas variáveis no canal tooltip, e criar interatividade através do método interactive.

alt.Chart(dados).mark_point().encode(
    y = 'mes',    
    x = 'temperatura',
    color = 'cidade',
    tooltip = ['cidade', 'descricao'] # lista de duas variáveis codificadas para interatividade
).interactive()

Perceba que as duas variáveis codificadas em tooltip surgem quando o cursor é posicionado sobre cada ponto do gráfico. A interatividade também permite que o usuário aplique zoom ou movimente o interior do gráfico. Vale repetir, a linguagem declarativa permite a sobreposição de diversas funcionalidades com pouquíssimas linhas de código.

O exemplo que criamos pode ser muito simples, e não transmitir de maneira apropriada a capacidade e a elegância dessa biblioteca, mas você certamente irá apreciar a facilidade na criação de visualizações extremamente sofisticadas, incluindo datasets com dezenas de variáveis e milhares de observações.

Dados Agregados

Outra funcionalidade de Altair e sua linguagem declarativa é a de agregar os dados dentro da especificação da visualização, ou seja, elimina a necessidade de se utilizar pandas e agregar-los antes de cada visualização. Isso proporciona mais velocidade e mais espaço para explorar e experimentar, o que é extremamente importante.

No exemplo abaixo, nós iremos agregar os dados calculando a média da temperatura dentro do canal de codificação, isto é, iremos codificar a média das temperaturas no eixo-x:

alt.Chart(dados).mark_point().encode(
    x='mean(temperatura)',
    y='cidade'
)

Ouro Preto apresenta a menor temperarura média. Podemos explorar agora outros tipos de gráfico para essa aplicação, o que equivale dizer outras marcas gráficas.

Alterando o Tipo de Marca

Vamos alterar o tipo de marca que utilizamos até agora para visualizar os dados para barras retangulares, para isso iremos substituir mark_point por mark_bar:

alt.Chart(dados).mark_bar().encode(
    x='mean(temperatura)',
    y='cidade'
)

Digamos que você queira visualizar as barras na posição vertical. Perceba que nós não especificamos que as barras deveriam ser horizontais, como seria o caso para uma linguagem imperativa. Para alterar a orientação das barras, nós devemos apenas alterar as especificações dos canais de codificação, o que iremos fazer em seguida – e iremos também incluir a descrição da cidade quando o cursor estiver sobre as baras.

alt.Chart(dados).mark_bar().encode(
    y='mean(temperatura)',
    x='cidade',           # cidade está agora codificada no eixo-x
    tooltip = 'descricao' # descrição codificada em tooltip
)

Vega-lite foi desenvolvida pelo Laboratório de Visualizações Interativas da Universidade de Washington. As propriedades dos gráficos, como dimensões, escalas, legendas, esquema de cores e etc são baseadas em décadas de pesquisa e são implementadas seguindo as melhores e mais atualizadas práticas.

Seguem os links para alguns videos que ilustram as motivações para a criação de Vega-lite por Jeffrey Heer e equipe, bem como as motivações para criação de Altair, por Jake VanderPlas e Brian Granger.

Kaggle