Introdução ao pacote `ggvis`

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 de g(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. o ggvis dimensiona 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 ggvis o tratará como um objeto do R com esse nome;

  • variáveis: ao se colocar um til, ~, antes de uma string, o ggvisa tratará como o nome de uma variável. O ggvisbuscará no banco de dados uma coluna com esse nome; e

  • valores brutos: ao se colocar aspas em torno de uma “string”, o ggvis a 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 modelo do R a ser ajustado. Por exemplo, “lm” ajusta uma regressão linear, usando o método “loess”.

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 de compute_count() e layer_rects(),

  • layer_histograms() no lugar de compute_bin() e layer_rects(),

  • layer_densities() como alternativa à compute_density() e layer_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

  1. CRAN

  2. ggivs: website

  3. Curso DataCamp
comments powered by Disqus