Gráficos

Gráficos com plot

Uma parte importante da análise de dados esta na visualização destes dados em forma gráfica. A representação visual de dados permite, muitas vêzes, o reconhecimento de padrões que dificilmente seriam percebidos apenas com tabelas e números. R fornece várias funções para representar dados graficamente, tanto em gráficos bidimensionais quanto tridimensionais. Em particular dá-se ênfase aos gráficos estatísticos, tais como histogramas, curvas de distribuições, gráfico de barras e outros. Existem métodos gerais que se aplicam à diversas formas básicas de gráficos. Pode-se incluir títulos, nomes para os eixos, cores, representações por pontos linhas e sinais variados e anotações.

A função plot() é a mais básica na geração de um gráfico.

> x <- -10:10;   y <- x^2;  plot(x,y)    # resultado na figura 1
> # Parâmetro para tornar a linha contínua
> plot(x,y, type="l")                    # resultado na figura 2

Alguns dos parâmetros são listados abaixo:

plot(x, y, ...)

x coordenadas horizontais dos pontos. Pode ser um objeto numerado.
y coordenadas verticais dos pontos. Omitido se x é um objeto numerado.
Argumentos adicionais (parâmetros gráficos).

Entre os argumentos adicionais:

type = “p” (pontos), “l” (linhas), “b” (ambos),
“h” (histograma), “s” (degraus), “n” (em branco)
main = título principal
sub = subtítulo
xlab = título para eixo x
ylab = título para eixo y
asp = aspecto (razão y/x).

Observe que, se u é um objeto ordenado, então plot usa como coordenada x a ordem dos elementos. Por exemplo:

> u <- rnorm(10)
> # Os dois gráficos seguintes são idênticos
> plot(u)
> plot(1:10, u)

Algumas funções sobreescrevem o gráfico já traçado. É o caso da função lines. É possível alterar características das linhas com os parâmetros lwd (largura da linha) e lty (tipo da linha), como se mostra no exemplo:

> a <- 1:20; b <- a^2
> plot(a, .1*b ,type="l")   # linha 1
> lines(a, .2*b , lwd=4 )   # linha 2
> lines(a, .3*b , lwd=2)    # linha 3
> lines(a, .4*b , lty=3)    # linha 4
> points(a,.5*b, pch=3)     # pontos 5
> text(10, 2, "Título do gráfico")  # título na posição 10 , 2

As linhas acima resultam no gráfico:

Se vários gráficos devem ser analisados ao mesmo tempo, uma nova instância da janela de saída gráfica pode ser aberta com o comando dev.new() ou X11() (apenas para sistemas tipo Unix). Pode-se navegar entre “devices” gráficos abertos usando dev.new(), dev.next(), dev.prev(), dev.set() e dev.off().

> plot(rnorm(10))      # plota o primeiro gráfico
> dev.new()            # abre nova janela
> plot(rnorm(20))      # plota o segundo gráfico

Os gráficos podem ser enviados diretamente para arquivos nos formatos JPEG, BMP, PDF, TIFF, PNG, entre outros. Como exemplo, direcionamos a saída gráfica para um arquivo JPEG:


> # grava arquivo hiperbole.jpg
> jpeg(file='hiperbole.jpg')
> # plota gráfico
> plot(x<- -100:100, 1/x, type='l', main="hipérbole")
> # fecha janela gráfica
> dev.off()
> # Grava arquivo jpeg com a imagem à direita.

Outras funções capazes de escrever por cima de um gráfico já plotado são locator(n) e identify(). A primeira serve para que o se selecione regiões do gráfico utilizando o botão esquerdo do mouse até que se tenha um número n de pontos selecionados (ou até pressionar o botão direito do mouse, para terminar). A cada clique dado com o botão esquerdo do mouse a função retorna no console as coordenadas do clique. Por exemplo:


