Quando procuramos informação em um conjunto de dados com muitos campos, nos sentimos tentados a carregar a visualização com o máximo de variáveis visuais possíveis: x, y, color, size, shape, e mais. Entretanto, mapear muita informação ao mesmo tempo torna o gráfico difícil de entender. Utilizando Altair, temos duas opções para evitar isso: utilizar gráficos com múltiplas visões e interação.
O conteúdo desse notebook tem as técnicas que diferenciam o Vega-Lite e Altair de programas como Excel e Tableau, com o controle desses aspectos multivisão sobre visualizações.
import pandas as pd
import altair as alt
url = "https://raw.githubusercontent.com/tiagodavi70/vl-altair-tutorial/master/datasets/completo.csv"
df = pd.read_csv("https://raw.githubusercontent.com/tiagodavi70/vl-altair-tutorial/master/datasets/dados.csv")
Altair apresenta algumas funções para combinar gráficos e apresentar os dados em visões múltiplas e coordenadas.
Uma única visão complexa pode ser cognitivamente incompreensível para os usuários. Visões múltiplas podem ajudar na estratégia de "dividir e conquistar", reduzindo o volume de dados que são consultados de cada vez. Essas visões evem ser usadas com cuidado para evitar mais complexidade, já que cada visão é um contexto novo. Mesmo assim, quando usada de maneira correta pode ter bons resultados, já que a visão sempre é melhor que a memória para comparações, e visões múltiplas podem potencializar a percepção entre a relação dos dados.
Agora vamos ver algumas funções para manipular visões em Altair.
Um jeito comum de combinar gráficos é sobrepor as variáveis visuais uma em cima da outra. Se os domínios forem iguais os eixos podem ser compartilhados, senão dá separar os eixos.
Vamos comparar os valores de Temperatura de ponto de orvalho entre duas cidades.
alt.Chart(url, title="Comparação de ponto de orvalho de Capitão Poço e Altamira").mark_area(opacity=.45).transform_filter(
"(datum.Cidade == 'Capitão Poço' || datum.Cidade == 'Altamira')"
).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(Temperatura orvalho máxima):Q",scale=alt.Scale(zero=False),title="Média dos pontos de orvalho °C"),
alt.Y2("average(Temperatura orvalho mínima):Q"),
alt.Color("Cidade:N")
)
Temos a distribuição pela área e agora vamos fazer um gráfico com o ponto médio.
alt.Chart(url, title="Comparação de temperatura de Capitão Poço e Altamira").mark_line(opacity=.85).transform_filter(
"(datum.Cidade == 'Capitão Poço' || datum.Cidade == 'Altamira')"
).transform_calculate(
temp_mid="(+datum['Temperatura orvalho máxima'] + +datum['Temperatura orvalho mínima']) / 2"
).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(temp_mid):Q",scale=alt.Scale(zero=False), title="Ponto médio °C"),
alt.Color("Cidade:N")
)
Agora que temos os dois gráficos separados, podemos sobrepor as áreas e linhas no mesmo gráfico para comparar mínimo, máximo e ponto médio. Para isso podemos usar o operador +
salvando os dois gráficos.
orvalhoMinMax = alt.Chart(url, title="Comparação de ponto de orvalho de Capitão Poço e Altamira").mark_area(opacity=.45).transform_filter(
"(datum.Cidade == 'Capitão Poço' || datum.Cidade == 'Altamira')"
).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(Temperatura orvalho máxima):Q",scale=alt.Scale(zero=False),title="Média dos pontos de orvalho °C"),
alt.Y2("average(Temperatura orvalho mínima):Q"),
alt.Color("Cidade:N")
)
orvalhoMid = alt.Chart(url).mark_line(opacity=.85).transform_filter(
"(datum.Cidade == 'Capitão Poço' || datum.Cidade == 'Altamira')"
).transform_calculate(
temp_mid="(+datum['Temperatura orvalho máxima'] + +datum['Temperatura orvalho mínima']) / 2"
).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(temp_mid):Q",scale=alt.Scale(zero=False)),
alt.Color("Cidade:N")
)
orvalhoMinMax + orvalhoMid
O operador +
é um atalho para a função alt.layer
. Quando tem somente um título de eixo ele é o único que é apresentado no gráfico.
Os pontos de orvalho entre 9:00 e 18:00 tem uma área grande, e com o ponto médio dá pra acompanhar a tendência.
Vamos comparar agora os valores de temperatura de orvalho com a umidade do ar.
orvalhoMinMax = alt.Chart(url, title="Comparação de ponto de orvalho e Umidade").mark_area(opacity=.45).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(Temperatura orvalho máxima):Q",scale=alt.Scale(zero=False),title="Média do pontos de orvalho °C"),
alt.Y2("average(Temperatura orvalho mínima):Q")
)
umidade = alt.Chart(url).mark_line(opacity=.85).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(Umidade Relativa do Ar):Q", scale=alt.Scale(zero=False))
)
orvalhoMinMax + umidade
Compartilhar o mesmo eixo não deu nada certo, então vamos separar os eixos.
orvalhoMinMax = alt.Chart(url, title="Comparação do Ponto de Orvalho").mark_area(opacity=.45).encode(
alt.X("hours(Data):T",title=None),
alt.Y("average(Temperatura orvalho máxima):Q",scale=alt.Scale(zero=False),title="Média do Ponto de orvalho °C"),
alt.Y2("average(Temperatura orvalho mínima):Q")
)
umidade = alt.Chart(url).mark_line(
interpolate='monotone',
opacity=.85,
color="#333333"
).transform_calculate(
perc_umid="+datum['Umidade Relativa do Ar'] / 100" # criar nova coluna para as etiquetas
).encode(
alt.X("hours(Data):T"),
alt.Y("average(perc_umid):Q",
scale=alt.Scale(zero=False),
axis=alt.Axis(format="%"),
title="Umidade Relativa do Ar %")
)
alt.layer(orvalhoMinMax, umidade).resolve_scale(y='independent')
Quando a umidade é alta, o ponto de orvalho varia pouco e quando a umidade é baixa tem muita variação de ponto de carvalho.
Esse tipo de eixo deve ser usado com cuidado, já que é fácil confundir uma escala com outra.
Nós já vimos alguns aspectos de uma faceta, usando os parâmetros row e column. Uma faceta é uma subdivisão de um conjunto de dados em grupos e cria um gráfico novo para cada um deles. Vamos mostrar também um operador `facet` mais genérico.
Vamos começar com um histograma da temperatura do ar.
alt.Chart(df).mark_bar().encode(
alt.X("Temperatura do ar - bulbo seco:Q",bin=alt.Bin(maxbins=20)),
alt.Y("count()")
)
E agora vamos separar por estação, criando um novo campo marcando o verão e o inverno.
alt.Chart(url).mark_bar(
).transform_filter(
"year(datum['Data']) == 2019"
).transform_calculate(
estação="month(datum.Data) < 5 || month(datum.Data) == 11 ? 'Chove muito':'Chove pouco'"
).encode(
alt.X("Temperatura do ar - bulbo seco:Q",bin=alt.Bin(maxbins=12),title=None),
alt.Y("count()", title="Contagem de Registros"),
alt.Column("month(Data):T",title=None),
alt.Color("estação:N",
scale=alt.Scale(range=["DeepSkyBlue","Brown"]),
title="Estação")
).properties(width=40, height=100)
Conseguimos ver os meses de março e setembro se destacando no inverno e verão com temperaturas baixas e altas, respectivamente.
Utilizando o nosso exemplo do orvalho anterior, podemos usar o operador facet
para mostrar as cidades lado a lado.
orvalhoMinMax = alt.Chart(title="Comparação de ponto de orvalho de Altamira").mark_area(opacity=.45).transform_filter(
"(datum.Cidade == 'Capitão Poço' || datum.Cidade == 'Altamira')"
).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(Temperatura orvalho máxima):Q",scale=alt.Scale(zero=False),title="Média dos pontos de orvalho °C"),
alt.Y2("average(Temperatura orvalho mínima):Q"),
alt.Color("Cidade:N")
)
orvalhoMid = alt.Chart(title="Comparação de ponto de orvalho de Capitão Poço").mark_line(opacity=.85).transform_filter(
"(datum.Cidade == 'Capitão Poço' || datum.Cidade == 'Altamira')"
).transform_calculate(
temp_mid="(+datum['Temperatura orvalho máxima'] + +datum['Temperatura orvalho mínima']) / 2"
).encode(
alt.X("hours(Data):T",title="Hora no dia"),
alt.Y("average(temp_mid):Q",scale=alt.Scale(zero=False)),
alt.Color("Cidade:N")
)
alt.layer(orvalhoMinMax, orvalhoMid).facet(
data=url,
column='Cidade:N'
).resolve_axis(y='independent')
Alteramos também a função layer
, tirando os dados de ambos os gráficos, e definimos somente na fusão, assim como o parâmetro column
. Também mudamos o eixo para que cada gráfico tenho o seu.
Troque resolve_axis(y='independent')
por resolve_scale(y='independent')
e veja a direfença. Talvez não seja uma boa ideia trocar quando as escalas são tão próximas, mas em outros casos talvez ajude a análise.
As funções que vimos até agora são visões múltiplas muito parecidas com small multiples, baseados no mesmo conjunto de dados. Usando concatenação podemos misturar gráficos com dados diferentes.
O operador hconcat
(atalho |
) concatena horizontalmente e o operador vconcat
(atalho &
) concatena verticalmente.
Vamos começar comparando rajada de vento de duas cidades durante o ano.
alt.Chart(df).mark_line(opacity=.85).transform_filter(
"(datum.Cidade == 'Castanhal' || datum.Cidade == 'Altamira')"
).encode(
alt.X("month(Data):T"),
alt.Y("average(Rajada Máxima de Vento):Q"),
alt.Color("Cidade:N")
)
Além da rajada de vento, vamos ver também temperatura e a pressão atmosférica. Vamos criar um gráfico base e mudar somente a variável visual para cada gráfico, depois concatenar.
base = alt.Chart(df).mark_line(opacity=.85).transform_filter(
"(datum.Cidade == 'Castanhal' || datum.Cidade == 'Altamira')"
).encode(
alt.X("month(Data):T"), # sem encode do Y
alt.Color("Cidade:N")
).properties(width=240, height=180)
vento = base.encode(alt.Y("average(Rajada Máxima de Vento):Q", scale=alt.Scale(zero=False)))
temp = base.encode(alt.Y("average(Temperatura do ar - bulbo seco):Q", scale=alt.Scale(zero=False)))
press = base.encode(alt.Y("average(Pressão Atmosférica ao nível da estação):Q", scale=alt.Scale(zero=False)))
vento | temp | press
Podemos até combinar os operadores.
(vento | temp) & press.properties(width=540)
No caso de pequenas mudanças nos gráficos, podemos usar a repetição. A repetição permite usar uma especificação template e preenche com os campos que escolhermos.
alt.Chart(df).mark_line().transform_filter(
"(datum.Cidade == 'Castanhal' || datum.Cidade == 'Altamira')"
).encode(
alt.X("month(Data):T"),
alt.Y(alt.repeat("column"), aggregate='average', type='quantitative', scale=alt.Scale(zero=False)),
alt.Color("Cidade:N")
).properties(
width=240,
height=180
).repeat(
column=["Rajada Máxima de Vento", "Velocidade Horária do Vento", "Temperatura do ponto de orvalho"]
)
Com essa estrutura podemos criar uma matriz de scatterplot. Vamos usar alguns atributos do conjunto de dados.
colunas = ["Precipitação", "Temperatura mínima", "Umidade Relativa do Ar"]
linhas = colunas[::-1]
splom = alt.Chart(df).mark_circle().encode(
alt.X(alt.repeat("column"), type='quantitative', scale=alt.Scale(zero=False)),
alt.Y(alt.repeat("row"), type='quantitative', scale=alt.Scale(zero=False)),
tooltip=[alt.Tooltip("day(Data)"), alt.Tooltip("Cidade")]
).properties(
width=120,
height=120
).repeat(
column=colunas,
row=linhas
)
splom
Por fim, podemos agregar tudo que fizemos até aqui em um dashboard.
barras = alt.Chart().mark_bar().encode(
alt.X(alt.repeat("row"), type='quantitative', bin=True, title="Histograma com média"),
alt.Y("count()",title=None)
)
regua = alt.Chart().mark_rule(color="firebrick").encode(
alt.X(alt.repeat("row"), aggregate='average', type='quantitative')
)
hist = alt.layer(barras, regua, data=df).properties(
width=120,
height=120
).repeat(
row=linhas
)
tempo = alt.Chart(df).mark_line().encode(
alt.X("month(Data):T"),
alt.Y(alt.repeat("column"), aggregate='average', type='quantitative',scale=alt.Scale(zero=False))
).properties(
width=130,
height=120
).repeat(
column=colunas
)
(splom | hist) & tempo
Agora temos meios de ver várias dimensões dos dados sem carregar as váriaveis visuais, recuperando e mostrando os valores para uma análise visual. Ainda sim, falta um aspecto interativo nos nossos gráficos, e veremos no próximo notebook.
Crie um small multiples com a temperatura máxima pela hora, com a faceta sendo as cidades.
Crie um small multiples com precipitação de cada cidade, facetando o mês.
Crie um gráfico de linhas que também tenha pontos.
Crie um gráfico de barras horizontais com o texto ao lado das barras.
Crie dois heatmaps lado a lado, um para dia da semana e outro para o mês. Devem mapear a média de um atributo númerico por cidade e o mês.
Crie um scatterplot e em cima e do lado direito ficam os histogramas alinhados das dimensões selecionadas.
Crie um dashboard com 4 tipos de gráficos diferentes.
Crie um scatterplot de duas camadas, com os pontos na frente e um histograma em forma de heatmap atrás. Ajuste os atributos, as cores e a opacidade de acordo.
Use o scatterplot do exercício anterior e amplie para usar em cada cidade.
Escolha um atributo númerico e apresenta os valores da média pelo mês em um gráfico de linha. Sobreponha esse gráfico com os pontos de cada medição.
Crie um gráfico lollipop (exemplo) .
Crie um gráfico de horizonte (horizon chart - exemplo).
Crie um gráfico de pirâmide (exemplo) com os histogramas de um atributo comparando duas cidades.