Manipulação e Visualização de Dados

leg.ufpr.br/~walmes/cursoR/data-vis

1 Os recusos para visualização

O landscape de recursos para visualização de dados no R pode ser representado por uma divisão em 3 territórios.

Para mais detalhes sobre os recursos gráficos, siga esse link: https://www.stat.ubc.ca/~jenny/STAT545A/block90_baseLatticeGgplot2.html.

Para uma descrição completada comparação entre lattice e ggplot2, siga esse link: https://learnr.wordpress.com/2009/08/26/ggplot2-version-of-figures-in-lattice-multivariate-data-visualization-with-r-final-part/

Confira no R Graph Gallery a variedade de gráficos confeccionados com o R.

Além dos recursos disponíveis nos territórios supradescritos, pode-se considerar ainda a existência de mais dois conjuntos de recursos: para mapas e gráficos interativos.

A visualização de dados geográficos conta com um vários pacotes específicos. Siga os links abaixo para ter uma ideia das funcionalidades:

Os recursos para visualização interativa de dados estão distribuídos em vários pacotes. Talvez o mais interessante deles seja o plotly que permite a criação de gráficos interativos a partir da ggplot2 além de possuir funções próprias. Veja em plotly-R a galeria de gráficos.

A visualização interativa é voltada para exibicação na WEB. Alguns dos pacotes para isso são estes:

2 Leitura dos dados

Para explorar os recursos básicos de visualização, serão usados dados reais sobre a venda do modelo Renault Duster extraídos da WEB. Os dados estão disponíveis online em http://www.leg.ufpr.br/~walmes/data/duster_venda_260314.txt.

Os dados contém informações sobre o preço de venda (R$, valor) e distância percorrida (km, km) além de outras características descritoras do veículo, como ano de fabricação, cor, tipo de câmbio.

#-----------------------------------------------------------------------
# Dados de carros Duster à venda no webmotors em 26/03/2014.

# Importa a tabela de dados da web.
url <- "http://www.leg.ufpr.br/~walmes/data/duster_venda_260314.txt"
dus <- read.table(file = url,
                  header = TRUE,
                  sep = "\t",
                  encoding = "utf-8")
str(dus)
## 'data.frame':    699 obs. of  10 variables:
##  $ modelo: chr  "RENAULT DUSTER 1.6 EXPRESSION 4X2 16V FLEX 4P MANUAL" "RENAULT DUSTER 1.6 4X2 16V FLEX 4P MANUAL" "RENAULT DUSTER 1.6 DYNAMIQUE 4X2 16V FLEX 4P MANUAL" "RENAULT DUSTER 1.6 DYNAMIQUE 4X2 16V FLEX 4P MANUAL" ...
##  $ cor   : chr  "Prata" "Azul" "Prata" "Prata" ...
##  $ km    : int  31442 40800 56000 NA 45000 50000 44000 30000 41000 55000 ...
##  $ ano   : chr  "2011/2012" "2011/2012" "2011/2012" "2011/2012" ...
##  $ valor : num  41990 42500 42900 42990 43800 ...
##  $ cambio: chr  "MANUAL" "MANUAL" "MANUAL" "MANUAL" ...
##  $ poten : num  1.6 1.6 1.6 1.6 1.6 1.6 1.6 1.6 2 1.6 ...
##  $ trac  : chr  "4X2" "4X2" "4X2" "4X2" ...
##  $ cat   : chr  " EXPRESSION " " " " DYNAMIQUE " " DYNAMIQUE " ...
##  $ novo  : chr  "usado" "usado" "usado" "usado" ...
# Cria ano do veículo com extração regex.
dus$ano <- factor(gsub(x = as.character(dus$ano),
                       pattern = "/\\d{4}$",
                       replacement = ""))

# Quantidade de NA em cada coluna.
apply(dus, MARGIN = 2, FUN = function(x) sum(is.na(x)))
## modelo    cor     km    ano  valor cambio  poten   trac    cat   novo 
##      0      0    132      0      0      0      0      0      0      0

A variável km apresentou mais de uma centena de valores ausentes. A tabela tem 3 variáveis numéricas (km, valor, poten), sendo as demais todas categóricas.

3 Gráficos para uma variável categórica

A análise exploratória de variáveis categóricas é praticamente baseada na distribuição das frequências. Por ser uma variável não numérica, não é possível calcular medidas descritivas numéricas de posição (como média, mediada) ou de dispersão (variância, amplitude). Dessa forma, esta-se confinado à visualizações que consideram as frequências.

#-----------------------------------------------------------------------
# Gráfico de barras e setores.

# Tabela de frequência.
x <- table(dus$cambio)
class(x)
## [1] "table"
# Se vem da xtabs() também tem classe `table`.
x <- xtabs(~cambio, data = dus)
class(x)
## [1] "xtabs" "table"
# Gráfico padrão.
# barplot(x)

