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:

  1. Ele acorda, levanta da cama e tira o pijama;
  2. Toma banho;
  3. Se veste e se arruma;
  4. Toma o café da manhã e depois escova os dentes;
  5. Olha para o céu e decide se será necessário levar o guarda-chuva;
  6. 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.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 ife 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.

Uso Frequente
Uso Menos Frequente
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

Documentação:

É 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"


João Matheus S. K. T. Hneda

Lineu Alberto C. de Freitas

Aula 2 - 04/05/2019 - PET-Estatística (UFPR)