Minicurso R - 2019
1 Estruturas de Controle
Uma das grandes vantagens em se programar é a possibilidade de estabelecer uma condição operacional ou a repetição de uma operação n vezes. Isso somente é possível por meio das estruturas de controle em um algoritmo. Estrutura de Controle refere-se à ordem em que instruções, expressões e funções são executadas em um programa de computador.
Mas antes de falar sobre os tipos de Estruturas de Controle, é necessário entender quais são os Operadores Relacionais e Operadores Lógicos.
1.1 Operadores Relacionais
Formato | Descrição |
---|---|
> | Maior que |
>= | Maior ou igual que |
< | Menor que |
<= | Menor ou igual que |
== | Igual |
!= | Diferente |
Esses operadores nos permitem fazer comparações com os nossos dados. Se a equação for verdadeira, então a operação relacional irá retornar TRUE
, do contrário irá retornar FALSE
.
Como exemplo, logo abaixo vetores foram criados e verificações foram feitas:
a <- 42.95
b <- 75.87
a>b
## [1] FALSE
a>=b
## [1] FALSE
a<b
## [1] TRUE
a<=b
## [1] TRUE
a==b
## [1] FALSE
a!=b
## [1] TRUE
c <- c(70,80,60,90)
d <- c(70,90,65,85)
c>d
## [1] FALSE FALSE FALSE TRUE
c>=d
## [1] TRUE FALSE FALSE TRUE
c==d
## [1] TRUE FALSE FALSE FALSE
e <- "hello world"
f <- "Hello World"
e==f
## [1] FALSE
1.2 Operadores Lógicos
Operador | Descrição | Representação Matemática |
---|---|---|
& | E | Intersecção |
| | OU | União |
! | NÃO | Negação |
Esses operadores nos permitem fazer mais de uma comparação por vez.
c <- c(70,80,60,90)
d <- c(70,90,65,85)
c>72 & c<85
## [1] FALSE TRUE FALSE FALSE
d==70 | d==90
## [1] TRUE TRUE FALSE FALSE
c!=d
## [1] FALSE TRUE TRUE TRUE
1.3 Tipos de Estruturas de Controle
Como exemplo de motivação, imagine a rotina de um profissional que trabalha com programação:
- Ele acorda, levanta da cama e tira o pijama;
- Toma banho;
- Se veste e se arruma;
- Toma o café da manhã e depois escova os dentes;
- Olha para o céu e decide se será necessário levar o guarda-chuva;
- Vai de ônibus até o trabalho.
Perceba o fluxo que o profissional segue para realizar essas tarefas. Ele repete essa lista de tarefas 5 vezes por semana e verifica se deve levar o guarda-chuva para o trabalho. Em programação, damos o nome a essa lista de tarefas de Estruturas de Controle. Existem dois tipos principais: Estruturas de Seleção (Decidir se deve levar o guarda-chuva para o trabalho) e Estruturas de Repetição (Seguir a rotina 5 vezes por dia).
1.3.1 Estruturas de Seleção
Uma estrutura de seleção tem como objetivo executar uma tarefa apenas se alguma condição for satisfeita.
1.3.1.1 if
A forma mais utilizada para se realizar uma tarefa condicional é utilizando a estrutura de seleção if()
.
A sintaxe para essa estrutura segue o formato:
if(<condição>){
<comandos que satisfazem a condição>
}
Como exemplo, considere o código abaixo que verifica se um número é maior que 100.
x = 150
if (x > 100){
print("Este número é maior que 100")
}
## [1] "Este número é maior que 100"
Como o número é maior do que 100, a verificação feita pela estrutura if() é verdadeira, e portanto, a frase ‘Este número é maior que 100’ é printada no console. Agora, abaixo trocamos o valor de x
e analisamos o resultado.
x = 80
if (x > 100){
print("Este número é maior que 100")
}
Como o número não é maior que 100, a verificação feita pela estrutura if() é falsa, e portanto, a frase ‘Este número é maior que 100’ não é printada no console.
1.3.1.2 else
Muitas vezes quando a condição não é verdadeira, desejamos que outra tarefa seja executada.
A sintaxe para essa estrutura segue o formato:
if(<condição>) {
<comandos que satisfazem a condição>
} else {
<comandos que não satisfazem a condição>
}
Seguindo nosso exemplo, desejamos que se o número não for maior que 100, queremos que apareça no console a frase ‘Este número NÃO é maior que 100’.
x = 25
if (x > 100) {
"Este número é maior que 100"
} else {
"Este número NÃO é maior que 100"
}
## [1] "Este número NÃO é maior que 100"
Outra possibilidade seria printar uma frase para quando o número fosse igual a 100. Para isso, uma nova estrutura if() pode ser utilizada.
if(<condição>) {
<comandos que satisfazem a condição>
} else if(<condição de igualdade>) {
<comando que satisfaz a condição>
} else {
<comandos que não satisfazem a condição>
}
Seguindo nosso exemplo, desejamos que se o número for igual a 100, queremos que apareça no console a frase ‘Este número é IGUAL a 100’.
x = 100
if (x > 100) {
"Este número é maior que 100"
} else if(x == 100) {
"Este número é IGUAL a 100"
} else {
"Este número NÃO é maior que 100"
}
## [1] "Este número é IGUAL a 100"
1.3.1.3 ifelse()
A função ifelse()
tem a função de reduzir o tamanho da estrutura if
. Essa função cria uma estrutura if
em uma única linha de código.
A sintaxe para essa estrutura segue o formato:
ifelse(<condição>,
<comandos que satisfazem a condição>,
<comandos que não satisfazem a condição>)
ifelse(c(70,80,60,90)>=75,'É maior que 75','É menor ou igual que 75')
## [1] "É menor ou igual que 75" "É maior que 75"
## [3] "É menor ou igual que 75" "É maior que 75"
1.3.2 Estruturas de Repetição
Essa estrutura de controle tem como objetivo repetir uma ou mais tarefas diversas vezes por meio de laços (loops).
1.3.2.1 for
Uma das formas de se repetir um bloco de programação é utilizando o loop for.
A sintaxe padrão para esse loop segue o formato:
for(<índice> in <valores>){
<comandos>
}
Como exemplo, considere o código abaixo que tem como objetivo printar os valores de 1 a 10 separadamente.
for(i in 1:10){
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
Uma outra possibilidade seria printar separadamente os primeiros 10 valores de um vetor.
x <- 100:200
for(i in 1:10){
print(x[i])
}
## [1] 100
## [1] 101
## [1] 102
## [1] 103
## [1] 104
## [1] 105
## [1] 106
## [1] 107
## [1] 108
## [1] 109
Uma outra possibilidade seria printar 5 números aleatórios separadamente.
for (i in runif(5)) {print(i)}
## [1] 0.2436871
## [1] 0.7552192
## [1] 0.9930037
## [1] 0.1897305
## [1] 0.7023165
1.3.2.2 while
Uma outra forma de se repetir um bloco de programação é utilizando o loop while.
A sintaxe padrão para esse loop segue o formato:
while(<condição satisfeita>){
<conjunto de tarefas>
}
O while
executa comandos enquanto uma determinada condição permanece verdadeira.
x = 1
while(x < 15){
x = x + 2
print(x)
}
## [1] 3
## [1] 5
## [1] 7
## [1] 9
## [1] 11
## [1] 13
## [1] 15
1.3.2.3 next
Muitas vezes desejamos que uma iteração seja ignorada e que a próxima seja executada se uma condição for atendida. Isso pode ser feito por meio do bloco de programação next
.
Como exemplo, o código abaixo exibe alguns números do intervalo de 1 a 10. Perceba como os valores de i
não aparecem no console quando i
é igual a 5 ou 7.
for(i in 1:10){
if(i == 5 | i == 7)
next
print (i)}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 6
## [1] 8
## [1] 9
## [1] 10
1.3.2.4 break
Uma outra possibilidade é parar o loop se uma condição for atendida.
Como exemplo, o código abaixo exibe alguns números do mesmo intervalo e para de rodar o loop no momento em que i
é igual a 13.
for(i in 1:10){
if(i == 8)
break
print (i)}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
1.3.2.5 repeat
Esse é o loop mais simples da linguagem R. Essa estrutura de repetição é uma outra opção para repetir uma tarefa. Essa estrutura deve vir acompanhada das estruturas if
e break
. Do contrário, o loop irá repetir infinitamente.
A sintaxe padrão para esse loop segue o formato:
repeat{
<conjunto de tarefas>
if(<condição>){
break
}
}
x = 0
repeat {
x = x + 2
if (x > 30){
break
}
print(x)
}
## [1] 2
## [1] 4
## [1] 6
## [1] 8
## [1] 10
## [1] 12
## [1] 14
## [1] 16
## [1] 18
## [1] 20
## [1] 22
## [1] 24
## [1] 26
## [1] 28
## [1] 30
2 Família apply
A família apply é um conjunto de funções e uma alternativa popular aos loops em R. Essas funções são úteis para melhorar o fluxo de trabalho de análises estatísticas e reduzir a perda de tempo com grandes estruturas de controle.
Função | Descrição | Função | Descrição |
---|---|---|---|
apply | Apply functions over array margins | mapply | mapply is a multivariate version of sapply. |
tapply | Apply a function over a ragged array | eapply | Apply a Function Over Values in an Environment |
laaply | Apply a function over a list or vector | rapply | Recursively Apply a Function to a List |
sapply | Simplify the result from lapply | vapply | vapply is similar to sapply, but has a pre-specified type of return value. |
2.1 apply
Quando você tem uma matriz ou um dataframe e você quer aplicar a mesma função para todas as linhas ou para todas as colunas, apply()
pode ser uma solução.
A sintaxe padrão para essa função segue o formato:
apply(X, MARGIN, FUN, ...), onde
X = É uma matriz ou dataframe
MARGIN = 1 para cálculos em linhas; 2 para cálculos em colunas
FUN = Função a ser aplicada
Como exemplo, o dataset rock
nativo do R foi utilizado e a média foi calculada para todas as linhas e para todas as colunas do objeto.
str(rock)
## 'data.frame': 48 obs. of 4 variables:
## $ area : int 4990 7002 7558 7352 7943 7979 9333 8209 8393 6425 ...
## $ peri : num 2792 3893 3931 3869 3949 ...
## $ shape: num 0.0903 0.1486 0.1833 0.1171 0.1224 ...
## $ perm : num 6.3 6.3 6.3 6.3 17.1 17.1 17.1 17.1 119 119 ...
apply(X=rock,MARGIN=1,FUN=mean)
## [1] 1947.0726 2725.2622 2873.7858 2806.9343 2977.1906 3001.6043 3424.0099
## [8] 3142.7535 3048.5609 2410.7031 3490.8002 3182.3470 3692.5421 3117.1679
## [15] 3374.7356 3146.4059 3907.3661 3897.3707 4200.2550 3601.2887 2852.2135
## [22] 4092.8578 4262.9725 2973.4005 2269.4177 1578.1324 2518.1080 1892.8883
## [29] 2312.6088 1756.2503 2348.3629 2561.3340 2089.2394 1814.6787 2423.2285
## [36] 2576.3340 1236.4692 511.1902 1203.4059 1753.0535 1822.4679 656.2180
## [43] 2012.7885 3093.4776 1307.3277 707.2677 1887.5156 2945.9451
apply(X=rock,MARGIN=2,FUN=mean)
## area peri shape perm
## 7187.7291667 2682.2119375 0.2181104 415.4500000
2.2 tapply
Quando você deseja dividir a base de dados em subconjuntos e aplicar funções à esses subconjuntos, a função tapply()
pode ser uma solução.
A sintaxe padrão para essa função segue o formato:
tapply(X, INDEX, FUN, ...), onde
X = É a coluna de uma matriz ou dataframe que será utilizada para fazer os cálculos
INDEX = É a coluna de uma matriz ou dataframe que será utilizada para dividir a base
FUN = Função a ser aplicada
Como exemplo, o dataset iris
nativo do R foi utilizado e a média da coluna Sepal.Length foi calculada para cada espécie diferente da coluna Species.
str(iris)
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
tapply(X=iris$Sepal.Length,INDEX=iris$Species,FUN=mean)
## setosa versicolor virginica
## 5.006 5.936 6.588
2.3 laaply
Quando você tem uma lista e você quer aplicar a mesma função para cada elemento da lista, lapply()
pode ser uma solução que sempre retornará uma outra lista.
A sintaxe padrão para essa função segue o formato:
lapply(X, FUN, ...), onde
X = É uma lista
FUN = Função a ser aplicada em cada elemento da lista
Como exemplo, duas amostras de alturas de mulheres foram extraídas do dataset women
nativo do R e armazenados em uma lista.
str(women)
## 'data.frame': 15 obs. of 2 variables:
## $ height: num 58 59 60 61 62 63 64 65 66 67 ...
## $ weight: num 115 117 120 123 126 129 132 135 139 142 ...
w1 <- women[sample(x=1:15,size=7,replace=FALSE),1]
w2 <- women[sample(x=1:15,size=7,replace=FALSE),1]
(lista <- list(w1,w2))
## [[1]]
## [1] 72 66 69 70 59 67 63
##
## [[2]]
## [1] 67 62 60 70 72 59 63
lapply(X=lista,FUN=mean)
## [[1]]
## [1] 66.57143
##
## [[2]]
## [1] 64.71429
2.4 sapply
lapply()
é uma função muito boa, entretanto muitas vezes é melhor retornar o resultado em uma forma mais simples que uma lista. Dentro desse contexto, a função sapply()
simplificará o resultado se possível.
A sintaxe padrão para essa função segue o formato:
sapply(X, FUN, ..., simplify=TRUE), onde
X = É uma lista
FUN = Função a ser aplicada em cada elemento da lista
sapply(X=lista,FUN=mean,simplify=TRUE)
## [1] 66.57143 64.71429
3 tidyverse
O tidyverse oferece uma reimplementação e extensão das funcionalidades básicas do R para manipulação e visualização de dados. É composto por 8 pacotes principais e diversos outros secundários que foram planejados e construídos para trabalhar de forma conjunta. A gramática, argumentos e filosofia dos pacotes é mais intuitiva que o R base. Estas vantagens tornam o código mais simples de desenvolver e ler.
library(tidyverse)
## -- Attaching packages --------------------------------------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.1 v purrr 0.3.2
## v tibble 2.1.1 v dplyr 0.8.0.1
## v tidyr 0.8.3 v stringr 1.4.0
## v readr 1.3.1 v forcats 0.4.0
## -- Conflicts ------------------------------------------------------------------------------------------ tidyverse_conflicts() --
## x tidyr::extract() masks magrittr::extract()
## x dplyr::filter() masks stats::filter()
## x dplyr::group_rows() masks kableExtra::group_rows()
## x dplyr::lag() masks stats::lag()
## x purrr::set_names() masks magrittr::set_names()
tidyverse_packages()
## [1] "broom" "cli" "crayon" "dplyr" "dbplyr"
## [6] "forcats" "ggplot2" "haven" "hms" "httr"
## [11] "jsonlite" "lubridate" "magrittr" "modelr" "purrr"
## [16] "readr" "readxl\n(>=" "reprex" "rlang" "rstudioapi"
## [21] "rvest" "stringr" "tibble" "tidyr" "xml2"
## [26] "tidyverse"
4 Pipe
O pacote magrittr é o pacote no qual o operador Pipe (%>%) está inserido. É um dos pacotes componentes do Tidyverse. O operador %>% torna a leitura e escrita de códigos mais lógica e compreensível quando comparada ao R base, pois o código é estruturado da esquerda para a direita, evitando funções aglutinadas (uma dentro da outra) e minimizando a necessidade de variáveis locais.
A melhor forma de entender o funcionamento é com exemplos:
4.1 Exemplo 1
# Sem pipe
mean(1:10)
## [1] 5.5
# Com pipe
1:10 %>% mean()
## [1] 5.5
4.2 Exemplo 2
# Sem pipe
str(head((as_tibble(iris))))
## Classes 'tbl_df', 'tbl' and 'data.frame': 6 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1
# Com pipe
iris %>% as_tibble() %>% head() %>% str()
## Classes 'tbl_df', 'tbl' and 'data.frame': 6 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1
4.3 Exemplo 3
# disponível em https://www.r-statistics.com/2014/08/simpler-r-coding-with-pipes-the-present-and-future-of-the-magrittr-package/
#install.packages('babynames')
library(babynames)
library(magrittr)
babynames %>%
filter(name %>% substr(1, 3) %>% equals("Ste")) %>%
group_by(year, sex) %>%
summarize(total = sum(n)) %>%
qplot(year, total, color = sex, data = ., geom = "line") %>%
add(ggtitle('Names starting with "Ste"')) %>%
print
5 Tibble
É uma reimplementação do Data Frame tradicional do R com melhorias.
As mudanças dão maior consistência à manipulação de dados. Possui método print() mais informativo e conciso.
Possui funções para criação de tibbles e operações básicas como adicionar linhas e colunas.
5.1 Criação de Tibble
# Criação de tibble por especificação de colunas
tibble(col1 = c(1,2,3),
col2 = c(4,5,6),
col3 = col1^2,
col4 = col1 + col2)
## # A tibble: 3 x 4
## col1 col2 col3 col4
## <dbl> <dbl> <dbl> <dbl>
## 1 1 4 1 5
## 2 2 5 4 7
## 3 3 6 9 9
# Criação de tibbles por especificação de linhas
tribble(~col1, ~col2, ~col3, ~col4,
"row1", 1, 2, 3,
"row2", 4, 5, 6,
"row3", 7, 8, 9)
## # A tibble: 3 x 4
## col1 col2 col3 col4
## <chr> <dbl> <dbl> <dbl>
## 1 row1 1 2 3
## 2 row2 4 5 6
## 3 row3 7 8 9
5.2 Conversões para tibble
5.2.1 Conversão de vetor nomeado para tibble
vetor_nomeado <- c('nome1' = 10 ,
'nome2' = 20,
'nome3' = 30,
'nome4' = 40)
vetor_nomeado %>% enframe()
## # A tibble: 4 x 2
## name value
## <chr> <dbl>
## 1 nome1 10
## 2 nome2 20
## 3 nome3 30
## 4 nome4 40
5.2.2 Conversão de matriz para tibble
matriz <- matrix(round(rnorm(25),2), nrow = 5, ncol = 5)
matriz %>% as_tibble()
## Warning: `as_tibble.matrix()` requires a matrix with column names or a `.name_repair` argument. Using compatibility `.name_repair`.
## This warning is displayed once per session.
## # A tibble: 5 x 5
## V1 V2 V3 V4 V5
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 -1.43 1.1 -2.7 0.21 0.35
## 2 -1.2 -1.78 0.96 1.88 -0.35
## 3 -0.81 1.7 -1.08 -0.77 1.59
## 4 0.64 0.99 1.03 2.05 -1.46
## 5 -0.63 -1.05 -0.73 -0.53 -0.96
5.2.3 Conversão de data frame para tibble
df <- data.frame(col1 = c(1,2,3),
col2 = c(4,5,6),
col3 = c('a', 'b', 'c'))
df %>% as_tibble()
## # A tibble: 3 x 3
## col1 col2 col3
## <dbl> <dbl> <fct>
## 1 1 4 a
## 2 2 5 b
## 3 3 6 c
5.3 Seleção de linhas e colunas em tibbles
df2 <- mtcars %>% head() %>% as_tibble()
# Resulta em vetor.
df2$mpg
## [1] 21.0 21.0 22.8 21.4 18.7 18.1
df2[["mpg"]]
## [1] 21.0 21.0 22.8 21.4 18.7 18.1
df2[[1]]
## [1] 21.0 21.0 22.8 21.4 18.7 18.1
# Resulta em `tibble`!
df2[, 1]
## # A tibble: 6 x 1
## mpg
## <dbl>
## 1 21
## 2 21
## 3 22.8
## 4 21.4
## 5 18.7
## 6 18.1
df2[, "mpg"]
## # A tibble: 6 x 1
## mpg
## <dbl>
## 1 21
## 2 21
## 3 22.8
## 4 21.4
## 5 18.7
## 6 18.1
df2[1, ]
## # A tibble: 1 x 11
## mpg cyl disp hp drat wt qsec vs am gear carb
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 21 6 160 110 3.9 2.62 16.5 0 1 4 4
5.4 Adicionar linhas e colunas em tibbles
tb <- c('nome1' = 10, 'nome2' = 20,
'nome3' = 30, 'nome4' = 40) %>% enframe()
tb %>% add_row(name = 'nome5', value = 50)
## # A tibble: 5 x 2
## name value
## <chr> <dbl>
## 1 nome1 10
## 2 nome2 20
## 3 nome3 30
## 4 nome4 40
## 5 nome5 50
tb %>% add_column(class = letters[1:nrow(tb)])
## # A tibble: 4 x 3
## name value class
## <chr> <dbl> <chr>
## 1 nome1 10 a
## 2 nome2 20 b
## 3 nome3 30 c
## 4 nome4 40 d
6 Readr
Serve não só para leitura (importação) mas também para escrita (exportação).
# Funções de importação disponíveis
ls("package:readr") %>% str_subset("^read_")
## [1] "read_csv" "read_csv_chunked"
## [3] "read_csv2" "read_csv2_chunked"
## [5] "read_delim" "read_delim_chunked"
## [7] "read_file" "read_file_raw"
## [9] "read_fwf" "read_lines"
## [11] "read_lines_chunked" "read_lines_raw"
## [13] "read_lines_raw_chunked" "read_log"
## [15] "read_rds" "read_table"
## [17] "read_table2" "read_tsv"
## [19] "read_tsv_chunked"
# Funções de exportação disponíveis
ls("package:readr") %>% str_subset("^write_")
## [1] "write_csv" "write_csv2" "write_delim"
## [4] "write_excel_csv" "write_excel_csv2" "write_file"
## [7] "write_lines" "write_rds" "write_tsv"