# Com anotações nas margens e cores.
barplot(x,
        xlab = "Tipo de câmbio",
        ylab = "Frequência absoluta",
        col = c("seagreen", "yellowgreen"))

# Horizontal.
barplot(x,
        horiz = TRUE,
        xlab = "Tipo de câmbio",
        ylab = "Frequência absoluta",
        col = c("seagreen", "#FF9911"))
box(bty = "L")

# Cores com `green` no vetor de cores `colors()`.
grep(pattern = "green", x = colors(), value = TRUE)
##  [1] "darkgreen"         "darkolivegreen"    "darkolivegreen1"   "darkolivegreen2"  
##  [5] "darkolivegreen3"   "darkolivegreen4"   "darkseagreen"      "darkseagreen1"    
##  [9] "darkseagreen2"     "darkseagreen3"     "darkseagreen4"     "forestgreen"      
## [13] "green"             "green1"            "green2"            "green3"           
## [17] "green4"            "greenyellow"       "lawngreen"         "lightgreen"       
## [21] "lightseagreen"     "limegreen"         "mediumseagreen"    "mediumspringgreen"
## [25] "palegreen"         "palegreen1"        "palegreen2"        "palegreen3"       
## [29] "palegreen4"        "seagreen"          "seagreen1"         "seagreen2"        
## [33] "seagreen3"         "seagreen4"         "springgreen"       "springgreen1"     
## [37] "springgreen2"      "springgreen3"      "springgreen4"      "yellowgreen"
# Gráfico de setores. Evite usar referências radiais pois comparações de
# arcos são prejudicadas por não estarem alinhadas.
pie(x,
    col = c("#5398ed",
            rgb(12, 58, 114, max = 255)),
    main = "Tipo de câmbio")

# Para as cores do carro.
x <- xtabs(~cor, data = dus)
pie(x)

Tanto os gráficos de barras (vertical ou horizontal) quanto o de setores (pizza), representam a distribuição de frequência. No entanto, exceto por razões bem específicas, o gráfico de setores deve ser evitado. A percepção sobre do comprimento dos setores é comprometida pela ausência de um sistema de referência linear. Ou seja, o gráfico está em um sistema de referência polar que dificulta a percepção de comprimentos em arco.

# Vetor que cores para usar com cada cor de veiculo.
cols <- c("blue", "white", "gray50", "Yellow", "gray90", "black",
          "green4", "red", "red4")

cbind(levels(dus$cor), cols)
##       cols    
##  [1,] "blue"  
##  [2,] "white" 
##  [3,] "gray50"
##  [4,] "Yellow"
##  [5,] "gray90"
##  [6,] "black" 
##  [7,] "green4"
##  [8,] "red"   
##  [9,] "red4"
# Reordenar os níveis do fator e as cores.
ord <- order(x, decreasing = TRUE)
cols <- cols[ord]
x <- x[ord]
n <- names(x)
dus$cor <- factor(dus$cor, levels = n)

cbind(levels(dus$cor), cols)
##                    cols    
##  [1,] "Prata"      "gray90"
##  [2,] "Branco"     "white" 
##  [3,] "Preto"      "black" 
##  [4,] "Verde"      "green4"
##  [5,] "Cinza"      "gray50"
##  [6,] "Vermelho"   "red"   
##  [7,] "Azul"       "blue"  
##  [8,] "Indefinida" "Yellow"
##  [9,] "Vinho"      "red4"
a <- barplot(x,
             xaxt = "n",
             las = 1,
             col = cols)
mtext(side = 2, text = "Frequência absoluta", line = 3)
axis(side = 1, at = a, labels = n, las = 2)
box(bty = "L")

par(mar = c(4.1, 7.1, 2.1, 2.1))
barplot(x,
        horiz = TRUE,
        las = 1,
        col = cols)
mtext(side = 2, text = "Cor", line = 5)
mtext(side = 1, text = "Frequência absoluta", line = 2)
box(bty = "L")

# Fecha a janela gráfica para restaturar as configurações.
# dev.off()

Quando o número de categorias cresce, a leitura do gráfico de barras é naturalmente mais demorada. Porém, se não existe uma ordenação natural nos níveis da variável categória (variável categórica nominal e não ordinal), a ordenação das barras com relação ao valor aprimora a visualização.

O uso das barras horizontais é recomendado quando os texto sob o eixo é comprido. O texto na vertical não favorece a rápida leitura, por isso colocá-lo na horizontal é uma boa opção.

4 Gráfico para duas variáveis categóricas

#-----------------------------------------------------------------------
# Gráficos de barras emplilhadas (stacked) e lado a lado.

