Matheus B. Guerrero | 18/11/2016
A gramática dos gráficos
O basicão
Criado por Winston Chang, Hadley Wickham, entre outros.
O objetivo do ggvis é tornar simples a construção de gráficos iterativos para a análise exploratória de dados. É parecido com o ggplot2, com pequenas diferenças, em que o foco é a construção de gráficos iterativos para web. Incorpora a parte iterativa do shinye as tranformações de dados do dplyr.
Como dito, os gráficos produzidos pelo ggvis são basicamente gráficos web e trabalham de forma muito diferente dos gráficos tradicionais do R. Por exemplo, todo gráfico iterativo do ggvis necessita estar conectado a uma sessão iniciada do R; gráficos estatísticos não precisam de uma sessão iniciada do R para serem visualizados. Logo, o ggvisé ótimo para visualização e iteratividade (na web), mas não é muito útil para publicações (não web), no que se prefere o ggplot2.
Um Exemplo:
mtcars %>% ggvis(~disp, ~mpg) %>% layer_points()
A sintaxe do ggvisé muito intuitiva. Por exemplo, na primeira linha de código abaixo, pode-se alterar o argumento fill a fim de mudar a cor dos pontos. Ou, pode-se trocar a opção layer_points() para alterar o tipo de gráfico. Ou ainda, pode-se plotar os dados mais de uma vez para tornar o gráfico mais informativo.
mtcars %>% ggvis(~wt, ~mpg, fill := "red") %>% layer_points()
mtcars %>% ggvis(~wt, ~mpg) %>% layer_smooths()
mtcars %>% ggvis(~wt, ~mpg) %>% layer_points() %>% layer_smooths()
Gramática
ggvis é baseado na “gramática dos gráficos”. Você pode combinar um conjunto de dados com propiedades e marcadores da seguinte forma:
 <data>  %>%
   ggvis(~<x property>,~<y property>,
         fill = ~<fill property>, ...) %>%
   layer_<marks>()
Observe que todo gráfico ggvis tem 4 componentes essenciais:
- dados (data);
 - um sistema de coordenadas (coordinate system);
 - marcadores (marks), e
 - propriedades correspondentes (properties).
 
