#!/usr/bin/env python # coding: utf-8 # # Múltiplas Visões #
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.
# In[1]: import pandas as pd import altair as alt # In[2]: 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. # ## Camadas #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. # In[3]: 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. # In[4]: 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. # In[5]: 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. # In[6]: 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. # In[7]: 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. # ## Facetas #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. # In[8]: 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. # In[9]: 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. # In[10]: 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._ # ## Concatenar # 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. # In[11]: 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. # In[12]: 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. # In[13]: (vento | temp) & press.properties(width=540) # ## Repetição # 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. # In[14]: 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. # In[15]: colunas = ["Precipitação", "Temperatura mínima", "Umidade Relativa do Ar"] linhas = colunas[::-1] # In[16]: 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. # In[17]: 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.
# ## Exercícios # # 1. Crie um _small multiples_ com a temperatura máxima pela hora, com a faceta sendo as cidades. # # 1. Crie um _small multiples_ com precipitação de cada cidade, facetando o mês. # # 1. Crie um gráfico de linhas que também tenha pontos. # # 1. Crie um gráfico de barras horizontais com o texto ao lado das barras. # # 1. 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. # # 1. Crie um scatterplot e em cima e do lado direito ficam os histogramas alinhados das dimensões selecionadas. # # 1. Crie um dashboard com 4 tipos de gráficos diferentes. # # 1. 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. # # 1. Use o scatterplot do exercício anterior e amplie para usar em cada cidade. # # 1. 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. # # 1. Crie um gráfico lollipop ([exemplo](https://datavizproject.com/wp-content/uploads/2017/09/DVP_101_200-64.png)) . # # 1. Crie um gráfico de horizonte (_horizon chart_ - [exemplo](https://camo.githubusercontent.com/c0437ff0743c97b3d933be35fecd4737284f5a8c/68747470733a2f2f76617374757269616e6f2e6769746875622e696f2f686f72697a6f6e2d74696d657365726965732d63686172742f6578616d706c652f62617369632f73637265656e73686f742e706e67)). # # 1. Crie um gráfico de pirâmide ([exemplo](https://upload.wikimedia.org/wikipedia/commons/a/a7/Population_pyramid_of_South_Korea_2015.png)) com os histogramas de um atributo comparando duas cidades.