dus$cambio <- factor(dus$cambio)

x <- xtabs(~cambio + ano, data = dus)
x
##             ano
## cambio       2011 2012 2013 2014
##   AUTOMÁTICO   51   40  100    4
##   MANUAL       88  204  180   32
cols <- c("#660d32", "#bc1a5e")

# Barras empilhadas.
barplot(x,
        beside = FALSE,
        xlab = "Ano",
        ylab = "Frequência absoluta",
        col = cols)
legend("topleft",
       legend = levels(dus$cambio),
       fill = cols,
       bty = "n")
box(bty = "L")

# Barras lado a lado.
barplot(x,
        beside = TRUE,
        xlab = "Ano",
        ylab = "Frequência absoluta",
        col = cols)
legend("topleft",
       legend = levels(dus$cambio),
       fill = cols,
       bty = "n")
box(bty = "L")

Quando duas ou variáveis categóricas são consideradas, pode-se representar a distribuição de frequência com barras empilhadas ou barras lado a lado. O enfoque desses gráficos é diferente.

Barras empilhadas

  1. Principal: ver os totais absolutos marginais de cada ano.
  2. Secundário: ver as proporções de cada câmbio em cada ano.
  3. Ruim: compreender a série de frequências absolutas de cada câmbio ao longo dos anos.

Barras lado a lado

  1. Principal: comparar os valores absolutos dos câmbios em cada ano.
  2. Secundário: entender a série de cada câmbio ao longo dos anos.
  3. Ruim: compreender a série do total absoluto ao longo dos anos.
  4. Ruim: determinar a proporção de cada câmbio em cada ano.
x
##             ano
## cambio       2011 2012 2013 2014
##   AUTOMÁTICO   51   40  100    4
##   MANUAL       88  204  180   32
u <- x %*% diag(1/colSums(x))
colnames(u) <- colnames(x)
u
##             
## cambio            2011      2012      2013      2014
##   AUTOMÁTICO 0.3669065 0.1639344 0.3571429 0.1111111
##   MANUAL     0.6330935 0.8360656 0.6428571 0.8888889
# Barras empilhadas relativas.
barplot(u,
        beside = FALSE,
        xlab = "Ano",
        ylab = "Frequência relativa",
        col = cols)
legend("topleft",
       inset = c(0.025, -0.12),
       xpd = TRUE,
       ncol = 2,
       legend = levels(dus$cambio),
       fill = cols,
       bty = "n")
box(bty = "L")

mosaicplot(t(x),
           off = c(2, 1),
           col = cols,
           ylab = "Tipo de câmbio",
           xlab = "Ano",
           main = NULL)
legend("topleft",
       inset = c(0.025, -0.12),
       xpd = TRUE,
       ncol = 2,
       legend = levels(dus$cambio),
       fill = cols,
       bty = "n")

Barras empilhadas padronizadas

As barras empilhadas podem ter comprimento padronizado para representar a frequência relativa. Dessa forma é mais facil comprar as frquências relativas. Nesse gráfico as ênfases são:

  1. Principal: comprar as proporções dos câmbios em cada ano independente dos totais absolutos.
  2. Ruim: determinar os totais relativos de cada ano.
  3. Ruim: determinar os totais absolutos dos anos e câmbios.

Gráfico de barras como mosaico

Mais um tipo de gráfico interessante é o mosaico. Ele é uma exibição das frequências relativas marginais e condicionais. Tem por objetivo:

  1. Principal: ver os totais dos anos (largura).
  2. Secundário: ver as proporções de cada câmbio em cada ano (alturas relativas).
  3. Ruim: determinar os valores absolutos.
#-----------------------------------------------------------------------
# Anotações nas barras.

x <- xtabs(~cambio + poten, data = dus)
x
##             poten
## cambio       1.6   2
##   AUTOMÁTICO   0 195
##   MANUAL     379 125
# Cores de preenchimento para as barras.
cols <- c("#04510a", "#229b2b")

# Barras lado a lado.
bp <- barplot(t(x),
              beside = TRUE,
              col = cols,
              xlab = "Tipo de câmbio",
              ylab = "Frequência absoluta")

bp
##      [,1] [,2]
## [1,]  1.5  4.5
## [2,]  2.5  5.5
# Calcula a altura de uma palavra em termos da escala y do gráfico.
sh <- strheight("um texto qualquer")
sh
## [1] 8.886638
# Opera com os limites do gráfico armazenados em `par()$usr`.
lim <- par()$usr[4] + 3 * sh

# Refaz o gráfico com espaço para o texto.
barplot(t(x),
        beside = TRUE,
        col = cols,
        ylim = c(0, lim),
        xlab = "Tipo de câmbio",
        ylab = "Frequência absoluta")