De forma mais intuitiva:
Ao alterar cada um desses componentes, pode-se criar uma variedade de gráficos distintos.
Mais alguns exemplos:
pressure %>% ggvis(~temperature, ~pressure) %>% layer_bars()
pressure %>% ggvis(~temperature, ~pressure) %>% layer_lines()
pressure %>% ggvis(~temperature, ~pressure, fill = ~temperature) %>% layer_points()
pressure %>% ggvis(~temperature, ~pressure, size = ~pressure) %>% layer_points()
Sintaxe
Os 3 operadores
O ggvistrabalha com 3 operadores. Entender cada um é fundamental para criar os gráficos.
O Condutor (
%>%): passa o resultado do seu lado esquerdo para o primeiro argumento da função que fica ao seu lado direito.f(x) %>% g(y)é um atalho deg(f(x), y).O Mapeador (
=): mapeia uma propriedade à valores de uma variável, ou conjunto de variáveis. É assim que se visualiza variação em um conjunto de dados. oggvisdimensiona os valores apropriadamente (escala) e adiciona uma legenda que explica como os valores são mapeados para instâncias específicas da propriedade.O Definidor (
:=): Atribui uma cor específica (ou tamanho, largura, etc.) a uma propriedade (não em termos de escala). É assim que se personaliza a aparência dos gráficos. Os números serão tipicamente interpretados como pixels. As especificações de cor são passadas àvega, uma biblioteca de javascript, para que se possa usar qualquer nome de cor reconhecido por HTML/CSS.
Exemplificando: Com o operador %>% pode-se reescrever a linha de código:
layer_points(ggvis(faithful, x = ~waiting, y =  ~eruptions))
Como:
faithful %>% ggvis(~waiting, ~eruptions) %>% layer_points()
Mais exemplos:
pressure %>% ggvis(~temperature, ~pressure, size = ~pressure) %>% layer_points()
pressure %>% ggvis(~temperature, ~pressure, size:= 100) %>% layer_points()
pressure %>% ggvis(~temperature, ~pressure, fill:= "red") %>% layer_points()
Referindo-se a diferentes objetos
Podemos nos referir a 3 tipos distintos de objetos com o ggvis, a saber:
objetos: ao se digitar uma string, o
ggviso tratará como um objeto doRcom esse nome;variáveis: ao se colocar um til,
~, antes de uma string, oggvisa tratará como o nome de uma variável. Oggvisbuscará no banco de dados uma coluna com esse nome; evalores brutos: ao se colocar aspas em torno de uma “string”, o
ggvisa tratará como um valor bruto, i.e., como um pedaço de texto.
Exemplificando: Considere o seguinte objeto e a seguinte variáveil:
red <- "green"
pressure$red <- pressure$temperature
Agora veja os diferentes gráficos produzidos ao se usar:
fill = ~red:
pressure %>% ggvis(~temperature, ~pressure, fill = ~red) %>% layer_points()
fill := "red":
pressure %>% ggvis(~temperature, ~pressure, fill = "red") %>% layer_points()
fill := red:
pressure %>% ggvis(~temperature, ~pressure, fill := red) %>% layer_points()
Points: propriedades
É possível manipular diferentes propriedades ao se usar a função layer_points(), incluindo x, y, fill, fillOpacity, opacity, shape, size, stroke, strokeOpacity e strokeWidth.
A propriedade shape, por exemplo, reconhece diversos valores: circle (default), square, cross, diamond, triangle-up e triangle-down.
Para uma revisão complenta das propriedades do ggvisconsulte o vignette: properties and scales.
Exemplos nunca são d+!!!:
faithful %>% ggvis(~waiting, ~eruptions, size = ~eruptions, opacity := .5, 
fill := "blue", stroke := "black") %>% layer_points()
faithful %>% ggvis(~waiting, ~eruptions, fillOpacity = ~eruptions, size := 100, 
fill := "red", stroke := "red", shape := "cross") %>% layer_points()
Lines: propriedades
De forma análoga à points, lines tem propriedades específicas, se relacionando a: x, y, fill, fillOpacity, opacity, stroke, strokeDash, strokeOpacity e strokeWidth. Alguns são comuns às propriedades de points, alguns não existem, como size; outras são novas, como strokeDash.
Para visualizar:
pressure %>% ggvis(~temperature, ~pressure, stroke := "red",
strokeWidth := 2, strokeDash := 6) %>% layer_lines()
Path e polygons
A função layer_lines() sempre conectará os pontos no gráfico a partir dos pontos mais a esquerda aos pontos mais a direita. O que não é desejável se o interesse é plotar uma forma específica.
Por exemplo, se em um banco de dados, tem-se as coordenadas em ordem sequencial, em termos de longitude e latitude, do estado do Texas, EUA, e o objetivo é plotar o mapa deste estado, então é preciso lançar mão da função layer_paths(). Esta função conecta os pontos na ordem em que aparecem no banco de dados.
Visualizando:
texas <- read.csv("2016-11-18-gg_files/figure-html/texas.csv", header=TRUE, sep=";", dec=",")
texas %>% ggvis(~long, ~lat, fill:= "darkorange") %>% layer_paths()
Mostrando modelos ajustados
A função compute_model_prediction()é uma função útil para se usar com gráficos. Essa função usa um banco de dados como entrada e retorna um novo banco de dados como saída. O novo banco de dados conterá os valores de xe yde uma linha ajustada ao banco de dados original.
O código abaixo calcula a linha que mostra a relação entre as variáveis eruptions e waiting do banco de dados faithful
faithful %>% compute_model_prediction(eruptions ~ waiting, model = "lm")
A função compute_model_prediction() assume uma série de argumentos:
faithful, o banco de dados,um objeto “fórmula” do R,
eruptions ~ waiting, que especifica a relação para o modelo,um
modelodo R a ser ajustado. Por exemplo, “lm” ajusta uma regressão linear, usando o método “loess”.
Já compute_smooth() é uma função que “embrulha” compute_model_prediction() e calcula uma linha de regressão “loess” por padrão.
faithful %>% compute_smooth(eruptions ~ waiting)
compute_smooth() sempre retorna uma banco de dados com duas colunas, uma de nome pred_ e outra de nome resp_. Pode-se passar esses dados para o ggvis plotar uma linha aos dados, confome o exemplo:
faithful %>%
  compute_smooth(eruptions ~ waiting) %>%
  ggvis(~pred_, ~resp_) %>%
  layer_lines()