> x <- 1:100; y <- sqrt(x)
> plot(x,y, type="l")
> text(locator(1), "x é aqui!")
> text(locator(1), "y é aqui!")
> text(locator(5), paste("<", 1:4, ">")
> # A linha acima marca 4 pontos
> # no gráfico, com o texto:
> # "<1>", "<2>", "<3>", "<4>"
> # respectivamente

Para os próximos passos usaremos o dataframe carregado por padrão no R de nome mtcars. Este é um conjunto de dados sobre automóveis com campos mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb. Antes de prosseguir, observe que as quatro formas de notação abaixo são equivalentes:

> # ------------------------------ (1)
> plot( mtcars$mpg, mtcars$cyl)
> # ------------------------------ (2)
> attach(mtcars)
> plot( mpg, cyl)
> detach(mtcars)
> # ------------------------------ (3)
> attach(mtcars)
> plot(cyl ~ mpg)
> detach(mtcars)
> # ------------------------------ (4)
> plot( cyl ~ mpg, data= mtcars )

Na quarta forma usamos y ~ x para representar x como variável independente, y como variável dependente.

Aproveitamos a oportunidade para conhecer uma sintaxe especial. O comando plot( cyl ~ ., data= mtcars ) realiza a plotagem de todos os gráficos de cyl como função de todos os demais campos de mtcars. O prompt Hit to see next plot: aparece entre as operações.


> attach(mtcars)
> plot(mpg ~ wt)
> abline(lm(mpg ~ wt))
> title("Regressão Linear de Consumo por Peso")
> detach(mtcars)
Para enviar este gráfico para um arquivo pdf basta cercar todas as linhas acima pelos comandos pdf("NomeArquivo.pdf") e dev.off().

A função abline(a,b) traça uma reta sobre o gráfico, recebendo os parâmetros a como a interseção da reta com o eixo y (x = 0) e b como a inclinação da reta. Ela recebe como argumento lm(y~x) que retorna diversos dados sobre a regressão linear da função y~x, entre eles os parâmetros a e b necessários para definar a reta.

A função abline possui outros parâmetros. Entre eles:

abline(a=NULL, b=NULL, h=NULL, v=NULL, col=, ...)
       a = interseção com eixo y
       b = inclinação da reta
       h = y (traça reta horizontal por y)
       v = x (traça reta vertical por x)
       col = cor: "red", "blue", etc; ou rgb(x,y,z), onde x, y, z ∈ [0, 1]
             ou hexadecimal #abcdef; a, ..., f ∈ [0, f], hexadecimal.

Alguns exemplos de uso de abline(). O código seguinte gera os gráficos abaixo:

> plot(x<- 0:10, x) # plotar uma reta
> title("Reta y = x")
> abline(v=4)       # reta vertical por x = 4
> abline(v=6, col="blue") # reta vertical por x = 6, azul
> abline(h=8, col="#779900") # outra cor
> # Usando a tabela cars (embutida em R)
> dev.new()
> plot(cars)
> abline(v=c(15,20), col=c("blue", "red"), lty=c(1,2), lwd=c(1, 3), h=40)

Parâmetros Gráficos

O conjunto de parâmetros para construção de gráficos podem ser lidos e (alguns deles) alterados através da função par(). Podem ser características como fonts, cores, eixos e títulos.

par(..., no.readonly = FALSE)
Argumentos:
      no.readonly = Booleano. Se TRUE apenas parâmetros que podem ser
      alterados pelo usuário são exibidos.
      ... Outros parâmetros são passados na forma:
      par1 = valor1, ..., parn = valorn

Se nenhum parâmetro for fornecido par() exibe uma lista dos parâmetros atuais, par(no.readonly = TRUE) exibe uma lista dos parâmetros que podem ser alterados. Valores alterados dessa forma permanecem válidos durante a sessão.

Para exemplificar suponha que pretendemos ter nossos gráficos plotados com quadrados sólidos ligados por retas. O seguinte código pode ser usado:

> # parâmetros default são armazenados
> oldPar <- par(no.readonly=TRUE)
> par(lty=1, pch=15)
> plot(cars$dist ~ cars$speed, type="b")
> title("Usando quadrados e retas")
> # parâmetros default são restaurados
> par(oldPar)
> # Claro que o mesmo efeito seria obtido com
> plot(cars$dist ~ cars$speed, type="b", lty=1, pch=15)

Alguns parâmetros são listados na tabela:

Parâmetro Descrição
pch símbolo usado para marcar pontos.
cex tamanho do símbolo, relativo ao default. 1 = default, 1.5 is 50% maior, etc.
lty tipo da linha.
lwd largura da linha, relativa ao default. Ex.: lwd=2 dupla largura.
Valores de pch, lty

As cores que podem ser alteradas nos gráficos estão listas na tabela seguinte:

Parâmetro Descrição
col cor default do gráfico.
col.axis cor para texto nos eixos.
col.lab cor para labels nos eixos.
col.main cor do título.
col.sub cor do subtítulo.
fg cor do primeiro plano.
bg cor de fundo.

Para o parâmetro col algumas funções aceitam valores reciclados. Por ex., se col=c("blue", "green") e três curvas são exibidas então a primeira e a terceira serão azuis, a segunda verde. Cores podem ser especificadas por índice, nome, valores hexadecimais, RGB e HSV. A função colors() exibe uma lista de todas as cores disponíveis, por nome.

Algumas funções permitem a criação de palhetas, vetores com n cores contíguas:

rainbow(n, s = 1, v = 1, start = 0, end = max(1, n - 1)/n, alpha = 1,
        heat.colors(n, alpha = 1),
        terrain.colors(n, alpha = 1),
        topo.colors(n, alpha = 1),
        cm.colors(n, alpha = 1))
Parâmetros:
n número (≥ 1) de cores no vetor.
s, v “saturação” e “valor” no formato HSV.
start [0,1] cor inicial para o arco-íris (rainbow).
end [0,1] cor final para o arco-íris (rainbow).
alpha [0,1], transparência.

Vamos explorar o uso destas palhetas de cores na próxima seção.

Para especificar o estilo, tamanho e família das fontes os seguintes parâmetros gráficos podem ser usados:

Parâmetro Descrição
cex magnificação do texto: 1 = default, 1.5 = 50% maior; 0.5 = 50% menor, etc.
cex.axis magnificação dos eixos, relativo a cex.
cex.lab magnificação do texto nos eixos, relativo a cex.
cex.main magnificação do texto do título, relativo a cex.
cex.main magnificação do texto do subtítulo, relativo a cex.
font inteiro para fonte: 1 = simples, 2 = negrito, 3 = itálico, 4 = negrito itálico, 5=símbolo.
font.axis fonte nos eixos.
font.lab fonte nos labels de eixos.
font.main fonte nos títulos.
font.sub fonte nos subtítulos.
ps tamanho do ponto na fonte (~1/72 polegada).
family família da fonte. Os padrões são serif, sans e mono

Por exemplo, após a aplicação dos parâmetros:
par(cex.main=3, cex.lab=1.5, font.lab=2, font.main=4, font.sub=3)
o texto nos gráficos serão representados com: títulos com fontes 3 vezes maior que o padrão definido em cex, eixos magnificados em 1.5, labels em negrito nos eixos, títulos em negrito itálico e subtítulos em itálico.

Para controle das dimensões do gráfico e margens usamos:

Parâmetro Descrição
pin largura e altura do gráfico, em polegadas.
mai vetor com larguras das margens, c(inferior, esquerda, superior, direita) em polegadas.
mai vetor com larguras das margens, c(inferior, esquerda, superior, direita) em linhas. [default = c(5, 4, 4, 2) + 0.1].

Função barplot()

A função barplot() permite a exibição de gráficos de barras. Um resumo de seus parâmetros está mostrado abaixo.

barplot(height, width = 1, space = NULL, names.arg = NULL,
        horiz = FALSE, density = NULL, col = NULL, border = par("fg"),
        main = NULL, sub = NULL, xlab = NULL, ylab = NULL, axes = TRUE)
Parâmetros:
height vetor ou matriz contendo altura das barras.
width vetor com largura das barras.
space espaço deixado antes da barras (uma fração da largura).
names.arg vetor de nomes para barras.
horiz booleano. FALSE = barras verticais; TRUE = barras horizontais.
density vetor, densidade do hachurado. NULL= sem hachura.
col vetor de cores das barras.
border cor das bordas das barras.
main,sub título e subtítulo
xlab texto para o eixo x.
ylab texto para o eixo y.
axes booleano. Se eixos são desenhados

O código acima gera o gráfico de barras abaixo:

Gráfico de barras

A função bar plot pode receber uma matriz como argumento. Para ilustrar vamos usar a função table() para tabelar dados no data frame mtcars. Este data frame possui o campo mtcars$carbs que lista o número de carburadores de uma lista de automóveis. Em seguida criamos uma tabela com um teste clínico hipotético para o tratamento da gripe usando um antiviral, vitammina C e um “chazinho”.

> carburadores <- table(mtcars$carb)
> carburadores
 1  2  3  4  6  8
 7 10  3 10  1  1
> # A tabela mostra que existem 7 modelos com 1 carburador, 10 com 2, etc.
> barplot(carburadores, main="Modelos x carburadores", horiz=TRUE,
          names.arg=c("1", "2", "3","4", "6", "8"), xlab="Quantos modelos",
          ylab="Número de carburadores", col=rainbow(6))

> testeClinico <- matrix(c(45,9,12,4,31,31,1,10,7), ncol=3, byrow=TRUE)
> cores <-c("#5FC0A0", "#DE7A6B", "#6BA0DE")
> colnames(testeClinico) <- c("Antiviral","Vitamina C","Chazinho")
> rownames(testeClinico) <- c("Melhorou","Sem alteração","Piorou")
> testeClinico
              Antiviral Vitamina C Chazinho
Melhorou             45          9       12
Sem alteração         4         31       31
Piorou                1         10        7

> resultado <- as.table(testeClinico)
> barplot(resultado, main="Gripe: teste clínico", xlab="Medicamento",
          ylab="Eficácia", col=cores, legend=rownames(resultado))

O código acima gera os gráficos:

O mesmo gráfico, com os dados agrupados por tipo de medicamento testado pode ser obtido ao se acrescentar o parâmetro beside=TRUE que força a exibição de dados lado à lado para uma mesma coluna:


cores <-c("#5FC0A0", "#DE7A6B", "#6BA0DE")
> barplot(resultado,
          main="Gripe: teste clínico",
          xlab="Medicamento",
          ylab="Eficácia",
          col=cores, beside=TRUE)

Usando funções de agregamento e passando o resultado para barplot() pode-se representar médias, medianas, desvios padrões e outros em gráficos de barras.

Para experimentar com esta funcionalidade usaremos o dataset embutido com o R denomidado states (US State Facts and Figures). Ele contém dados antigos sobre os 50 estados americanos. Em particular usaremos state.region, um fator contendo as regiões de cada estado (Northeast, South, North Central, West) e state.x77, uma matriz com 50 linhas e 8 colunas com informações sobre os estados. O campo state.x77$Illiteracy contém taxas de analfabetismo nos estados americanos em 1970, como porcentagem da população.

> # Carregamos uma palheta de 4 cores
> cor <- c("#F3E16E", "#6EC6F3", "#6FF36E", "#F36E84")

> reg <- state.region
> levels(reg)   # as regiões estão em inglês
[1] "Northeast"     "South"         "North Central" "West"
# Para traduzir para o português alteramos os levels:
> levels(reg) <- c("Nordeste","Sul","Central Norte","Oeste")
> levels(reg)
[1] "Nordeste"   "Sul"   "Central Norte"   "Oeste"

> # Usamos apenas a 3a. coluna de state.x77 (analfabetismo %)
> analfabetismo <- state.x77[,3]
> # Criamos um dataframe com informações: regiões x analfabetismo
> estados <- data.frame(reg, analfabetismo)

> # Agregamos a informação sobre analfabetismo por região,
> # usando a função mean (média)
> media <- aggregate(estados$analfabetismo, by=list(estados$reg), FUN=mean)
> # para alterar os nomes das colunas
> names(media) <- c("regiao", "taxa")

> # Ordenamos o resultado por taxa de analfabetismo
> media <- media[order(media$taxa),]
> View(media)   #  resulta na tabela 1

> # plotando o gráfico de barras
> barplot(media$taxa, names.arg=media$regiao, col=cor)
> title("Analfabetismo nos EUA / por região"
> #  resultado no gráfico abaixo

O código acima gera o gráfico:

Função pie()

Gráficos de setores ou gráficos de pizza (pie charts) também são úteis para a representação de dados.

pie(x, labels = names(x), edges = 200, clockwise = FALSE,
    init.angle = if(clockwise) 90 else 0,
    col = NULL, main = NULL, ...)
Parâmetros:
x vetor de valores, exibidos como áreas dos setores no gráfico.
labels nomes para legendas dos setores. NA ou “” = sem legenda.
edges borda externa é um polígono com este número de lados.
clockwise booleano. Sentido horário ou não das fatias.
init.angle ângulo inicial (da primeira fatia).
col vetor de cores de preenchimento das fatias.
main título do gráfico.
> z <- (-10:10)^2 - 50
> barplot(z, col=rainbow(25), main="Gráfico de barras", ylab="y=x^2-50")
> dev.new()

> legenda <- paste("fatia ",1:16) # gera vetor fatia 1, ..., fatia 16
> cores <- c("#F3E16E", "#6EC6F3", "#6FF36E", "#F36E84")
> pie(rep(1,16), col=cores, labels=legenda, main="Setores")

O seguinte gráfico é gerado:

Gráfico de Setores

O código abaixo gera três gráficos de setores (pie charts). As populações listadas para os cinco países mais populosos são dadas em milhões. No gráfico-2 os percentuais (apenas entre estes 5 países) são exibidas. No terceiro gráfico a package plotrix é usada para desenhar um gráfico em 3 dimensões.

> populacao <- c(1420, 1368, 329, 269, 212)
> pais <- c("China", "India", "EUA", "Indonesia" , "Brasil")
> pie(populacao, labels=pais, main = "população em milhões")
> # Gera Gráfico-1
> pc <- round(populacao/sum(populacao)*100)
> pc  # porcentagem de população (entre estes 5 países)
[1] 39 38  9  7  6
> label <- paste(pais, "-", pc, "%", sep="")
> label
[1] "China-39%"  "India-38%"  "EUA-9%"  "Indonesia-7%"  "Brasil-6%"
> pie(populacao, labels=label, col=rainbow(length(labels)), main = "população em milhões (%)")
> # Observe que length(labels)=5 e temos 5 cores geradas
> # Gera Gráfico-2
> library(plotrix)   # deve ser instalado com install.packages("plotrix")
> pie3D(populacao, labels=label,explode=0.1, main="3D Gráfico setores")
> # Gera Gráfico-3

Os gráficos de setores são podem ser úteis para uma visualização rápida de uma relação entre valores. No entanto podem dificultar a análise mais minuciosa destes dados. Por exemplo, se dois setores tem aproximadamente o mesma área pode ser difícil perceber qual é maior. Em geral o uso de barras é mais recomendado.

Uma alternativa atraente é o fan.plot, carregado junto com a library plotrix. Neste tipo de gráfico os setores são sobrepostos e seus raios variados para que todos apareçam na representação.


> library(plotrix)
> populacao <- c(1420, 1368, 329, 269, 212)
> pais <- c("China", "India", "EUA",
            "Indonesia" , "Brasil")
> fan.plot(populacao, labels=pais,
           main = "Usando o fan.plot",
           col=rainbow(5))
> # O gráfico à direita é gerado.

Histogramas com a função hist()

Histogramas (ou distribuições de frequências) são uma forma de exibir a distribuição de uma variável contínua. A faixa de valores a serem analisados é dividida em classes (que podem ser ou não uniformes). A base de cada retângulo na representação é dada pela extensão da classe e a altura pela quantidade de dados (frequências) dentro de cada classe. Histogramas são criados com a função hist(v) onde v é um vetor numérico.
O parâmetro freq=FALSE gera um gráfico baseado em densidades de probabilidade e não em frequências. O parâmetro break informa em quantos classes os dados devem ser divididos. Por default as classes são divididas uniformemente.

> distUniforme <- runif(1000, 0, 10)
> # gera 1000 valores distribuídos uniformemente, com média 0 e desvio padrão 1
> hist(distUniforme, col=rainbow(10))
> # plota o histograma dessa distribuição

> distNormal <- rnorm(100000, 0, sd=2)
> # gera 10^5 valores distribuídos aleatóriamente com distribuição normal,
> # com média 0 e desvio padrão 2
> hist(distNormal, col=rainbow(12))
> # plota o histograma dessa distribuição

Para os exemplos que se seguem usaremos o data frame faithful, embutido na instalação do R. Este é um data frame contendo 272 observações, registradas em 2 variáveis numéricas: eruptions, tempo de erupção, e waiting intervalo entre erupções ambas em minutos.

> attach(faithful)
> hist(eruptions)
> # Gera o primeiro histograma abaixo
> hist(faithful$eruptions, seq(1.6, 5.2, 0.2), prob=TRUE, col=rainbow(18))
> lines(density(faithful$eruptions, bw=0.1))
> rug(faithful$eruptions)
> # Gera o segundo histograma abaixo
> # Os efeitos de lines() e rug() estão marcados no gráfico
> detach(faithful)

Gráficos de densidade kernel

Dada uma variável aleatória discreta, a estimativa de densidade kernel (EDK) é uma técnica para se estimar a função de densidade de probabilidade que melhor se ajusta à esta variável. Ela busca suavizar dados discretos fazendo inferências sobre uma amostra finita de dados. Desta forma é possível extrapolar dados discretos, fazendo previsões para valores não diretamente medidos. O kernel (ou núcleo) é uma função simétrica, suave. Tipicamente se usa a gaussiana, embora outras funções podem ser escolhidas. No R um gráfico de densidade kernel para o vetor x pode ser obtido com plot(density(x, )).


> attach(mtcars)
> # Construimos uma densidade usando
> # mtcars$mpg (milhas por galão)
> d <- density(mpg)
> plot(d,
       main="Milhas/galão (densidade kernel)")
> # Para colorir a área sob a curva
> polygon(d, col="lightblue", border="black")
> # Para inserir marcas nos valores
> # discretos que geraram a densidade
> rug(mpg, col="red")
> detach(mtcars)

A função polygon() desenha um polígono com vértices x, y, neste caso os pares fornecidos pela densidade. rug() marca os valores presentes no vetor mtcars$mpg.

Gráficos de densidade kernel podem ser usados para comparar dados em grupos distintos. Para isso usaremos o pacote sm. Nesse pacote usamos
a função sm.density.compare() para sobrepor gráficos nos grupos dentro de fatores cyl.f, que são, no caso, 4, 6 e 8. O formato é sm.density.compare(x, factor) onde x é um vetor numérico e o fator fornece a variável de agrupamento.

> install.package("sm")
> library(sm)
> attach(mtcars)
> cyl.f <- factor(cyl, levels= c(4,6,8),
           labels = c("4 cilindros", "6 cilindros", "8 cilindros"))
> sm.density.compare(mpg, cyl, xlab="Milhas por galão")
> title(main="Consumo x Cilindros")
> cores<-c(2:(1+length(levels(cyl.f))))
> legend(locator(1), levels(cyl.f), fill=cores)
> # locator(1) faz com que o quadro de legendas
> # fique ancorado no ponto clicado pelo usuário.
> detach(mtcars)

O código acima gera o gráfico:

Gráficos de caixas boxplot()

Um gráficos de caixas (boxplot()) é uma ferramenta muita usada para analisar e comparar a variação de uma variável entre diferentes grupos de dados. Ela representa uma variável traçando as mesmas informações obtidas em sumário de cinco números:
o mínimo, o quartil inferior (percentil 25), a mediana (percentil 50), o quartil superior (percentil 75) e o máximo. Ele também pode ser usado para mostrar outliers (ou discrepantes, que são valores fora do intervalo de ± 1,5 * IQR, onde IQR é o intervalo interquartil definido como o quartil superior menos o menor quartil).
Aproveitamos, nas linhas de código abaixo, para rever algumas funções estatísticas básicas, como median(), quantil() e summary().

> u <- mtcars$mpg
> min(u)
[1] 10.4
> max(u)
[1] 33.9
> median(u)
[1] 19.2
> quantile(u)
    0%    25%    50%    75%   100%
10.400 15.425 19.200 22.800 33.900
> quantile(u, .25)
   25%
15.425
> quantile(u, .75)
 75%
22.8
> quantile(u, .25, .5,.75)
   25%
15.425
> quantile(u, c(.25, .5, .75))
   25%    50%    75%
15.425 19.200 22.800
> fivenum(u)
[1] 10.40 15.35 19.20 22.80 33.90
> summary(u)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  10.40   15.43   19.20   20.09   22.80   33.90
> boxplot(u, main="Box plot", ylab="Milhas/galão")

O gráfico é gerado:

Boxplots podem ser usados para comparar grupos de variáveis dentro de um dataframe ou lista. O formato para isto é: boxplot(formula, data=dataframe) onde formula é uma relação entre campos do dataframe. Um exemplo de fórmula é y ~ A, onde A é uma variável categórica. Neste caso um plot separado de y é traçado para cada valor de A. A fórmula y ~ A*B resultaria em plots separados de y para cada combinação dos níveis nas variáveis categóricas A e B.

> boxplot(mpg ~ cyl, data=mtcars,
          main="Dados de Consumo",
          xlab="Número de Cilindros",
          ylab="Milhas/galão", col=c("red", "blue", "green"), varwidth=TRUE)
> legend(locator(1), levels(cyl.f), fill=c("red", "blue", "green"))


O opção varwidth=TRUE faz com que as caixas tenham larguras proportionais à raiz quadrada do tamanho das amostras. O parâmetro horizontal=TRUE (não usado no gráfico acima) produz a reversão da orientação dos eixos.

Visualizações interativas

O R fornece muitas formas de exibir gráficos que podem ser modificados por interações com o usuário. Vamos exibir aqui apenas alguns exemplos.

Gráficos interativos com iplots

> library(iplots)
> attach(mtcars)
> cyl.f <- factor(cyl)
> gear.f <- factor(gear)
> ihist(mpg) # histograma
> ibar(carb) # gráfico de barras
> iplot(mpg, wt) # gráfico de pontos
> ibox(mtcars[c("qsec","disp","hp")]) # boxplots
> ipcp(mtcars[c("mpg","wt","hp")]) # coordenadas paralelas
> imosaic(cyl.f,gear.f) # gráfico mosaico

Gráfico gerado por ihist(mpg). O colorido foi feito após a geração do gráfico usando-se o item de menu View > Set color(rainbow).

Leaflet

O leaflet é uma biblioteca javascript voltada para a visualização interativa de mapas. O código abaixo carrega uma sessão com o leaflet. A função addTiles() insere uma camada com um mapa ao leaflet inicializado.

> library(dplyr)
> library(leaflet)
> leaflet() %>% addTiles()
> # O gráfico 1 é desenhado.
> # Inserindo a latitude e a longitude da
> # Praça da Liberdade, em Belo Horizonte, MG.
> # (que foi encontrada no Google Maps)
> pcaLiberdade <- data.frame(longitude = -43.938023, latitude= -19.931743)
> pcaLiberdade$titulo <- "Praça da Liberdade, BH!"
> # pcaLiberdade é um data frame com campos:
> pcaLiberdade
       longitude      latitude        titulo
1      -43.93802     -19.93174        Praça da Liberdade, BH!
> leaflet(pcaLiberdade)
          %>% addTiles()
          %>% addMarkers(lat = ~latitude, lng = ~longitude, popup = ~titulo)
> # O gráfico 2 é desenhado

Lembrando: as bibliotecas dplyr e leaflet devem ser instaladas. Observe as linhas de retorno na instalação para verificar sucesso ou erro. A biblioteca iplots, por exemplo, depende de Java.

Biblioteca shiny

> library(shiny)
> ui <- basicPage(
        plotOutput("plot1", click = "plot_click"),
        verbatimTextOutput("info")
  )
> server <- function(input, output) {
     output$plot1 <- renderPlot({
         plot(mtcars$wt, mtcars$mpg)
     })

     output$info <- renderText({
         paste0("x=", input$plot_click$x, "\ny=", input$plot_click$y)
     })
}
> shinyApp(ui, server)
Listening on http://127.0.0.1:6260

A url mostrada (no caso “http://127.0.0.1:6260”) deve ser visualizada no Browser. A cada clique de mouse as coordenadas do cursor são exibidas na caixa abaixo, como mostra a figura.

Biblioteca plotly

O código abaixo utiliza o data frame diamonds que contém informações sobre cor, clareza, medidas, carat, preço de diamantes. O ponto clicado abre um pop-up com dados sobre a posição no gráfico e a clareza do diamante.

> library(plotly)
> set.seed(100)
> d <- diamonds[sample(nrow(diamonds), 1000), ]
> plot_ly(d, x = carat, y = price, text = paste("Clareza: ", clarity),
          mode = "markers", color = carat, size = carat)

Sobre operadores em R

Em R o programador pode criar aperadores ou alterar o significado de operadores nativos usando o sinal “`” (backtick ou acento grave).
Por exemplo:
`+` <- function(a, b) paste(a, b, sep="")
"a"+"v" # retorna "av"

O sinal "+" se transformou no operador de concatenação.
Em geral se pode programar %X% (qualquer X) para qualquer funcionalidade.
`%@%` <- function(a, b) a^b
`%*%` <- function(x, y) x/y
2 %@% 3 # retorna 8
15 %*% 3 # retorna 5

As bibliotecas magrittr e dplyr definem o operador %>% com o seguinte significado:
`%>%` <- function(x, FUN) FUN(x)

Isso quer dizer que
x %>% hist
é o mesmo que
hist(x)
Por exemplo:
iris$Sepal.Length %>% hist # traça o histograma do vetor
mtcars$mpg %>% hist(col=rainbow(5)) # histograma de mtcars$mpg usando 5 cores.

Gráficos tridimensionais

Existem muitas bibliotecas em R para a geração de gráficos 3D. Entre eles estão: RGL, car, lattice e scatterplot3d (e muitos outras).

Gráfico de dispersão em 3D com scatterplot3d

scatterplot3d é uma biblioteca de uso simples, com formato básico:

scatterplot3d(x, y=NULL, z=NULL)

onde x, y, z são as coordenadas dos pontos a serem plotados. Os argumentos y e z são opcionais, dependendo da estrutura de x.

  • Se x é uma fórmula (como em zvar ~ xvar + yvar) então xvar, yvar e zvar são usados como valores para x, y e z.
  • Se x é uma matriz com pelo menos 3 colunas então as variáveis x, y e z são lidas diretamente da matriz.
> library("scatterplot3d")
> data(iris)
> flor <- iris[1:50,1:3]  # 50 linhas, 3 primeiras colunas
> names(flor) <- c("comprimentoSepala", "larguraSepala", "comprimentoPepala")
> # A forma mais simples de uso:
> scatterplot3d(flor)
> scatterplot3d(flor, pch = 20,
                main="Gráfico dispersão 3D",
                xlab = "Comprimento sétala (cm)",
                ylab = "Largura sétala (cm)",
                zlab = "Comprimento pétala (cm)", color="steelblue")
> # O gráfico 1 é gerado. (pch=20 usa símbolo bola cheia)

> scatterplot3d(flor, pch = 8, main="pch = 8 -> estrela",
                color="#E8582D", angle=55, grid=TRUE, box=FALSE)
> # O gráfico 2 é gerado. pch=8 usa símbolo estrela,
> # o gráfico é girado de 55º, com grid e sem a caixa envelope.

> z <- seq(-10, 10, 0.01)
> x <- cos(z)
> y <- sin(z)
> scatterplot3d(x, y, z, highlight.3d = T, col.axis = "blue",
              col.grid = "lightblue", main = "Hélice", pch = 20)


O gráfico ao lado é gerado.

Mais informações sobre scatterplot3d no site STHDA.

Mais informações sobre 3d scatterplots no site STHDA.

Outras visualizações em 3D com scatter3d

O formato básico para scatter3d, com alguns de seus parâmetros, é o seguinte:

scatter3d(formula, data, subset, radius, xlab, ylab, zlab, ...)
ou
scatter3d(x, y, z,
          xlab, ylab, zlab, revolutions=0,  speed=1,
          bg.col=c("white", "black"), axis.scales=TRUE,
          axis.col, surface=TRUE, surface.col=carPalette()[-1],
          fill=TRUE, point.col="yellow", text.col=axis.col,
          radius=1, groups=NULL, fill=TRUE, grid=TRUE,
          ellipsoid=FALSE, sphere.size=1, radius=1, threshold=0.01,
          parallel=TRUE, ellipsoid=FALSE, id=FALSE, ...)

onde

formula fórmula y ~ x + z. Para plotar os pontos por grupos use y ~ x + z | g onde g é o fator que distingue grupos.
data data frame usado para avaliação da fórmula.
x, y, z coordenadas dos pontos a serem plotados. Os argumentos y e z são opcionais, dependendo da estrutura de x.
subset expressão definindo subconjunto das observações a serem usadas.
xlab, ylab, zlab labels nos eixos.
radius raios das esferas representando pontos.
axis.scales Se TRUE, nomeia valores nas pontas dos eixos.
revolutions quantas revoluções a figura fará (animação).
bg.col cor de fundo.
axis.col cores para eixos.
surface.col vetor de cores para os planos.
point.col cores dos pontos.
text.col cores dos eixos.
grid.col colour of grid lines on the regression surface(s).
surface lógico, plotar superfícies.
fill lógico. Preencher superfícies com cores.
grid lógico. Plotar lines da grade nas superfícies de regressão.
grid.lines número de linhas nas grades.
speed velocidade de revolução.
fov controla ângulo da perspectiva.
groups Se NULL nenhum grupo é definido. Se um fator uma superfície diferente é desenhada para cada nível.
parallel lógico. Se as superfícies para grupos devem ser paralelas.
ellipsoid lógico. Concentração elipsóide para pontos.
labels texto para labels nos pontos. Default são os índices da observação.
col cores para labels em pontos.
outros argumentos.
> install.packages(c("rgl", "car"))
> library(rgl, car)
> data(iris)
> sep.l <- iris$Sepal.Length
> sep.w <- iris$Sepal.Width
> pet.l <- iris$Petal.Length
> scatter3d(x = sep.l, y = pet.l, z = sep.w)           # plota Gráfico-1
> scatter3d(x = sep.l, y = pet.l, z = sep.w,
            point.col = "steelblue", surface=FALSE)    # plota Gráfico-2

> scatter3d(x = sep.l, y = pet.l, z = sep.w,
            groups = iris$Species)                     # plota Gráfico-3

> scatter3d(x = sep.l, y = pet.l, z = sep.w, groups = iris$Species,
         grid = FALSE, fit = "smooth")                 # plota Gráfico-4
         
> scatter3d(x = sep.l, y = pet.l, z = sep.w, groups = iris$Species,
           surface=FALSE, ellipsoid = TRUE)            # plota Gráfico-5

> scatter3d(x = sep.l, y = pet.l, z = sep.w,
            groups = iris$Species, surface=FALSE,
            grid = FALSE, ellipsoid = TRUE)            # plota Gráfico-6

> # Para gravar estes gráficos nos formatos png e pdf podemos usar

> rgl.snapshot(filename = "plot.png")
> rgl.postscript("plot.pdf",fmt="pdf")

Todos os gráficos podem ser girados e redimensionados com o arraste de mouse.

Continuaremos o estudo sobre gráficos na próxima sessão, usando ggplot2.

 


Aprofundando as técnicas sobre Gráficos

Aquisição de Dados


Como a principal motivação para o uso do software R está na análise de dados e exibição gráfica de resultados é necessário ter formas eficientes para promover a leitura de dados para dentro de nosso ambiente.

Edição básica de tabelas

Já vimos que objetos como data frames podem ser editados por meio dos comandos edit(objeto) ou fix(objeto) que abrem uma janela para a alteração em forma de grade, permitindo inclusive a inserção de novos campos ou a alteração de nomes dos campos já existentes. Esta pode ser uma boa estratégia para se fazer pequenas alterações nas tabelas.

Outra função usada para leitura de dados do usuário ou à partir da leitura de um arquivo é scan().

scan(file = "", what = double(), n = -1, sep = "")

Valores listados para os parâmetros são default. Existem muitos outros parâmetros.
file = "" indica que a leitura será feita do teclado. Se file = "arquivo" este arquivo será lido.

what indica o tipo de dado a ser lido. what=character() significa que strings serão lidas.
n é o número de dados que serão inserido. n = -1 significa um número ilimitado. Neste caso a inserção (para n=-1) termina com dois <ENTER> seguidos.

sep = "" é o tipo de separador esperado. O default é um espaço em branco.

> x <- scan(n=3) # insere 3 valores do teclado
 1: 12 2: 23 3: 34
 Read 3 items
> x
[1] 12 23 34

Arquivos CSV

Para a leitura de bases de dados mais extensas outras formas estão disponíveis. Uma delas consiste em realizar a leitura de um arquivo csv (valores separados por vírgula, em inglês comma separated values). Este tipo de arquivo consiste em uma lista de linhas, cada uma delas contendo um número constante de valores, separados por vírgula (ou outro sinal). Estes arquivos podem ser lidos por gerenciadores de planilhas tais como o Excel ou o CALC, do Libre Office. Eles podem também ser gerados por estes aplicativos.

Vamos criar um arquivo para efeito de aprendizado dessa importação de dados. Em um editor de texto ASCII qualquer digitamos os valores seguintes:

id, Nome,  Sobrenome, Idade, Sexo
1,  Marta, Rocha,     24,    F
2,  Pedro, Souza,     12,    M
3,  José,  Marciano,  15,    M
4,  Joana, Santos,    21,    F
5,  Lucas, Pereira,   20,    M

O espaçamento entre os campos não é necessário. Digamos que gravamos este arquivo com o nome alunos.csv na pasta de trabalho em uso (ou em outra qualquer).

Estes dados podem ser lidos com o comando read.table:

read.table("nomeArquivo.csv", header=TRUE, sep=",", dec=".")

Aqui o parâmetro header=TRUE indica que a primeira linha do arquivo contém títulos para as colunas, sep="," indica que as valores estão separados por vírgula (poderiam estar separados por outro caracter, como “;”) e dec="." indica que o ponto é o separador numérico de decimais.

Se o arquivo não estiver na pasta de trabalho atual o nome completo ("caminho/nomearquivo.csv") deve ser fornecido.Para a conveniência do usuário, diversas funções do R são acompanhadas de outras com nomes diversos que realizam as mesmas operações mas usam parâmetros default diferentes. É o caso de read.table() e read.csv().

Consulte a ajuda para ver quais são estes parâmetros.

Por default ítens numéricos são lidos como variáveis numéricas e texto como fatores, embora este comportamento possa ser alterado se necessário. A primeira linha (o cabeçalho) alimenta os valores de nomes de colunas.

> dir()
[1] "alunos.csv"
> alunos <- read.table("alunos.csv", header=TRUE, sep=",", dec=".")
> alunos
  id    Nome  Sobrenome Idade  Sexo
1  1   Marta      Rocha    24     F
2  2   Pedro      Souza    12     M
3  3    José   Marciano    15     M
4  4   Joana     Santos    21     F
5  5   Lucas    Pereira    20     M
> class(alunos)
[1] "data.frame"
> fix(alunos)     # permite a edição em uma tabela de alunos
> names(alunos)   # lista propriedades names
[1] "id"        "Nome"      "Sobrenome" "Idade"     "Sexo"
> dim(alunos)     # dimensões da lista (5 linhas com 5 campos)
[1] 5 5
> alunos[1,]      # primeira linha da lista
  id    Nome Sobrenome Idade  Sexo
1  1   Marta     Rocha    24     F

> # O parâmetro row.names permite usar uma coluna para nomear as linhas:
> outroAlunos <- read.table("alunos.csv", header=TRUE, row.names="id", sep=",")
> outroAlunos
   N Nome   Sobrenome  Idade  Sexo
   1 Marta      Rocha     24     F
   2 Pedro      Souza     12     M
   3 José    Marciano     15     M
   4 Joana     Santos     21     F
   5 Lucas    Pereira     20     M

Em muitos casos precisamos executar a operação inversa: exportamos os dados em uma tabela para um arquivo csv para transferir dados e utilizá-los em outro aplicativo. Para isso usaremos, a seguir, a função write.csv()

> # Para recordar, criamos uma nova tabela, semelhante à alunos:
> alunos2 <- data.frame(
                   id =1:5,
                   Nome = c("Marta","Pedro","José","Joana","Lucas"),
                   Sobrenome = c("Rocha","Souza","Marciano","Santos","Pereira"),
                   Idade = c(24, 12, 15, 21, 20),
                   Sexo = c("F", "M","M", "F", "M")
                   )
> # Para gravar esta tabela em disco, como um arquivo csv:
> write.csv(alunos2, file="alunos2.csv")
> # Se o parâmetro file for omitido a saída é para o console
> write.csv(alunos2)
   "","id", "Nome", "Sobrenome", "Idade","Sexo"
   "1", 1, "Marta", "Rocha",      24,    "F"
   "2", 2, "Pedro", "Souza",      12,    "M"
   "3", 3,  "José", "Marciano",   15,    "M"
   "4", 4, "Joana", "Santos",     21,    "F"
   "5", 5, "Lucas", "Pereira",    20,    "M"

Para gravar o objeto alunos2 (uma lista) para uso futuro usamos save(). O objeto pode ser recuperado para o projeto através da função load().

> save(alunos2, file="alunos2.Rdata")
> dir()                   # Para verificar quais são os aqruivos na pasta
[1] "alunos.csv"       "alunos2.Rdata"
> rm(alunos2)             # alunos2 não existe mais na sessão
> load('alunos2.Rdata')   # recupera alunos2
> str(alunos2)
'data.frame':	5 obs. of  5 variables:
 $ id       : int  1 2 3 4 5
 $ Nome     : Factor w/ 5 levels "Joana","José",..: 4 5 2 1 3
 $ Sobrenome: Factor w/ 5 levels "Marciano","Pereira",..: 3 5 1 4 2
 $ Idade    : num  24 12 15 21 20
 $ Sexo     : Factor w/ 2 levels "F","M": 1 2 2 1 2

Alternativamente, podemos ler uma variável de texto para dentro de uma tabela.

> dados <- " idade sexo altura 13 F 1.25 15 F 1.60 10 M 1.40 "
> tabela <- read.table(header=TRUE, text=dados)
> str(tabela)
'data.frame':	3 obs. of  3 variables:
 $ idade : int  13 15 10
 $ sexo  : Factor w/ 2 levels "F","M": 1 1 2
 $ altura: num  1.25 1.6 1.4

Nos exemplos anteriores os tipos das colunas foram inferidos à partir dos dados lidos. Os campos de texto foram convertidos em fatores. O parâmetro colClasses permite que sejam informados previamente o tipo de cada coluna lida.

> alunosNotas <- "
     id| aluno |nota |bolsista
     1 | Marco | 5.2 |sim
     2 | Ana   | 7.5 |nao
     3 | Celia | 2.5 |sim"
> notas <- read.table(header=TRUE, text=alunosNotas,
         row.names="id", sep="|",
         colClasses=c("numeric", "character", "numeric", "character"))
> str(notas)
'data.frame':	3 obs. of  3 variables:
 $ aluno   : chr  " Marco " " Ana   " " Celia "
 $ nota    : num  5.2 7.5 2.5
 $ bolsista: chr  "sim" "nao" "sim"
> # A coluna bolsista foi importada como strings.
> # Para transformá-la em uma coluna de valores lógicos podemos fazer
> notas$bolsista <- notas$bolsista=="sim"
> str(notas)
'data.frame':	3 obs. of  3 variables:
 $ aluno   : chr  " Marco " " Ana   " " Celia "
 $ nota    : num  5.2 7.5 2.5
 $ bolsista: logi  TRUE FALSE TRUE

Se um arquivo *.csv se encontra na web, disponível através de protocolo http (como o arquivo na url usada abaixo, do site Sample Videos) podemos usar sua url:

> url <- "https://www.sample-videos.com/csv/Sample-Spreadsheet-10-rows.csv"
> dadosCsv <- read.csv(url)
> # A função carrega um data frame em dadosCsv

Se a página da web usa o protocolo https (mais seguro que o anterior) podemos usar o pacote RCurl. Como exemplo vamos baixar um arquivo diponibilizado pela cidade de Seattle, EUA (King County Open Data), contendo dados sobre animais de estimação perdidos:

> install.packages("RCurl")
> library (RCurl)
> url <- "https://data.kingcounty.gov/api/views/yaai-7frk/rows.csv?accessType=DOWNLOAD"
> dw <- getURL(url)
> dados <- read.csv (text = dw)
> class(dados)
[1] "data.frame"
> View(dados)

Com frequência dados baixados da internet contém falhas como, por exemplo, uma entrada em texto em uma coluna numérica. Estes dados precisam ser visualizados e tratados antes de uma análise de sua informação. Podemos vizualizar estes dados de forma gráfica usando a função View() que exibe em tabela um dataframe.

Importando planilhas

O pacote xlsx depende para seu funcionamento dos pacotes rJava e xlsxjars, bem como uma instalação funcional do Java em seu computador.

Para importar uma planilha do Excel ou Libre Office Spreadsheet podemos exportar estes dados para um arquivo *.csv e importá-lo usando as técnicas já descritas. Alternativamente é possível importar diretamente estas planilhas usando o pacote xlsx, que deve ser instalado antes do uso. Planilhas podem ser importadas com as funções read.xlsx e read.xlsx2 que têm a seguinte sintaxe:

read.xlsx(file, sheetIndex, sheetName=NULL, rowIndex=NULL,
          startRow=NULL, endRow=NULL, colIndex=NULL,
          as.data.frame=TRUE, header=TRUE, colClasses=NA,
          keepFormulas=FALSE, encoding="unknown", password=NULL, ...)

read.xlsx2(file, sheetIndex, sheetName=NULL, startRow=1,
          colIndex=NULL, endRow=NULL, as.data.frame=TRUE, header=TRUE,
          colClasses="character", password=NULL, ...)
Argumento Descrição
file arquivo (com caminho) a ser lido.
sheetIndex número da planilha dentro da pasta de trabalho.
sheetName nome da planilha.
rowIndex vetor numérico indicando linhas a serem extrarídas. Se NULL todas as linhas, exceto se startRow, endRow são especificados.
colIndex vetor numérico indicando colunas a serem extrarídas. Se NULL todas as colunas.
as.data.frame valor lógico. Se TRUE os dados serão montados em um data.frame. Se FALSE, uma lista, com um elemento por coluna.
header valor lógico indicando que a primeira linha contém os nomes das colunas.
colClasses (read.xlsx) vetor de strings com a classe de cada coluna.
keepFormulas valor lógico. Se TRUE as fórmulas do excel são mostradas como texto e não avaliadas.
encoding codificação para strings na planilha.
startRow numérico, especificando índice da 1ª linha. (Ativo se rowIndex=NULL).
endRow numérico, especificando índice da última linha. Se NULL, todas as linhas. (Ativo se rowIndex=NULL).
password senha para a pasta de trabalho.
outros argumentos para a data.frame. Ex. stringsAsFactors

A função read.xlsx procura adequar o tipo lido com o da planilha de acordo com cada coluna, preservando o tipo de dado lido.
read.xlsx2 é mais rápida, adequada para ser usada em planilhas muito grandes, acima de 100 mil células. Ambas podem ser usadas para ler arquivos *.xlsx ou *.xls.

> library(xlsx)
> xlFrame <- read.xlsx("planilha.xlsx",1, header=TRUE); xlFrame
     Data Local        Crédito   Débito
  1 43223 Casa Coral   1002.56   65.45
  2 43224 Fornecedor 1   23.34   NA
  3 43225 Cliente 2      24.34   33.00
  4 43226 Fornecedor 2   15.23   54.00
> # Valor não existente na planilha foi lido como 'NA'
> # A data foi lida como um campo numérico.

Gravando dados em uma planilha Excel

As funções write.xlsx e write.xlsx2 podem ser usadas para gravar dados de uma tabela em uma pasta de trabalho Excel. A segunda delas atinge uma performance melhor para planilhas longas, acima de 100 mil células.

Elas têm a sintaxe:

write.xlsx(x, file, sheetName="Sheet1",
          col.names=TRUE, row.names=TRUE,
          append=FALSE, showNA=TRUE, password=NULL)write.xlsx2(x, file, sheetName="Sheet1",
          col.names=TRUE, row.names=TRUE,
          append=FALSE, password=NULL, ...)

São seus argumentos:

Argumento Descrição
x data.frame a ser escrito como pasta de trabalho.
file arquivo (com caminho) a ser escrito.
sheetName nome da planilha.
col.names valor lógico. Se TRUE os nomes das colunas de x são escritos no arquivo.
row.names valor lógico. Se TRUE os nomes das linhas de x são escritos no arquivo.
append valor lógico. Se TRUE o arquivo é lido no disco e incrementado.
showNA valor lógico. Se FALSE valores NA são gravados em branco.
password senha para a pasta de trabalho.
outros argumentos passados para addDataFrame (no caso de read.xlsx2).

Para exibir o comportamento destas funções usamos o data.frame USArrests (que vem instalado no pacote básico de R).
Primeiro criamos uma planilha com nome USA-ARRESTS. Depois gravamos em outra planilha na mesma pasta o dataframe alunos que temos carregado em nossa sessão.

> write.xlsx(USArrests, file="pastaTrabalho.xlsx",
             sheetName="USA-ARRESTS", append=FALSE)
> write.xlsx(alunos, file="pastaTrabalho.xlsx",
             sheetName="alunos", append=TRUE)

O resultado é a gravação, em disco, de uma pasta de trabalho com duas planilhas com nomes USA-ARRESTS e alunos.

Existem outros pacotes destinados à manipulação de arquivos de planilhas. Entre eles citamos os pacotes XLConnect e openxlsx. Este último não depende de Java.

Manipulação de arquivos XML

XML (Extensible Markup Language) é um formato de transmissão de dados bastante usado na internet e computação em geral, usando apenas texto puro (ASCII). Ele contém tags de marcação que descrevem a estrutura dos dados.

Instale o pacote usando

install.packages("XML")

No R se pode ler e escrever em arquivos XML usando o pacote "XML". Para experimentar com a biblioteca usaremos um arquivo ASCII com o conteúdo abaixo, que gravaremos no disco com o nome livros.xml.

<biblioteca>
    <livro>
        <id>1</id>
        <titulo>A Dança do Universo</titulo>
        <autor>Marcelo Gleiser</autor>
    </livro>
    <livro>
        <id>2</id>
        <titulo>DNA: O Segredo da Vida</titulo>
        <autor>James D. Watson</autor>
    </livro>
    <livro>
        <id>3</id>
        <titulo>Uma breve história do tempo</titulo>
        <autor>Stephen W. Hawking</autor>
    </livro>
    <livro>
        <id>4</id>
        <titulo>Como a mente funciona</titulo>
        <autor>Steven Pinker</autor>
    </livro>
    <livro>
        <id>5</id>
        <titulo>A falsa medida do homem</titulo>
        <autor>Stephen Jay Gould</autor>
    </livro>
    <livro>
        <id>6</id>
        <titulo>O último teorema de Fermat</titulo>
        <autor>Simon Singh</autor>
    </livro>
</biblioteca>

O código seguinte carrega este arquivo para um objeto do R e o manipula.

> # Carrega os pacotes necessários
> library("XML", "methods")
> # Importa dados para um objeto de R
> livros <- xmlParse(file="livros.xml", encoding="UTF8" )
> class(livros) # "XMLInternalDocument" "XMLAbstractDocument"
> print(livros)
<biblioteca>
  <livro>
    <id>1</id>
    <titulo>A Dança do Universo</titulo>
    <autor>Marcelo Gleiser</autor>
  </livro>
  ... (outros 5 livros)
</biblioteca>
> xmlTop <- xmlRoot(livros) # node principal > class(xmlTop)
[1] "XMLInternalElementNode" "XMLInternalNode" "XMLAbstractNode"
> xmlName(xmlTop)             # nome do node principal
[1] "biblioteca"
> xmlSize(xmlTop)             # tamanho do node principal
[1] 6
> xmlName(xmlTop[[1]])        # nome do primeiro node
[1] "livro"
> xmlSize(xmlTop[[1]])        # tamanho do primeiro node
[1] 3
> print(xmlTop[[2]])          # exibe o segundo node            
<livro>
  <id>2</id>
  <titulo>DNA: O Segredo da Vida</titulo>
  <autor>James D. Watson</autor>
</livro> 
> xmlTop[[3]][[2]]            # segundo ítem do terceiro node
<titulo>Uma breve história do tempo</titulo>
> # Dados podem ser recuperados usando-se o nome dos nodes
> xmlTop[["livro"]][["autor"]]
<autor>Marcelo Gleiser</autor>
> # Convert um objeto xml para um dataframe
> dfLivros <- xmlToDataFrame(livros) > # Visualiza o dataframe
> View(df.livros, "Dataframe Livros")

A função View() exibe o dataframe como na figura abaixo:

Conexão com banco de dados

Naturalmente, grande parte dos dados a serem analisados estão armazendos em bancos de dados relacionais. R pode se relacionar com diversos dos sistemas de gerenciamento, entre eles os mais populares como SQL Server, Access, MySQL, Oracle, PostgreSQL e SQLite. Existem pacotes que permitem o acesso direto aos drivers nativos destes sistemas e outros que permitem esse acesso via ODBC ou JDBC. Desta forma o poder das consultas SQL amplia bastante o potencial de R na análise de dados.

Usando a interface ODBC

Uma forma comum de acessar bancos de dados de dentro do R é através do pacote RODBC que permite a conexão com qualquer DBMS (Data Base Management System) que admite conexão com o driver ODBC (como é o caso de todos os sistemas listados acima). Para isso é necessário instalar o driver ODBC apropriado para o banco de dados a ser usado, na plataforma específica. Em seguida se instala o pacote ODBC, usando install.packages("RODBC"). As funções básicas do pacote são:

Função Descrição
odbcConnect(dsn,uid=””,pwd=””) abre uma conexão com o driver ODBC
sqlFetch(channel,sqltable) lê uma tabela e a carrega em um data frame
sqlQuery(channel,query) submete uma consulta sql e retorna os resultados
sqlSave(channel,mydf,tablename= sqltable,append=FALSE) escreve ou atualiza um data frame para tabela da base
sqlDrop(channel,sqltable) remove uma tabela do bando de dados
close(channel) fecha a conexão

O pacote RODBC permite a comunicação bidirecional entre R e o banco de dados, que pode ser lido ou alterado. Suponha que um banco de dados possua duas tabelas possua, digamos debito e credito. É possível importá-las para dentro de uma sessão fazendo:

> library(RODBC)
> conn <- odbcConnect("dsn", uid="usr", pwd="senha")
> debito <- sqlFetch(conn, debito)
> credito <- sqlQuery(conn, "select * from credito")
> close(myconn)
Nem todos os pacotes estão disponíveis em todas as plataformas. Confira a documentação do CRAN.

A função sqlQuery() pode ser usada para a aplicação de qualquer instrução SQL, permite uma seleção detalhada de variáveis, a criação de novos campos, alteração e inserção de dados no banco.

Usando o pacote DBI

O pacote DBI (DataBase Interface) fornece outra forma de acesso à DBMS com suporte à diversos drivers. Entre eles estão os pacotes RJDBC (acesso ao driver JDBC), RMySQL, ROracle, RPostgreSQL e RSQLite.

Para o exemplo que se segue usamos o banco de dados chinook.db que pode ser baixado no site do SQLite Tutorial.

> library(DBI)
> library(RSQLite)
> banco <- "chinook.db"
> driver <- dbDriver("SQLite")
> db <- dbConnect(driver, banco)
> # Para exibir qual banco está associado a este objeto:
> db
<SQLiteConnection>
  Path: /home/guilherme/Projetos/R/Aprendendo/chinook.db
  Extensions: TRUE
> # Lista de tabelas em chinook.db
> dbListTables(db)
[1] "albums"         "artists"   "customers"    "employees"       "genres"
[6] "invoice_items"  "invoices"  "media_types"  "playlist_track"  "playlists"
[11]"sqlite_sequence" "sqlite_stat1" "sqlite_stat4"   "tracks"
> # Lista de campos na tabela 'albums'
> dbListFields(db, "albums")
[1] "AlbumId"  "Title"    "ArtistId"
> sql <- "SELECT AlbumId, Title FROM albums"
> db <- dbConnect(driver, banco)
> rs <- dbSendQuery(db, sql)
> rs
<SQLiteResult>
  SQL  SELECT AlbumId, Title FROM albums
  ROWS Fetched: 0 [incomplete]
       Changed: 0
> dbColumnInfo(rs)
     name      type
1 AlbumId   integer
2   Title character
> dbGetStatement(rs)
[1] "SELECT AlbumId, Title FROM albums"
> albuns <- fetch(rs, n = 5) > albuns
  AlbumId                                 Title
1       1 For Those About To Rock We Salute You
2       2                     Balls to the Wall
3       3                     Restless and Wild
4       4                     Let There Be Rock
5       5                              Big Ones

> # Lista de campos na tabela 'artists'
> dbListFields(db, "artists")
[1] "ArtistId" "Name"
> sql <- "SELECT a.Title, b.Name FROM albums
               a INNER JOIN artists b ON a.ArtistId = b.ArtistId
               WHERE b.Name LIKE \"Iron%\""
> # O resultado de uma consulta fica armazenado em rs:
> rs <- dbSendQuery(db, sql)
> # rs tem as seguintes colunas
> dbColumnInfo(rs)
   name      type
1 Title character
2  Name character
> # rs foi gerada pela consulta (sql query)
> dbGetStatement(rs)
[1] "SELECT a.Title, b.Name FROM albums a
     INNER JOIN artists b
     ON a.ArtistId = b.ArtistId
     WHERE b.Name LIKE \"Iron%\""
> # O número de colunas alteradas
> dbGetRowsAffected(rs)
[1] 0
> # Para ler 5 linhas deste resultado
> linhas <- fetch(rs, n = 5) > linha
                       Title        Name
1 A Matter of Life and Death Iron Maiden
2            A Real Dead One Iron Maiden
3            A Real Live One Iron Maiden
4            Brave New World Iron Maiden
5             Dance Of Death Iron Maiden
> dbGetRowCount(rs)   # quantas colunas
[1] 5
> # Liberando o 'resultset' e a conexão
> dbClearResult(rs); dbDisconnect(db)

No código abaixo está mostrado como abrir e manipular um banco de dados PostgreSQL que deve estar instalado na máquina local. Ele lê uma tabela no banco de nome Notas que tem uma tabela categorias com campos id, idPai, categoria. Em seguida ele usa o data frame alunos que já está carregado na sessão de R com campos id, Nome, Sobrenome, Idade, Sexo e grava esta tabela no banco de dados, com nome “alunos”. Finalmente uma consulta de atualização é feita usando a conexão aberta.

> library(DBI)
> conn <- dbConnect(odbc::odbc(),
                driver = "PostgreSQL Unicode",
                database = "Notas",
                uid = "nomeUsuario",
                pwd = "senhaUsuario",
                host = "localhost",
                port = 5432)
> # Este db possui uma tabela 'categorias'
> categorias <- dbReadTable(conn, "categorias")
> categorias
  id idPai             categoria
1 10     0               Ciência
2 14     0            Literatura
3 15    14     Ficção Científica
4 11    10                Física
...
> # Uma consulta SQL
> sql <- "SELECT id, categoria FROM categorias ORDER BY categoria"
> categ <- dbSendQuery(conn, sql)
> primeiros_3 <- dbFetch(categ, n = 3)  # primeiros 3 registros
> primeiros_3
  id             categoria
1 13              Biologia
2 10               Ciência
3 17            Phylos.net
> restante <- dbFetch(catg) # lê os demais registros
> # Gravando o data frame alunos no banco de dados, com nome "alunos"
> # data contém booleano com sucesso da operação
> data <- dbWriteTable(conn, "alunos", alunos)
> # Uma query de atualização na tabela Categorias
> sql <- "UPDATE Categorias SET categoria ='Nova categoria' WHERE id=17"
> dbSendQuery(conn, sql) # altera categoria com id = 17

O código a seguir cria um banco de dados virtual (que existe apenas na memória). Ele pode ser útil para teste, para operações provisórias ou mesmo para a criação completa de um banco até que ele esteja pronto para ser gravado em disco.

> library(DBI)
> # Cria um banco de dados SQL virtual
> conn <- dbConnect(RSQLite::SQLite(), dbname=":memory:") > dbListTables(conn)
character(0)
> dbWriteTable(conn, "mtcars", mtcars)
> dbListTables(conn)
[1] "mtcars"
> dbListFields(conn, "mtcars")
 [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"
 [7] "qsec" "vs"   "am"   "gear" "carb"
> dbReadTable(conn, "mtcars")
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1  21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
2  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
3  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
> # Todas as linhas podem ser recuperadas de uma vez:
> rs <- dbSendQuery(conn, "SELECT * FROM mtcars WHERE cyl = 4")
> dbFetch(rs)
    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
2  24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
3  22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
4  32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
5  30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
(... continua, 32 linhas ...)

> dbClearResult(res)
> # Lendo o banco por partes
> rs <- dbSendQuery(conn, "SELECT * FROM mtcars WHERE cyl = 4")
> while(!dbHasCompleted(res)) {
           parte <- dbFetch(res, n = 5)
           print(nrow(parte))
           }
  [1] 5 [1] 5 [1] 1
> # Fecha o resultset e a conexão
> dbClearResult(rs); dbDisconnect(conn)

Um resumo das funções disponíveis no pacote DBI estão listadas abaixo. Para uma lista completa, com descrição detalhada de cada função e seus parâmetros consulte a R Database Interface (versão 0.5-1) na página R Documentation.

Função Descrição
dbDriver carrega e descarrega drivers
dbColumnInfo informa sobre tipos em resultados
dbExecute executa uma query e fecha o result set
dbCallProc chama uma “stored procedure”
dbClearResult limpa uma result set
dbDisconnect fecha uma conexão
dbConnect cria conexão com uma DBMS
dbDataType determina o tipo (SQL) de um objeto
dbGetStatement verifica a query associada a um result set
dbGetRowCount número de linhas recuperadas até o momento
dbGetQuery envia query, retorna resultado e limpa o result set
dbHasCompleted status de realização de uma query
dbGetRowsAffected número de linhas afetada
dbExistsTable booleano, retorna existência da tabela
dbIsValid booleano, o DBMS é um objeto válido?
dbListConnections lista conexões abertas
dbListResults lista resultados pendentes
dbListFields lista nomes de campos de uma tabela
dbSendQuery executa consulta sobre uma conexão
dbRemoveTable remove uma tabela do banco de dados
dbFetch recupera registros de consulta já realizada
transactions inicia/commit/rollback transações SQL
sqlAppendTable insere linhas em uma tabela
sqlCreateTable cria uma tabela

Obtendo dados na Internet

Uma grande quantidade de dados se encontra hoje disponível na internet. Por isso é importante aprender a acessá-los e selecionar aqueles que nos interessam. Esta busca e seleção de dados é denominada web scraping.

Embora existam dados em formato estruturado, como tabelas e até mesmos bancos de dados, muita informação na Web está sob formato não estruturado, como ocorre em muitas páginas de texto HTML. É necessário, portanto, converter essa informação em formatos mais úteis.

Em muitos casos copiar e colar conteúdo de uma página em arquivo local pode ser suficiente. Em arquivos pequenos eles podem ser organizados manualmente e os dados postos em forma de uma tabela, por exemplo com os campos separados por vírgula. Em outros casos uma página pode ser analisada por meio de reconhecimento de padrões, usando expressões regulares ou outro processo.

Muitos sites importantes, como Facebook, Twitter e LinkedIn, fornecem APIs públicas ou privadas, que facilitam a leitura de seus dados. Além disso as páginas da web são alimentadas para os browers dentro de estruturas DOM (Document Object Model), o que facilita a garimpagem de dados.

Se os dados já estão estruturados, sob a forma de um arquivo csv (por exemplo) então eles podem ser importados para a sessão de R com a função read.table ou read.csv. Em seguida eles podem ser manipulados de acordo com a conveniência da análise desejada.

> # Há um arquivo csv de teste no endereço abaixo:
> url <- "https://www.sample-videos.com/csv/Sample-Spreadsheet-100-rows.csv"
> dados <- read.table(url, sep=",")
> # A lista está disponível. Os dois primeiros elementos do campo V3 são:
> head(dados$V3, n=2)
[1] Muhammed MacIntyre Barry French
> # Arquivo sem títulos nas colunas: os campos ficam nomeados V1 até v10
> # Independente da formatação uma página pode ser baixada com:
> download.file("https://endereco_url.html", "caminhoOndeSalvar/arquivo.html")

Usando o pacote rvest

O pacote rvest foi escrito por Hadley Wickham e é inspirado em bibliotecas como a Beautiful Soup, do Python. Um bom tutorial pode ser encontrado na página do Data Camp sobre o rvest.
 
XPath (XML Path Language) é uma especificação de pesquisa em nodes de um documento XML. Existem aplicativos e plugins nos principais browsers para facilitar esta localização. Para o Chrome uma boa ferramenta é o SelectorGadget.
No Firefox podemos usar o inspector, um ítem de menu em web developer, que pode ser aberto com CTRL-SHIFT-C.

Para ler uma página na web e analisar seu conteúdo podemos usar o pacote rvest. Nele encontramos a função read_html() que retorna um documento XML que contém toda a informação sobre a página.

Primeiro procuramos uma página na web contendo as informações que desejamos extrair. Para efeito de nosso aprendizado usamos uma páginas simples onde se exibe uma tabela dos estados brasileiros com suas populações e PIBs. Usaremos a página Lista de Estados Brasileiros com população e PIB em Excel. Ignoramos, claro, a possibilidade disponível nessa página de baixar diretamente a tabela em formato Excel. Abrindo esta página no browser abrimos (no Firefox) o web developer, inspector. Na janela de inspecção procuramos a tabela desejada. Ao movimentar o cursor do mouse sobre o elemento html a tabela fica sombreada. Clique na tabela e pressione o botão direito selecionando copy xpath. xpath é um localizador de posição dentro da página, que fica armazenado na área de transfrência. No nosso caso temos xpath = "/html/body/div[3]/div[1]/table", indicando que queremos extrair a tabela única dentro do primeiro div, dentro do terceiro div no corpo do documento html. Em seguida baixamos o conteúdo sob forma xml e depois selecionamos o node desejado usando html_nodes(xpath).

> library(rvest)
> url <- "http://www.servicos.blog.br/listas/lista-de-estados-brasileiros-com-populacao-e-pib-em-excel/"
> xPath <- "/html/body/div[3]/div[1]/table"
> # O seguinte comando armazena o conteúdo do elemento em xPath como 
> populacao <- url %>%
    read_html() %>%
    html_nodes(xpath=xPath) %>%
    html_table()
> # Da forma como foi obtida, populacao é uma lista com um elemento
> # Este elemento único é a tabela desejada
> populacao <- populacao[[1]]
> View(populacao)

O útimo comando abre a visualização da tabela:

Veremos em breve um pouco mais sobre o funcionamento do pipe %>%. Por enquanto basta saber que ele faz parte do pacote magrittr e facilita a notação para operações encadeadas, como a composição de funções:

  • x %>% f é equivalente a f(x)
  • x %>% f(y) é equivalente a f(x, y)
  • x %>% f %>% g %>% h é equivalente a h(g(f(x)))

Portanto a linha contendo pipes é idêntica aos seguintes comandos:

> pop <- read_html(url)
> pop2 <- html_nodes(pop, xpath=xPath)
> pop3 <- html_table(pop2) # pop3 é o mesmo que populacao, acima.


Operadores e Funções Internas