legend("topleft",
       title = "Potência",
       legend = c("1.6","2.0"),
       fill = cols,
       bty = "n")
text(x = c(bp),
     y = t(x),
     labels = t(x),
     pos = 3)
box()

Se mais variáveis forem envolvidas, o gráfico de mosaico fará a representação com mais divisões nos retângulos.

# Mais dimensões.
mosaicplot(HairEyeColor,
           off = 2,
           col = c("pink", "cyan"))

# Obtendo totais para fazer gráficos de barras.
dimnames(HairEyeColor)
## $Hair
## [1] "Black" "Brown" "Red"   "Blond"
## 
## $Eye
## [1] "Brown" "Blue"  "Hazel" "Green"
## 
## $Sex
## [1] "Male"   "Female"
a <- apply(HairEyeColor,
           MARGIN = c(1, 2),
           FUN = sum)

# Vetor que associa cores conforme os níveis dos fatores às cores usadas
# para preenchimento.
cols <- c(Brown = "#6b2205",
          Blue = "#4fb2ff",
          Green = "#1a9b1e",
          Blond = "#d8d652",
          Red = "#bc1405",
          Black = "#210a08",
          Hazel = "#a86526")

# Mosaico.
mosaicplot(a,
           col = cols[colnames(a)])

mosaicplot(t(a),
           col = cols[rownames(a)])

# Barras empilhadas.
barplot(a,
        xlab = "Eye",
        col = cols[rownames(a)])
legend("topright",
       title = "Hair",
       legend = rownames(a),
       fill = cols[rownames(a)],
       bty = "n")

# Barras lado a lado.
barplot(a,
        xlab = "Eye",
        beside = TRUE,
        col = cols[rownames(a)])
legend("topright",
       title = "Hair",
       legend = rownames(a),
       fill = cols[rownames(a)],
       bty = "n")

# Visite estes sites para pegar cores.
# browseURL("http://www.w3schools.com/html/html_colors.asp")
# browseURL("http://html-color-codes.info/")

5 Gráficos para uma variável contínua

Para representar a distribuição de frequência de variáveis contínuas tem-se mais opções. A mais simples delas, o histograma, consiste em discretizar os dados agrupando as observações em classes. A frequência das classes é exibida contra as classes.

#-----------------------------------------------------------------------
# Histograma.

# Gráfico básico.
# hist(dus$valor)

hist(dus$valor,
     xlab = "Preço de venda (R$)",
     ylab = "Frequência absoluta",
     col = "orange")
rug(dus$valor)

# Se breaks é um escalar então entende-se que é uma *sugestão* para o
# número de clases.
hist(dus$valor,
     breaks = 15,
     xlab = "Preço de venda (R$)",
     ylab = "Frequência absoluta",
     col = "orange")
rug(dus$valor)

# Se breaks é um vetor então entende-se que são os limites para
# classificação dos valores.
hist(dus$valor,
     breaks = seq(35000, 75000, by = 2500),
     xlab = "Preço de venda (R$)",
     ylab = "Frequência absoluta",
     col = "#7700B7",
     sub = "Amplitude de classe de R$ 2500",
     main = NULL)

# Gráfico onde a altura é a densidade e não a frequência.
hist(dus$valor,
     prob = TRUE,
     breaks = seq(35000, 75000, by = 2500),
     xlab = "Preço de venda (R$)",
     ylab = "Densidade",
     col = "#ba6dff",
     sub = "Amplitude de classe de R$ 2500",
     main = NULL)

O histograma pode representar duas medidas diferentes, porem relacionadas, de frequência. Como argumento prob = FALSE, a altura da barra é o número de registros em cada classe. Com prob = TRUE, o produto da altura (densidade) pela largura (amplitude de classe) de cada barra corresponde a frequência relativa. A soma das frequências relativas é 1 e, portanto, a área do gráfico coberta pelas barras do histograma é unitária.

#-----------------------------------------------------------------------
# Anotações sobre um histograma.

# Com domínio do R se pode fazer gráficos espetaculares, como por
# exemplo esses com variação da tonalidade ou destaque da classe modal.

ht <- hist(dus$valor,
           prob = TRUE,
           breaks = seq(35000, 75000, 2000),
           xlab = "Preço de venda (R$)",
           ylab = "Frequência absoluta",
           sub = "Amplitude de classe de R$ 2500")
rug(dus$valor) # Faz risquinhos no eixo x.

# Destacar a barra da classe modal usando outra cor.
wm <- which.max(ht$counts)
cols <- rep("yellow", length(ht$counts))
cols[wm] <- "red"
cols
##  [1] "yellow" "yellow" "yellow" "yellow" "yellow" "yellow" "yellow" "red"    "yellow"
## [10] "yellow" "yellow" "yellow" "yellow" "yellow" "yellow" "yellow" "yellow" "yellow"
## [19] "yellow" "yellow"
plot(ht, col = cols)