O mesmo gráfico com a função layer_smooths() do ggvis:
faithful %>% ggvis(~eruptions, ~ waiting) %>% layer_points() %>% layer_smooths(stroke := "red")
Assim como layer_smooths() é um atalho para o uso combinado das funções compute_smooth() e layer_lines(), existem outras funções no ggvis que fazem transformações análogas aos dados a fim de tornar a criação de gráficos uma tarefa mais simples. Por exemplo:
layer_bars()ao invés decompute_count()elayer_rects(),layer_histograms()no lugar decompute_bin()elayer_rects(),layer_densities()como alternativa àcompute_density()elayer_lines().
Veja os exemplos abaixo:
Gráficos de Barras:
mtcars %>% ggvis(~factor(cyl)) %>% layer_bars()
Histogramas:
faithful %>% ggvis(~waiting) %>% layer_histograms(width=5)
Densidades:
faithful %>% ggvis(~waiting, fill:= "green") %>% layer_densities()
ggvis e dplyr
group_by()
É possível combinar o ferramental gráfico do ggvis com as manipulações de base de dados do pacote dplyr. Por exemplo, a função group_by() agrupa variáveis em grupos que possuem uma característica em comum (de acordo com uma “variável de grupo”). Assim, quando o ggvis econtra uma variável agrupada, ele plota um “mark” separado para cada grupo distinto dessa variável.
Abaixo a praticidade de se usar a função group_by() na construção de gráficos.
mtcars %>% group_by(cyl) %>% ggvis(~mpg, ~wt, stroke = ~factor(cyl)) %>% layer_smooths()
mtcars %>% group_by(cyl) %>% ggvis(~mpg, fill = ~factor(cyl)) %>% layer_densities()
interaction()
group_by() pode agrupar dados baseado na interação de duas ou mais variáveis. Basta passar multiplos argumentos para a função:
my_data %>% group_by(<var1>, <var2>)
Assim, group_by() criará um grupo separado para cada combinação distinta das variáveis passadas como argumento. Contudo, para que o ggvismapeie as propriedades de forma adequada para cada combinação, é necessário utilizar a função interaction(). Por exemplo:
stroke = ~interaction(<var1>, <var2>)
mapeará stroke para cada combinação única das variáveis <var1> e <var2>.
Exemplificando:
mtcars %>% group_by(cyl, am) %>% ggvis(~mpg, fill = ~interaction(cyl,am)) %>% layer_densities()
+1 exemplo:
mtcars %>%
  group_by(am) %>%
  ggvis(~mpg, ~hp) %>%
  layer_smooths(stroke = ~factor(am)) %>%
  layer_points(fill = ~factor(am))
Gráficos iterativos
O gráfico abaixo é um gráfico iterativo simples. Ele inclui uma caixa de seleção que altera a forma dos pontos do gráfico.
faithful %>%
   ggvis(~waiting, ~eruptions, fillOpacity := 0.5,
    shape := input_select(label = "Choose shape:",
                               choices = c("circle", "square", "cross",
                                           "diamond", "triangle-up", "triangle-down")),
    fill := input_select(label = "Choose color:", choices = c("black", "red", "blue", "green"))) %>%
   layer_points()
Para habilitar gráficos iterativos em um relatório escrito no RMarkdown, utilize a seguinte opção do preâmbulo: runtime: shiny.
É possível criar os gráficos iterativos atribuindo a uma propriedade de saída uma widget de entrada. O ggvis tem 7 widgets de entrada.
- input_checkbox(),
 - input_checkboxgroup(),
 - input_numeric(),
 - input_radiobuttons(),
 - input_select(),
 - input_slider(), e
 - input_text().
 
