2017
Este curso procura apresentar um conjunto de técnicas utéis a cada fase da análise de dados, desde a importação dos dados à comunicação de resultados:
Neste primeiro dia introduzimos a linguagem R, abordamos a recolha de informação, como organizá-la para posterior análise e realização de visualizações exploratórias.
No 2º, 3º dia e ainda na manhã do 4º dia focar-se-ão os aspectos da transformação dos dados, da modelação e inferência.
Na tarde do 4º dia falar-se-á das ferramentas do RStudio para criar apresentações que possam comunicar os resultados de forma eficaz, atraente e até interativa.
Dia 1:
# inclui vários pacotes úteis
install.packages("tidyverse")
library(tidyverse)
Dias 2, 3 e manhã do dia 4:
Tarde do dia 4:
O R é uma linguagem de programação usada originalmente para computação estatística.
Foi desenvolvida na Universidade de Auckland nos anos 90 como um projecto open source.
Desde a sua primeira versão estável em 2000 tem ganho popularidade e é hoje uma das linguagens de eleição nas comunidades estatística e de machine learning.
Possui, no fim de 2016, cerca de 8000 bibliotecas especializadas. A maioria encontra-se nos repositórios CRAN, BioConductor e ainda no GitHub.
Usaremos o RStudio, uma aplicação gráfica que permite interagir de uma forma muito produtiva com a consola R.
Este curso assume que todos sabem programar. Faremos de seguida uma apresentação da sintaxe R e dos conceitos/funções úteis no resto do curso.
O RStudio está disponível em www.rstudio.com para vários sistemas operativos.
x <- 1
y <- "Uma string"
z <- TRUE
i <- 1L
class(x); class(y); class(z); class(i)
[1] "numeric"
[1] "character"
[1] "logical"
[1] "integer"
v1 <- 1:5
v1
[1] 1 2 3 4 5
class(v1)
[1] "integer"
v2 <- c(4:5, -2, 7, -1)
v2
[1] 4 5 -2 7 -1
letters
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q"
[18] "r" "s" "t" "u" "v" "w" "x" "y" "z"
letters[1]
[1] "a"
letters[length(letters)]
[1] "z"
letters[1:5]
[1] "a" "b" "c" "d" "e"
letters[1:5][-1]
[1] "b" "c" "d" "e"
Gerar vectores
1:9
[1] 1 2 3 4 5 6 7 8 9
seq(1,9)
[1] 1 2 3 4 5 6 7 8 9
seq(1,9,3)
[1] 1 4 7
seq(1,2,len=11)
[1] 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0
rep(NA, 4)
[1] NA NA NA NA
rep(1:2, 3)
[1] 1 2 1 2 1 2
v2
[1] 4 5 -2 7 -1
v2 > 0
[1] TRUE TRUE FALSE TRUE FALSE
v2[v2>0]
[1] 4 5 7
v2[!v2>0]
[1] -2 -1
which(v2>0)
[1] 1 2 4
l <- list(a=1:5, b="string", c=c(TRUE,FALSE))
class(l)
[1] "list"
l[1] # uma sublista com um vector de inteiros
$a
[1] 1 2 3 4 5
l["a"] # idem
$a
[1] 1 2 3 4 5
l[[1]] # o vector de inteiros
[1] 1 2 3 4 5
l$a # idem
[1] 1 2 3 4 5
l[[1]][2] = 0 # alterar o 2º valor do 1º elemento da lista
l$a
[1] 1 0 3 4 5
m <- matrix(15:24, nrow=2, ncol=5, byrow = TRUE)
m
[,1] [,2] [,3] [,4] [,5]
[1,] 15 16 17 18 19
[2,] 20 21 22 23 24
class(m)
[1] "matrix"
m[1,]
[1] 15 16 17 18 19
m[,1]
[1] 15 20
m[1,2]
[1] 16
m[,c(1,3,5)]
[,1] [,2] [,3]
[1,] 15 17 19
[2,] 20 22 24
colnames(m) <- paste0("col",1:5)
rownames(m) <- paste0("row",1:2)
m
col1 col2 col3 col4 col5
row1 15 16 17 18 19
row2 20 21 22 23 24
m["row1","col2"]
[1] 16
m1 <- matrix(-5:-1, nrow=1)
rbind(m, m1) # juntar matrizes por linhas
col1 col2 col3 col4 col5
row1 15 16 17 18 19
row2 20 21 22 23 24
-5 -4 -3 -2 -1
cbind(m, m1[1:2]) # juntar matrizes por colunas
col1 col2 col3 col4 col5
row1 15 16 17 18 19 -5
row2 20 21 22 23 24 -4
c(m) # deconstruir matriz
[1] 15 20 16 21 17 22 18 23 19 24
df <- data.frame(num=c(27,15,9,64),
char=c('a','b','c','a'),
bool=c(TRUE,FALSE,FALSE,NA))
df
num char bool
1 27 a TRUE
2 15 b FALSE
3 9 c FALSE
4 64 a NA
class(df)
[1] "data.frame"
nrow(df)
[1] 4
ncol(df)
[1] 3
head(iris,4) # os datasets estão organizados em data frames
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
df[[1]] # a 1ª lista do data frame
[1] 27 15 9 64
df$num # idem
[1] 27 15 9 64
df[,"num"] # idem
[1] 27 15 9 64
df[c(1,3)] # um data frame parte do original
num bool
1 27 TRUE
2 15 FALSE
3 9 FALSE
4 64 NA
df[c("num","bool")] # idem
num bool
1 27 TRUE
2 15 FALSE
3 9 FALSE
4 64 NA
df[c(1,3),c("num","bool")]
num bool
1 27 TRUE
3 9 FALSE
df$new.col <- ifelse(!df$bool, df$num*5, -10) # adicionar coluna
df
num char bool new.col
1 27 a TRUE -10
2 15 b FALSE 75
3 9 c FALSE 45
4 64 a NA NA
df[,4] <- NULL # apagar coluna
df
num char bool
1 27 a TRUE
2 15 b FALSE
3 9 c FALSE
4 64 a NA
head(iris,4)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
iris[iris$Sepal.Length == 4.9,]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
2 4.9 3.0 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
35 4.9 3.1 1.5 0.2 setosa
38 4.9 3.6 1.4 0.1 setosa
58 4.9 2.4 3.3 1.0 versicolor
107 4.9 2.5 4.5 1.7 virginica
iris[iris$Sepal.Length == 4.9,]$Petal.Length
[1] 1.4 1.5 1.5 1.4 3.3 4.5
names(iris)
[1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"
[5] "Species"
table(iris$Species)
setosa versicolor virginica
50 50 50
summary(iris[,1:4])
Sepal.Length Sepal.Width Petal.Length Petal.Width
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
Median :5.800 Median :3.000 Median :4.350 Median :1.300
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
sapply(iris, class) # operação aplicada a cada coluna
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"numeric" "numeric" "numeric" "numeric" "factor"
class(iris$Species)
[1] "factor"
levels(iris$Species)
[1] "setosa" "versicolor" "virginica"
plot(iris$Sepal.Length ~ iris$Species, xlab="Species", ylab="Sepal Length")
Pode transformar-se strings em factores e definir factores ordenados:
df$char <- factor(df$char, labels=c("a","b","c"))
df$ord <- ordered(cut(df$num, c(0,10,20,100),
labels=c("Baixo","Medio","Alto")))
df
num char bool ord
1 27 a TRUE Alto
2 15 b FALSE Medio
3 9 c FALSE Baixo
4 64 a NA Alto
sapply(df[,c(2,4)], class)
$char
[1] "factor"
$ord
[1] "ordered" "factor"
Na regressão o R cria dummy vars para os factores.
Para remover factores que já não estão a ser utilizados usar a função droplevels()
.
dobro <- function(x) {
2*x
}
ePar <- function(n) {
n %% 2 == 0
}
dobro(1.5)
[1] 3
dobro(-4:6)
[1] -8 -6 -4 -2 0 2 4 6 8 10 12
ePar(11)
[1] FALSE
ePar(1:6)
[1] FALSE TRUE FALSE TRUE FALSE TRUE
As funções do R podem devolver funções que se 'lembram' dos valores das variáveis quando foram criadas (designa-se environment a estes conjuntos de valores):
increment.factory <- function(amount) {
function(x) x + amount
}
succ <- increment.factory(1)
jump10 <- increment.factory(10)
succ(5)
[1] 6
jump10(5)
[1] 15
Qual a probabilidade de saírem exactamente três caras em dez lançamentos de uma moeda equilibrada? Três resoluções:
1) Usando a fórmula da binomial: \( {n \choose k} p^k (1-p)^{n-k} = {10 \choose 3} \left(\frac{1}{2}\right)^3 \left(\frac{1}{2}\right)^7 \)
choose(10,3) * (1/2)^3 * (1/2)^7
[1] 0.1171875
2) Usando a função massa de probabilidade da Binomial:
dbinom(3, prob=0.5, size=10)
[1] 0.1171875
3) Simulando:
set.seed(222)
n.sims <- 1e5
sum(replicate(n.sims, rbinom(1, prob=.5, size=10)==3)) / n.sims
[1] 0.11728
Um passeio aleatório baseado numa moeda equilibrada:
set.seed(222)
n.sims <- 1e3
walk <- cumsum(ifelse(rbinom(n.sims, prob=.5, size=1)==0, -1, 1))
plot(walk, type='l', main='Passeio Aleatorio')
abline(h=0, lty="dashed", col="darkgrey")
É possível manipular listas de valores sem usar ciclos explícitos:
n <- 1000
df <- data.frame(col1=runif(n), col2=rnorm(n), col3=rexp(n))
head(df,3)
col1 col2 col3
1 0.03562685 0.18488589 0.2916406
2 0.90972808 0.36186461 2.7866127
3 0.83601691 -0.02469412 1.2222748
Calcular a média com ciclos:
means <- rep(NA, ncol(df))
for(col in 1:ncol(df)) {
means[col] = mean(df[,col])
}
means
[1] 0.49398452 -0.01413925 1.02370468
E sem ciclos:
means <- apply(df, 2, mean)
means
col1 col2 col3
0.49398452 -0.01413925 1.02370468
Podemos facilmente mudar a estatística:
apply(df, 2, sd)
col1 col2 col3
0.2935531 0.9912709 0.9781108
A base R tem várias funções deste tipo mas apresentamos aqui o pacote purr
que as disponibiliza numa forma mais organizada.
As funções map_XXX
aplicam uma função a cada um dos elementos dados devolvendo valores de um dado tipo:
library(purrr)
data <- 1:10L
dobro <- function(x) 2L*x
map_int(data, dobro)
[1] 2 4 6 8 10 12 14 16 18 20
map_dbl(df, mean) # Os exemplos anteriores
col1 col2 col3
0.49398452 -0.01413925 1.02370468
map_dbl(df, sd)
col1 col2 col3
0.2935531 0.9912709 0.9781108
keep(1:10, ~ .x%%2 == 0) # guardar os pares
[1] 2 4 6 8 10
É possível compor funções a partir de outras existentes:
isEven <- compose(function(x) x==0, function(x) x%%2)
keep(data, isEven)
[1] 2 4 6 8 10
Também se pode aplicar uma função sobre todos os elementos:
reduce(c(1,3,5), `+`)
[1] 9
factorial <- function(n) reduce(1:n, `*`, .init=1)
factorial(4)
[1] 24
head(mtcars)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
groups <- split(mtcars, mtcars$cyl) # dividir os carros por cilindrada
head(groups$`4`)
mpg cyl disp hp drat wt qsec vs am gear carb
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
apply_lm <- function(df) { fit <- lm(mpg ~ wt, data=df); fit$cyl <- df[1,]$cyl; fit }
models <- map(groups, apply_lm)
draw_lm <- function(model) {
with(mtcars, plot (wt, mpg, col = "gray", pch=cyl));
with(mtcars[mtcars$cyl == model$cyl,], points(wt, mpg, col = "blue", pch=cyl));
abline(model, col="red")
}
map(models, draw_lm)
$`4`
NULL
$`6`
NULL
$`8`
NULL