# Traçar os segmentos que indicam o valor interpolado para a moda.
ycoor <- with(ht, counts[wm + 0:1])
xcoor <- with(ht, breaks[wm + 0:1])
segments(xcoor[1], ycoor[1], xcoor[2], ycoor[2], lty = 2)

ycoor <- with(ht, counts[wm - 1:0])
xcoor <- with(ht, breaks[wm + 0:1])
segments(xcoor[1], ycoor[1], xcoor[2], ycoor[2], lty = 2)

# Por semelhança de triangulos a moda obtida é:
ac <- with(ht, diff(breaks[1:2]))
d <- with(ht, abs(diff(counts[wm + (-1:1)])))
xmoda <- with(ht, breaks[wm] + (ac * d[1])/sum(d))
xmoda
## [1] 49789.47
abline(v = xmoda, lwd = 2)

É possível fazer gráficos personalizados com o domínios dos recursos básicos de plotagem do R. O código acima usa a função de alto nível hist() para produzir o histograma. Quando atribuida a um objeto, todos os elementos do histograma ficam salvos para permitir pós processamento (limites e cento das classes, frequência absoluta e relativa, etc). Funções de baixo nível (abline(), segments()) são chamadas para adicionar elementos aos gráficos.

Os dois gráficos abaixo fazem uso das funções de baixo para ilustrar o potencial do R para confecção de gráficos. No entanto, tais gráficos podem não ter muita utilidade prática.

#--------------------------------------------
# Destaque para a classe modal.

plot(ht,
     col = NULL,
     lty = 0,
     ann = FALSE,
     axes = FALSE)
abline(h = seq(0, 100, by = 10), lty = 2)
plot(ht,
     col = cols,
     ann = FALSE,
     axes = FALSE,
     add = TRUE)
rug(dus$valor)
axis(side = 1, at = seq(35000, 75000, 5000))
axis(side = 2, at = seq(0, 100, by = 10))
box(bty = "L")
title(main = "Histograma do valor (R$)",
      sub = "Dados retirados do webmotors.com",
      xlab = "Valor (R$)",
      ylab = "Frequência absoluta")
mtext(side = 3, line = 0,
      text = paste("Amostra de tamanho", length(dus$valor)))
mtext(side = 4,
      line = -1,
      col = "gray70",
      outer = TRUE,
      adj = 0,
      text = "Feito por Walmes Zeviani - walmes@ufpr.br")
legend("topright",
       fill = "red",
       legend = "Classe modal",
       bty = "n")

#--------------------------------------------

# Outra variação de um histograma.
ht <- hist(dus$valor, seq(35000, 75000, 2000), plot = FALSE)
nc <- length(ht$mids)             # Número de classes.
ac <- diff(ht$breaks[1:2])        # Amplitude de classe.
ma <- mean(dus$valor)             # Média da amostra.
md <- median(dus$valor)           # Mediana da amostra.
qts <- fivenum(dus$valor)[c(2,4)] # 1Q e 3Q da amostra.
modal <- which.max(ht$counts)     # Classe modal.
modal <- list(x = ht$mids[modal], y = ht$counts[modal])
colseq <- rgb(red = 0.25,
              blue = 0.7,
              green = seq(0.1, 0.9, length.out = nc))

plot(ht,
     col = colseq,
     ylim = c(0, modal$y + strheight("1")),
     xlab = "Preço de venda (R$)",
     ylab = "Frequência absoluta",
     sub = paste("Amplitude de classe de R$", ac),
     main = NULL,
     border = "gray50")
rug(dus$valor)
text(x = modal$x, y = modal$y, labels = modal$y, pos = 3)
arrows(ma, 0, ma, modal$y/3, code = 1, length = 0.15)
text(ma, modal$y/3, labels = paste("Média:", round(ma,2)), pos = 3)
arrows(md, 0, md, modal$y/6, code = 1, length = 0.15)
text(ma, modal$y/6, labels = paste("Mediana:", round(md,1)),
     pos = ifelse(md<ma, 2, 4))
box()

Responda: o que de informação adicional foi acrescentado com as barras mudando de cor? Nada! A variação das cores está se sobrepondo a informação de posição no eixo horizontal. Ou seja, são dois elementos estéticos (posição e preenchimento) mapeando a mesma informação. Uma boa prática na confecção de gráficos e evitar redundância. Pode até ser que o leitor seja atraído pelo visual pouco ortodoxo do gráfico mas há um desperdício de carga cognitiva para processamento dessa informação visual acessória ou meramente estética.

#-----------------------------------------------------------------------
# Gráficos de densidade.

den <- density(dus$valor)
plot(den)