Por defeito, cada uma retorna seu valor atual como um número ou como uma string.
Botões para selecionar as cores:
mtcars %>%
   ggvis(~mpg, ~wt,
     fill := input_radiobuttons(label = "Choose color:", 
                                choices = c("black", "red", "blue", "green"))) %>%
   layer_points()
Um exemplo mais elaborado:
mtcars %>% ggvis(x = ~wt) %>%
     layer_densities(
       adjust = input_slider(.1, 2, value = 1, step = .1, label = "Bandwidth adjustment"),
       kernel = input_select(
         c("Gaussian" = "gaussian",
           "Epanechnikov" = "epanechnikov",
           "Rectangular" = "rectangular",
           "Triangular" = "triangular",
           "Biweight" = "biweight",
           "Cosine" = "cosine",
           "Optcosine" = "optcosine"),
       label = "Kernel")
)
Para mais detalhes sobre controle de iteratividade, consulte o vignette: ggvis interactivity.
Gráficos multi-camadas
É possível criar gráficos mult-camadas adicionando camadas adicioais a um gráfico com o uso do operador %>%.
Propriedades definidas dentro da função ggvis() são definidas globalmente, sendo aplicadas a todas as camadas. Já prorpiedades atribuidas e mapeadas dentro de uma função layer_<mark>() são aplicadas localmente: apenas a camada criada por esta função fará uso da propriedade. Onde forem aplicadas, propriedades locais se sobrepõe sobre propriedades globais.
Exemplo:
pressure %>%
  ggvis(~temperature, ~pressure) %>%
  layer_lines(opacity := 0.5) %>%
  layer_points() %>%
  layer_model_predictions(model = "lm", stroke := "navy") %>%
  layer_smooths(stroke := "skyblue")
O mesmo, alterando propriedades e o ajuste:
pressure %>%
  ggvis(~temperature, ~pressure, stroke := "darkred") %>%
  layer_lines(stroke := "orange", strokeDash := 5, strokeWidth := 5) %>%
  layer_points( shape := "circle", size := 100, fill := "lightgreen") %>%
  layer_smooths()
Eixos e legendas
Eixos
A função add_axis() permite que se altere os títulos, o esquema dos “ticks” e a posição dos eixos. A função é bem intuitiva, como pode se observar no código abaixo:
add_axis("x", 
         title = "axis title", 
         values = c(1, 2, 3), 
         subdivide = 5,
         orient = "top")
Exemplo:
faithful %>% 
  ggvis(~waiting, ~eruptions) %>% 
  layer_points() %>%
  add_axis("x", title = "Time since previous eruption (m)",
                values = c(50, 60, 70, 80, 90),
                subdivide = 9,
                orient = "top") %>%
  add_axis("y", title = "Duration of eruption (m)",
                values = c(2, 3, 4, 5),
                subdivide = 9,
                orient = "right")
Legendas
A função add_legend() trabalha de forma similar à função add_axis().
Veja:
faithful %>% 
  ggvis(~waiting, ~eruptions, opacity := 0.6, 
        fill = ~factor(round(eruptions))) %>% 
  layer_points() %>%
  add_legend("fill", title = "~ duration (m)", orient = "left")
Quando add_legend() não é especificado, o ggvis automaticamente cria uma legenda separada para cada propriedade mapeada em uma variável, o que pode resultar em legendas confusas. O código abaixo cria 3 legendas separadas para cada propriedade - fill, shade e size - as sobrepondo!
What a mess!
faithful %>% 
  ggvis(~waiting, ~eruptions, opacity := 0.6, 
        fill = ~factor(round(eruptions)), shape = ~factor(round(eruptions)), 
        size = ~round(eruptions))  %>%
  layer_points()
Para corrigir este problema, especifique um vetor com os nomes das propriedades como primeiro argumento da função add_legend().
Much better!:
faithful %>% 
  ggvis(~waiting, ~eruptions, opacity := 0.6, 
        fill = ~factor(round(eruptions)), shape = ~factor(round(eruptions)), 
        size = ~round(eruptions))  %>%
  layer_points() %>%
  add_legend(c("fill", "shape", "size"), title = "~ duration (m)")
Para saber mais