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:
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: Factor w/ 11 levels "RENAULT DUSTER 1.6 4X2 16V FLEX 4P MANUAL",..: 3 1 2 2 1 1 2 3 6 2 ... # $ cor : Factor w/ 9 levels "Azul","Branco",..: 5 1 5 5 5 8 6 6 6 6 ... # $ km : int 31442 40800 56000 NA 45000 50000 44000 30000 41000 55000 ... # $ ano : Factor w/ 7 levels "2011/2011","2011/2012",..: 2 2 2 2 2 4 3 4 2 2 ... # $ valor : num 41990 42500 42900 42990 43800 ... # $ cambio: Factor w/ 2 levels "AUTOMÁTICO","MANUAL": 2 2 2 2 2 2 2 2 2 2 ... # $ poten : num 1.6 1.6 1.6 1.6 1.6 1.6 1.6 1.6 2 1.6 ... # $ trac : Factor w/ 2 levels "4X2","4X4": 1 1 1 1 1 1 1 1 1 1 ... # $ cat : Factor w/ 5 levels " "," DYNAMIQUE ",..: 3 1 2 2 1 1 2 3 2 2 ... # $ novo : Factor w/ 2 levels "novo","usado": 2 2 2 2 2 2 2 2 2 2 ... # 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.
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" # [4] "darkolivegreen2" "darkolivegreen3" "darkolivegreen4" # [7] "darkseagreen" "darkseagreen1" "darkseagreen2" # [10] "darkseagreen3" "darkseagreen4" "forestgreen" # [13] "green" "green1" "green2" # [16] "green3" "green4" "greenyellow" # [19] "lawngreen" "lightgreen" "lightseagreen" # [22] "limegreen" "mediumseagreen" "mediumspringgreen" # [25] "palegreen" "palegreen1" "palegreen2" # [28] "palegreen3" "palegreen4" "seagreen" # [31] "seagreen1" "seagreen2" "seagreen3" # [34] "seagreen4" "springgreen" "springgreen1" # [37] "springgreen2" "springgreen3" "springgreen4" # [40] "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,] "Azul" "blue" # [2,] "Branco" "white" # [3,] "Cinza" "gray50" # [4,] "Indefinida" "Yellow" # [5,] "Prata" "gray90" # [6,] "Preto" "black" # [7,] "Verde" "green4" # [8,] "Vermelho" "red" # [9,] "Vinho" "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() # null device # 1 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.
#----------------------------------------------------------------------- # Gráficos de barras emplilhadas (stacked) e lado a lado. 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
Barras lado a lado
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:
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:
#----------------------------------------------------------------------- # 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] 9.273014 # 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/")
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" # [8] "red" "yellow" "yellow" "yellow" "yellow" "yellow" "yellow" # [15] "yellow" "yellow" "yellow" "yellow" "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 - ") 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) # Warning in rug(dus$valor): some values will be clipped 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")
#----------------------------------------------------------------------- # 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.
#----------------------------------------------------------------------- # Preço em função dos anos. boxplot(valor ~ ano, data = dus) # Edita níveis do fator. levels(dus$cat) # [1] " " " DYNAMIQUE " " EXPRESSION " " TECH ROAD " # [5] " TECH ROAD II " levels(dus$cat) <- trimws(levels(dus$cat)) dus2 <- droplevels(subset(dus, cat != "")) dus2 <- transform(dus2, valor = valor/1000, km = km/1000) 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 # 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 # 52.75835 47.34853 59.79432 69.24500 bp <- boxplot(valor ~ cat, data = dus2) bp # $stats # [,1] [,2] [,3] [,4] # [1,] 42.90 38.000 49.0000 65.990 # [2,] 48.99 44.195 56.9995 65.990 # [3,] 51.90 46.999 59.9000 69.245 # [4,] 55.50 49.900 62.9000 72.500 # [5,] 65.00 56.990 68.4000 72.500 # # $n # [1] 442 51 164 2 # # $conf # [,1] [,2] [,3] [,4] # [1,] 51.41075 45.7368 59.17201 61.97184 # [2,] 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 # [9] 60.00000 # # $group # [1] 1 1 1 1 1 1 1 1 2 # # $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$)") # Warning in bxp(structure(list(stats = structure(c(42.9, 48.99, 51.9, # 55.5, : some notches went outside hinges ('box'): maybe set notch=FALSE 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.
#----------------------------------------------------------------------- # 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 DYNAMIQUE 52.758352 4.968483 47.78987 57.72684 1 # 2 EXPRESSION 47.348529 4.625478 42.72305 51.97401 2 # 3 TECH ROAD 59.794317 3.986051 55.80827 63.78037 3 # 4 TECH ROAD II 69.245000 4.603265 64.64173 73.84827 4 # 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) }) |