den
## 
## Call:
##  density.default(x = dus$valor)
## 
## Data: dus$valor (699 obs.);  Bandwidth 'bw' = 1482
## 
##        x               y            
##  Min.   :33555   Min.   :4.340e-09  
##  1st Qu.:44402   1st Qu.:1.390e-06  
##  Median :55250   Median :1.492e-05  
##  Mean   :55250   Mean   :2.302e-05  
##  3rd Qu.:66098   3rd Qu.:4.304e-05  
##  Max.   :76945   Max.   :7.011e-05
# Tipos de função kernel.
formals("density.default")$kernel
## c("gaussian", "epanechnikov", "rectangular", "triangular", "biweight", 
##     "cosine", "optcosine")
den <- density(dus$valor,
               kernel = "triangular")
plot(den)
rug(dus$valor)

# Controle da largura de banda.
den <- density(dus$valor,
               kernel = "rectangular",
               bw = 3000)
plot(den)
rug(dus$valor)
abline(v = seq(35000, 75000, 5000), col = "gray50")
abline(v = seq(35000, 75000, 1000), col = "gray90")

# Realce da classe modal.
den <- density(dus$valor/1000)
str(den)
## List of 7
##  $ x        : num [1:512] 33.6 33.6 33.7 33.8 33.9 ...
##  $ y        : num [1:512] 5.62e-06 6.73e-06 8.03e-06 9.52e-06 1.13e-05 ...
##  $ bw       : num 1.48
##  $ n        : int 699
##  $ call     : language density.default(x = dus$valor/1000)
##  $ data.name: chr "dus$valor/1000"
##  $ has.na   : logi FALSE
##  - attr(*, "class")= chr "density"
x <- eval(parse(text = den$data.name))
ma <- mean(x)             # Média da amostra.
md <- median(x)           # Mediana da amostra.

modal <- which.max(den$y)
modal <- list(x = den$x[modal], y = den$y[modal])

plot(den,
     type = "n",
     xlab = "Preço de venda (R$ x 1000)",
     ylab = "Densidade",
     ylim = c(0, modal$y + strheight("1")),
     main = "",
     sub = paste("Bandwidth:", round(den$bw,3)))
with(den, polygon(x, y, col = "gray90"))
with(modal, {
    segments(x, 0, x, y, col = 2)
    text(x, y, labels = sprintf("Moda: %0.2f", x), pos = 3)
})
arrows(ma, 0, ma, modal$y/3, code = 1, length = 0.15)
text(ma, modal$y/3, labels = sprintf("Média: %0.2f", ma), pos = 3)
arrows(md, 0, md, modal$y/6, code = 1, length = 0.15)
text(ma, modal$y/6, labels = sprintf("Mediana: %0.2f", md),
     pos = ifelse(md<ma, 2, 4))
rug(dus$valor)

O gráfico de densidade empírica kernel é o sucessor ou evolução do histograma. Esse gráfico calcula a densidade em um ponto \(x\) no domínio da variável por meio de uma função kernel que é, de forma simples, uma função de ponderação, e o resultado e uma soma de frequências ponderada pela distância. Existem 7 funções kernel disponíveis. Visite essa aplicação Shiny para compreender visualmente como funciona a densidade kernel: http://shiny.leg.ufpr.br/walmes/density/.

#-----------------------------------------------------------------------
# Gráfico de frequência acumulada empírica.

y <- ecdf(dus$valor)
plot(y)

plot(y,
     xlab = "Preço de venda (R$)",
     ylab = "Frequência relativa acumulada",
     cex = NA,
     verticals = TRUE,
     main = NULL)

# Destacando a frequência de veículos com preço de 50 à 60 mil.
lim <- c(50000, 60000)
ptbl <- prop.table(table(cut(dus$valor,
                             breaks = c(-Inf, lim, Inf))))
cs <- cumsum(ptbl)[seq_along(lim)]

# As observações do intervalo correspondem ao valor 1.
ins <- findInterval(dus$valor, vec = lim)
table(ins)
## ins
##   0   1   2 
## 254 333 112
plot(y,
     xlab = "Preço de venda (R$)",
     ylab = "Frequência relativa acumulada",
     cex = NA,
     col = "#00af20",
     lwd = 2,
     verticals = TRUE,
     main = NULL)
segments(x0 = lim,
         y0 = 0,
         x1 = lim,
         y1 = cs,
         lty = 2)
segments(x0 = lim,
         y0 = cs,
         x1 = par()
         $usr[3],
         y1 = cs,
         lty = 2)
arrows(x0 = lim[1],
       y0 = cs[1],
       x1 = lim[1],
       y1 = cs[2],
       code = 3,
       length = 0.15)
text(x = lim[1],
     y = median(cs),
     labels = sprintf("%0.3f", ptbl[2]),
     srt = 90,
     adj = c(0.5,-0.5))
rug(dus$valor[ins == 1L], col = "#00af20")
rug(dus$valor[ins != 1L], col = "black")

6 Gráfico para duas variáveis contínuas

#-----------------------------------------------------------------------
# Diagrama de dispersão.

dus2 <- subset(dus,
               complete.cases(cbind(km, valor)),
               select = c(cambio, valor, km))
dus2 <- transform(dus2, km = km/1000, valor = valor/1000)

# Diagrama de dispersão básico.
plot(valor ~ km, data = dus2)

# Adicionar uma linha de tendência suave.
plot(valor ~ km,
     data = dus2,
     xlab = "Distância percorrida (km)",
     ylab = "Preço de venda (R$)")
with(dus2, {
    lines(lowess(x = km, y = valor),
          lwd = 2,
          col = "#ff0050")
})

# Usar cores diferentes para identificar o tipo de câmbio, com linhas
# de tendência e grid.

cols <- c("#db0d9d", "#0c0099")
levels(dus2$cambio)
## [1] "AUTOMÁTICO" "MANUAL"
plot(valor ~ km,
     data = dus2,
     type = "n",
     xlab = "Distância percorrida (km)",
     ylab = "Preço de venda (R$)")
i <- 0
by(dus2,
   INDICES = dus2$cambio,
   FUN = function(data) {
       i <<- i + 1
       with(data, {
           points(x = km, y = valor, col = cols[i], pch = 19)
           lines(lowess(x = km, y = valor),
                 col = cols[i],
                 lwd = 1.5)
           rug(km, side = 1, col = cols[i])
           rug(valor, side = 4, col = cols[i])
           invisible()
       })
   })
## dus2$cambio: AUTOMÁTICO
## NULL
## ------------------------------------------------------------------- 
## dus2$cambio: MANUAL
## NULL
legend("top",
       lty = 1,
       col = cols,
       legend = levels(dus2$cambio),
       lwd = 1.5,
       bty = "n")
grid()

O diagrama de dispersão é um dos gráficos mais fáceis de produzir pois não requer pré-processamento dos dados. Ou seja, o gráfico exibe todos os pares de pontos, diferente dos gráficos de barras que presentam o resultado de uma agregação dos dados: as frequências absolutas ou relativas.

Por outro lado, quando-se deseja destacar categorias usando cores ou símbolos, é necessário trabalho manual. Uma das principais vantagens da lattice e ggplot2 é fazer isso de forma bem mais simples.

7 Gráfico para variável contínua e categórica

#-----------------------------------------------------------------------

# Preço em função dos anos.
boxplot(valor ~ ano, data = dus)

# Edita níveis do fator.
levels(dus$cat)
## NULL
levels(dus$cat) <- trimws(levels(dus$cat))
dus2 <- droplevels(subset(dus, cat != ""))
dus2 <- transform(dus2,
                  valor = valor/1000,
                  km = km/1000,
                  cat = factor(cat))

boxplot(valor ~ cat,
        data = dus2,
        xlab = "Modelo",
        ylab = "Preço de venda (R$)")

# Larguras proporcionais à raiz da quantidade em cada grupo.
boxplot(valor ~ cat,
        data = dus2,
        varwidth = TRUE,
        pars = list(boxwex = 1),
        xlab = "Modelo",
        ylab = "Preço de venda (R$)")

table(dus2$cat)
## 
##                    DYNAMIQUE     EXPRESSION      TECH ROAD   TECH ROAD II  
##             40            442             51            164              2
# Indicação do valor da média.
mds <- with(dus2, tapply(valor, cat, mean))
mds
##                    DYNAMIQUE     EXPRESSION      TECH ROAD   TECH ROAD II  
##       47.17897       52.75835       47.34853       59.79432       69.24500
bp <- boxplot(valor ~ cat, data = dus2)

bp
## $stats
##        [,1]  [,2]   [,3]    [,4]   [,5]
## [1,] 38.900 42.90 38.000 49.0000 65.990
## [2,] 43.900 48.99 44.195 56.9995 65.990
## [3,] 47.945 51.90 46.999 59.9000 69.245
## [4,] 49.900 55.50 49.900 62.9000 72.500
## [5,] 58.040 65.00 56.990 68.4000 72.500
## 
## $n
## [1]  40 442  51 164   2
## 
## $conf
##          [,1]     [,2]    [,3]     [,4]     [,5]
## [1,] 46.44608 51.41075 45.7368 59.17201 61.97184
## [2,] 49.44392 52.38925 48.2612 60.62799 76.51816
## 
## $out
## [1] 65.90000 66.82871 68.50000 66.99000 65.90000 66.90000 66.90000 67.70000 60.00000
## 
## $group
## [1] 2 2 2 2 2 2 2 2 3
## 
## $names
## [1] " "              " DYNAMIQUE "    " EXPRESSION "   " TECH ROAD "    " TECH ROAD II "
# Amplitude interquartílica.
aiq <- bp$stats[4, ] - bp$stats[2, ]
l <- bp$stats[2, ] - 1.5 * aiq
u <- bp$stats[4, ] + 1.5 * aiq
i <- seq_along(u)

boxplot(valor ~ cat,
        data = dus2,
        notch = TRUE,
        col = "#ff5c21",
        xlab = "Modelo",
        ylab = "Preço de venda (R$)")
points(x = 1:nlevels(dus2$cat), y = mds, pch = 4, cex = 1.5)
segments(x0 = i - 0.5, x1 = i + 0.5, y0 = l, y1 = l,
         col = "gray50", lty = 3)
segments(x0 = i - 0.5, x1 = i + 0.5, y0 = u, y1 = u,
         col = "gray50", lty = 3)

O gráfico/diagrama de caixas e bigodes (box and whiskers) representa os 5 números de Tukey: mínimo, 1 quartil, mediana, 3 quartil e máximo. Alguns pontos são represetados além da extremidade do bigode porque ultrapassam a linha imaginária construída baseana na amplitude interquartílica (AIQ = 3 quartil - 1 quartil). A opção notch = TRUE faz um entalhe para representar o intervalo de confiança para a mediana, baseados na distribuição normal assintótica da mediana. Visite ?boxplot.stats para mais detalhes.

8 Composição de gráfico

#-----------------------------------------------------------------------
# Gráficos com o valor para a média e barra de erro para o
# desvio-padrão.

res <- aggregate(valor ~ cat,
                 data = dus2,
                 FUN = function(x) {
                     c(m = mean(x), s = sd(x))
                 })

# Criando os limites superior e inferior.
res <- transform(res,
                 lwr = valor[, 1] - valor[, 2],
                 upr = valor[, 1] + valor[, 2],
                 catf = as.integer(cat))
res
##              cat   valor.m   valor.s      lwr      upr catf
## 1                47.178975  4.210052 42.96892 51.38903    1
## 2     DYNAMIQUE  52.758352  4.968483 47.78987 57.72684    2
## 3    EXPRESSION  47.348529  4.625478 42.72305 51.97401    3
## 4     TECH ROAD  59.794317  3.986051 55.80827 63.78037    4
## 5  TECH ROAD II  69.245000  4.603265 64.64173 73.84827    5
# dev.off()

# Com boxplot e pontos dispersos dos lados.
xlim <- extendrange(x = 1:nlevels(dus2$cat), f = 0.1)
ylim <- extendrange(x = c(res$lwr, res$upr, dus2$valor), f = 0.1)

par(mar = c(5.1, 4.1, 4.1, 0))
layout(matrix(c(1, 2), ncol = 2), widths = c(0.85, 0.15))
with(dus2, {
    plot.default(x = jitter(as.integer(cat), factor = 0.25) - 0.2,
                 y = valor,
                 xaxt = "n",
                 ann = FALSE,
                 col = "gray50",
                 xlim = xlim,
                 ylim = ylim)})
grid()

with(res, {
    points(x = catf, y = valor[, "m"], pch = 19)
    arrows(catf, lwr, catf, upr, code = 3, angle = 90, length = 0.05)
    axis(side = 1, at = catf, labels = as.character(cat), cex.axis = 0.95)
})

title(xlab = "Categoria", ylab = "Valor (R$)")
mtext(side = 3, line = 0,
      text = expression("Barras de erro representam " * bar(x) %+-% 1 * s))

boxplot(valor ~ cat,
        at = 1:nlevels(dus2$cat) + 0.2,
        col = "gray45",
        data = dus2,
        add = TRUE,
        ann = FALSE,
        axes = FALSE,
        pars = list(boxwex = 0.1))

par(mar = c(5.1, 0.1, 4.1, 1))
yhist <- hist(dus2$valor, plot = FALSE, breaks = 20)
with(yhist, {
    plot(x = NULL,
         y = NULL,
         ann = FALSE,
         axes = FALSE,
         ylim = ylim,
         xlim = c(0, max(density)))})
rug(side = 2, dus2$valor)
snc <- 1:length(yhist$mids)
with(yhist, {
    rect(0,
         breaks[snc],
         density[snc],
         breaks[snc+1],
         col = "gray70")})
den <- density(dus2$valor)
with(den, {
    lines(x = y, y = x, col = "red", lwd = 2)
})

25px

Licença Creative Commons 4.0

Este conteúdo está disponível por meio da Licença Creative Commons 4.0