5  Estruturas de Repetição: Loopings

As estruturas de repetição desempenham um papel crucial na programação, pois permitem que tarefas repetitivas sejam realizadas de maneira mais eficaz ou iterem sobre conjuntos de dados extensos. Além disso, as estruturas de repetição são essenciais para processar grandes volumes de dados, realizar cálculos complexos e criar algoritmos mais poderosos.

Em R existem diversas estruturas de repetição, veremos aqui: while, for e a família apply.

5.0.1 Estrutura while

O loop while é uma das estruturas de repetição mais simples. Ele é usado quando você precisa repetir um bloco de código enquanto uma condição for verdadeira. E seu critério de parada é atualizado, i.e., você não sabe de imediato quantas vezes vai precisar repetir o procedimento.

A estrutura básica do while é a seguinte:

while (condicao) {
  # Código a ser repetido enquanto a condição for verdadeira
}

Imprima na tela i, enquanto i for menor do que 6.

i <- 1 # sempre definimos o critério de parada fora do loop

while (i < 6) {
  print(i)
  i <- i + 1 # Sempre alteramos o critério 
  # de parada, senão caímos em um loop infinito
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

5.0.1.1 Break

Mesmo quando uma condição é verdadeira, podemos ter interesse em parar uma repetição, mesmo que a condição seja TRUE. Útil quando não queremos cair em um loop infinito.

Por exemplo, podemos parar o nosso loop caso nosso i == 4.

i <- 1
while (i < 6) {
  print(i)
  i <- i + 1
  if (i == 4) {
    break
  }
}
[1] 1
[1] 2
[1] 3

5.0.1.2 Next

Outras vezes, podemos pular uma iteração sem encerrar o loop:

i <- 0
while (i < 6) {
  i <- i + 1
  if (i == 3) {
    next
  }
  print(i)
}
[1] 1
[1] 2
[1] 4
[1] 5
[1] 6

5.0.2 Exercício

Suponha o lançamento de um dado não viesado, com seis faces. Quantas vezes devo lançar o dado para obter a face 5?

set.seed(1234)

dado <- seq(1:6)
n_lancamento = 0
sorteio = 0

while (sorteio != 5) {
  sorteio =  sample(dado, 1)
  n_lancamento = n_lancamento + 1
  
  cat(paste0("\n\nLançamento: ", n_lancamento, "\nValor Sorteado: ", sorteio))
}


Lançamento: 1
Valor Sorteado: 4

Lançamento: 2
Valor Sorteado: 2

Lançamento: 3
Valor Sorteado: 6

Lançamento: 4
Valor Sorteado: 5
n_lancamento
[1] 4
n_lancamento = 0
while (sorteio != 7) {
  sorteio =  sample(dado, 1)
  n_lancamento = n_lancamento + 1
  
  cat(paste0("\n\nLançamento: ", n_lancamento, "\nValor Sorteado: ", sorteio))
  
  if(n_lancamento == 100){
    break
  }
}


Lançamento: 1
Valor Sorteado: 4

Lançamento: 2
Valor Sorteado: 1

Lançamento: 3
Valor Sorteado: 5

Lançamento: 4
Valor Sorteado: 6

Lançamento: 5
Valor Sorteado: 4

Lançamento: 6
Valor Sorteado: 2

Lançamento: 7
Valor Sorteado: 6

Lançamento: 8
Valor Sorteado: 2

Lançamento: 9
Valor Sorteado: 6

Lançamento: 10
Valor Sorteado: 6

Lançamento: 11
Valor Sorteado: 4

Lançamento: 12
Valor Sorteado: 6

Lançamento: 13
Valor Sorteado: 6

Lançamento: 14
Valor Sorteado: 6

Lançamento: 15
Valor Sorteado: 4

Lançamento: 16
Valor Sorteado: 4

Lançamento: 17
Valor Sorteado: 5

Lançamento: 18
Valor Sorteado: 4

Lançamento: 19
Valor Sorteado: 3

Lançamento: 20
Valor Sorteado: 4

Lançamento: 21
Valor Sorteado: 5

Lançamento: 22
Valor Sorteado: 2

Lançamento: 23
Valor Sorteado: 5

Lançamento: 24
Valor Sorteado: 2

Lançamento: 25
Valor Sorteado: 6

Lançamento: 26
Valor Sorteado: 3

Lançamento: 27
Valor Sorteado: 4

Lançamento: 28
Valor Sorteado: 4

Lançamento: 29
Valor Sorteado: 3

Lançamento: 30
Valor Sorteado: 1

Lançamento: 31
Valor Sorteado: 3

Lançamento: 32
Valor Sorteado: 6

Lançamento: 33
Valor Sorteado: 4

Lançamento: 34
Valor Sorteado: 2

Lançamento: 35
Valor Sorteado: 3

Lançamento: 36
Valor Sorteado: 2

Lançamento: 37
Valor Sorteado: 5

Lançamento: 38
Valor Sorteado: 6

Lançamento: 39
Valor Sorteado: 1

Lançamento: 40
Valor Sorteado: 6

Lançamento: 41
Valor Sorteado: 3

Lançamento: 42
Valor Sorteado: 6

Lançamento: 43
Valor Sorteado: 1

Lançamento: 44
Valor Sorteado: 5

Lançamento: 45
Valor Sorteado: 1

Lançamento: 46
Valor Sorteado: 1

Lançamento: 47
Valor Sorteado: 2

Lançamento: 48
Valor Sorteado: 1

Lançamento: 49
Valor Sorteado: 3

Lançamento: 50
Valor Sorteado: 2

Lançamento: 51
Valor Sorteado: 6

Lançamento: 52
Valor Sorteado: 3

Lançamento: 53
Valor Sorteado: 1

Lançamento: 54
Valor Sorteado: 3

Lançamento: 55
Valor Sorteado: 6

Lançamento: 56
Valor Sorteado: 1

Lançamento: 57
Valor Sorteado: 2

Lançamento: 58
Valor Sorteado: 6

Lançamento: 59
Valor Sorteado: 5

Lançamento: 60
Valor Sorteado: 1

Lançamento: 61
Valor Sorteado: 3

Lançamento: 62
Valor Sorteado: 3

Lançamento: 63
Valor Sorteado: 2

Lançamento: 64
Valor Sorteado: 5

Lançamento: 65
Valor Sorteado: 2

Lançamento: 66
Valor Sorteado: 6

Lançamento: 67
Valor Sorteado: 4

Lançamento: 68
Valor Sorteado: 4

Lançamento: 69
Valor Sorteado: 1

Lançamento: 70
Valor Sorteado: 5

Lançamento: 71
Valor Sorteado: 3

Lançamento: 72
Valor Sorteado: 6

Lançamento: 73
Valor Sorteado: 5

Lançamento: 74
Valor Sorteado: 3

Lançamento: 75
Valor Sorteado: 4

Lançamento: 76
Valor Sorteado: 4

Lançamento: 77
Valor Sorteado: 1

Lançamento: 78
Valor Sorteado: 4

Lançamento: 79
Valor Sorteado: 4

Lançamento: 80
Valor Sorteado: 3

Lançamento: 81
Valor Sorteado: 1

Lançamento: 82
Valor Sorteado: 6

Lançamento: 83
Valor Sorteado: 6

Lançamento: 84
Valor Sorteado: 4

Lançamento: 85
Valor Sorteado: 1

Lançamento: 86
Valor Sorteado: 6

Lançamento: 87
Valor Sorteado: 6

Lançamento: 88
Valor Sorteado: 2

Lançamento: 89
Valor Sorteado: 6

Lançamento: 90
Valor Sorteado: 5

Lançamento: 91
Valor Sorteado: 5

Lançamento: 92
Valor Sorteado: 5

Lançamento: 93
Valor Sorteado: 1

Lançamento: 94
Valor Sorteado: 2

Lançamento: 95
Valor Sorteado: 6

Lançamento: 96
Valor Sorteado: 6

Lançamento: 97
Valor Sorteado: 2

Lançamento: 98
Valor Sorteado: 2

Lançamento: 99
Valor Sorteado: 5

Lançamento: 100
Valor Sorteado: 3
n_lancamento
[1] 100

Note que em ambos exemplos os valores estão sendo apenas calculados e não estamos guardando em nenhuma variável. Para resolver esse problema podemos simplesmnet guardar o valor sorteado em um vetor.

valor_sorteado = numeric()
n_lancamento = 0
while (sorteio != 7) {
  n_lancamento = n_lancamento + 1
  valor_sorteado[n_lancamento] =  sample(dado, 1)
  
  
  cat(paste0("\n\nLançamento: ", n_lancamento, "\nValor Sorteado: ", sorteio))
  
  if(n_lancamento == 100){
    break
  }
}


Lançamento: 1
Valor Sorteado: 3

Lançamento: 2
Valor Sorteado: 3

Lançamento: 3
Valor Sorteado: 3

Lançamento: 4
Valor Sorteado: 3

Lançamento: 5
Valor Sorteado: 3

Lançamento: 6
Valor Sorteado: 3

Lançamento: 7
Valor Sorteado: 3

Lançamento: 8
Valor Sorteado: 3

Lançamento: 9
Valor Sorteado: 3

Lançamento: 10
Valor Sorteado: 3

Lançamento: 11
Valor Sorteado: 3

Lançamento: 12
Valor Sorteado: 3

Lançamento: 13
Valor Sorteado: 3

Lançamento: 14
Valor Sorteado: 3

Lançamento: 15
Valor Sorteado: 3

Lançamento: 16
Valor Sorteado: 3

Lançamento: 17
Valor Sorteado: 3

Lançamento: 18
Valor Sorteado: 3

Lançamento: 19
Valor Sorteado: 3

Lançamento: 20
Valor Sorteado: 3

Lançamento: 21
Valor Sorteado: 3

Lançamento: 22
Valor Sorteado: 3

Lançamento: 23
Valor Sorteado: 3

Lançamento: 24
Valor Sorteado: 3

Lançamento: 25
Valor Sorteado: 3

Lançamento: 26
Valor Sorteado: 3

Lançamento: 27
Valor Sorteado: 3

Lançamento: 28
Valor Sorteado: 3

Lançamento: 29
Valor Sorteado: 3

Lançamento: 30
Valor Sorteado: 3

Lançamento: 31
Valor Sorteado: 3

Lançamento: 32
Valor Sorteado: 3

Lançamento: 33
Valor Sorteado: 3

Lançamento: 34
Valor Sorteado: 3

Lançamento: 35
Valor Sorteado: 3

Lançamento: 36
Valor Sorteado: 3

Lançamento: 37
Valor Sorteado: 3

Lançamento: 38
Valor Sorteado: 3

Lançamento: 39
Valor Sorteado: 3

Lançamento: 40
Valor Sorteado: 3

Lançamento: 41
Valor Sorteado: 3

Lançamento: 42
Valor Sorteado: 3

Lançamento: 43
Valor Sorteado: 3

Lançamento: 44
Valor Sorteado: 3

Lançamento: 45
Valor Sorteado: 3

Lançamento: 46
Valor Sorteado: 3

Lançamento: 47
Valor Sorteado: 3

Lançamento: 48
Valor Sorteado: 3

Lançamento: 49
Valor Sorteado: 3

Lançamento: 50
Valor Sorteado: 3

Lançamento: 51
Valor Sorteado: 3

Lançamento: 52
Valor Sorteado: 3

Lançamento: 53
Valor Sorteado: 3

Lançamento: 54
Valor Sorteado: 3

Lançamento: 55
Valor Sorteado: 3

Lançamento: 56
Valor Sorteado: 3

Lançamento: 57
Valor Sorteado: 3

Lançamento: 58
Valor Sorteado: 3

Lançamento: 59
Valor Sorteado: 3

Lançamento: 60
Valor Sorteado: 3

Lançamento: 61
Valor Sorteado: 3

Lançamento: 62
Valor Sorteado: 3

Lançamento: 63
Valor Sorteado: 3

Lançamento: 64
Valor Sorteado: 3

Lançamento: 65
Valor Sorteado: 3

Lançamento: 66
Valor Sorteado: 3

Lançamento: 67
Valor Sorteado: 3

Lançamento: 68
Valor Sorteado: 3

Lançamento: 69
Valor Sorteado: 3

Lançamento: 70
Valor Sorteado: 3

Lançamento: 71
Valor Sorteado: 3

Lançamento: 72
Valor Sorteado: 3

Lançamento: 73
Valor Sorteado: 3

Lançamento: 74
Valor Sorteado: 3

Lançamento: 75
Valor Sorteado: 3

Lançamento: 76
Valor Sorteado: 3

Lançamento: 77
Valor Sorteado: 3

Lançamento: 78
Valor Sorteado: 3

Lançamento: 79
Valor Sorteado: 3

Lançamento: 80
Valor Sorteado: 3

Lançamento: 81
Valor Sorteado: 3

Lançamento: 82
Valor Sorteado: 3

Lançamento: 83
Valor Sorteado: 3

Lançamento: 84
Valor Sorteado: 3

Lançamento: 85
Valor Sorteado: 3

Lançamento: 86
Valor Sorteado: 3

Lançamento: 87
Valor Sorteado: 3

Lançamento: 88
Valor Sorteado: 3

Lançamento: 89
Valor Sorteado: 3

Lançamento: 90
Valor Sorteado: 3

Lançamento: 91
Valor Sorteado: 3

Lançamento: 92
Valor Sorteado: 3

Lançamento: 93
Valor Sorteado: 3

Lançamento: 94
Valor Sorteado: 3

Lançamento: 95
Valor Sorteado: 3

Lançamento: 96
Valor Sorteado: 3

Lançamento: 97
Valor Sorteado: 3

Lançamento: 98
Valor Sorteado: 3

Lançamento: 99
Valor Sorteado: 3

Lançamento: 100
Valor Sorteado: 3
valor_sorteado
  [1] 4 4 3 2 4 5 2 6 6 1 3 3 1 6 6 5 1 5 1 2 6 6 3 6 6 3 5 6 6 4 3 3 1 3 4 3 3
 [38] 2 6 4 2 5 5 1 3 3 3 5 1 4 4 3 2 1 1 3 2 5 6 2 6 1 1 3 1 1 4 4 5 1 3 6 5 6
 [75] 2 6 3 2 3 6 4 3 5 1 3 2 3 3 2 2 3 1 6 2 5 4 4 2 4 1

5.0.3 Estrutura for

O loop for é usado para iterar sobre uma sequência de valores, como um vetor ou uma lista. A estrutura básica é a seguinte:

for (variavel in sequencia) {
  # Código a ser executado para cada valor da sequência
}

Suponha que queremos calcular o quadrado dos valores de 1:10. Para isso, podemos utilizar um for.

for(i in 1:10) {
  x1 <- i^2
  print(x1)
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81
[1] 100

Além disso, podemos ter for aninhados.

Suponha agora um experimento que consiste no lançamento de dois dados não viesados de seis faces, e definimos uma variável aleatória como o quadrado da soma das faces superiores dos dois dados. Queremos guardar o valor dos dois dados, a soma das faces superiores e o quadrado da soma.

Loading required package: dplyr

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
Loading required package: magrittr
dado = c(1:6)
soma_dois_dados = function(dado1, dado2){
  soma = dado1 + dado2
  
}
quadrado_soma = function(soma){
  soma2 = soma^2
  return(soma2)
}

resultado = list()
k = 0 
for(i in dado){
  for(j in dado){
    k = k + 1
    soma = soma_dois_dados(dado[i], dado[j])
    somaqd = quadrado_soma(soma)
    
    resultado[[k]] = data.frame(dado1 = dado[i], 
                               dado2 = dado[j], 
                               soma = soma, 
                               soma2 = somaqd)
  }
}

resultado
[[1]]
  dado1 dado2 soma soma2
1     1     1    2     4

[[2]]
  dado1 dado2 soma soma2
1     1     2    3     9

[[3]]
  dado1 dado2 soma soma2
1     1     3    4    16

[[4]]
  dado1 dado2 soma soma2
1     1     4    5    25

[[5]]
  dado1 dado2 soma soma2
1     1     5    6    36

[[6]]
  dado1 dado2 soma soma2
1     1     6    7    49

[[7]]
  dado1 dado2 soma soma2
1     2     1    3     9

[[8]]
  dado1 dado2 soma soma2
1     2     2    4    16

[[9]]
  dado1 dado2 soma soma2
1     2     3    5    25

[[10]]
  dado1 dado2 soma soma2
1     2     4    6    36

[[11]]
  dado1 dado2 soma soma2
1     2     5    7    49

[[12]]
  dado1 dado2 soma soma2
1     2     6    8    64

[[13]]
  dado1 dado2 soma soma2
1     3     1    4    16

[[14]]
  dado1 dado2 soma soma2
1     3     2    5    25

[[15]]
  dado1 dado2 soma soma2
1     3     3    6    36

[[16]]
  dado1 dado2 soma soma2
1     3     4    7    49

[[17]]
  dado1 dado2 soma soma2
1     3     5    8    64

[[18]]
  dado1 dado2 soma soma2
1     3     6    9    81

[[19]]
  dado1 dado2 soma soma2
1     4     1    5    25

[[20]]
  dado1 dado2 soma soma2
1     4     2    6    36

[[21]]
  dado1 dado2 soma soma2
1     4     3    7    49

[[22]]
  dado1 dado2 soma soma2
1     4     4    8    64

[[23]]
  dado1 dado2 soma soma2
1     4     5    9    81

[[24]]
  dado1 dado2 soma soma2
1     4     6   10   100

[[25]]
  dado1 dado2 soma soma2
1     5     1    6    36

[[26]]
  dado1 dado2 soma soma2
1     5     2    7    49

[[27]]
  dado1 dado2 soma soma2
1     5     3    8    64

[[28]]
  dado1 dado2 soma soma2
1     5     4    9    81

[[29]]
  dado1 dado2 soma soma2
1     5     5   10   100

[[30]]
  dado1 dado2 soma soma2
1     5     6   11   121

[[31]]
  dado1 dado2 soma soma2
1     6     1    7    49

[[32]]
  dado1 dado2 soma soma2
1     6     2    8    64

[[33]]
  dado1 dado2 soma soma2
1     6     3    9    81

[[34]]
  dado1 dado2 soma soma2
1     6     4   10   100

[[35]]
  dado1 dado2 soma soma2
1     6     5   11   121

[[36]]
  dado1 dado2 soma soma2
1     6     6   12   144
resultado %<>% bind_rows()
resultado
   dado1 dado2 soma soma2
1      1     1    2     4
2      1     2    3     9
3      1     3    4    16
4      1     4    5    25
5      1     5    6    36
6      1     6    7    49
7      2     1    3     9
8      2     2    4    16
9      2     3    5    25
10     2     4    6    36
11     2     5    7    49
12     2     6    8    64
13     3     1    4    16
14     3     2    5    25
15     3     3    6    36
16     3     4    7    49
17     3     5    8    64
18     3     6    9    81
19     4     1    5    25
20     4     2    6    36
21     4     3    7    49
22     4     4    8    64
23     4     5    9    81
24     4     6   10   100
25     5     1    6    36
26     5     2    7    49
27     5     3    8    64
28     5     4    9    81
29     5     5   10   100
30     5     6   11   121
31     6     1    7    49
32     6     2    8    64
33     6     3    9    81
34     6     4   10   100
35     6     5   11   121
36     6     6   12   144

5.0.4 Família apply

A família de funções apply em R inclui apply(), lapply(), sapply(), vapply(), tapply(), mapply(), entre outras. Essas funções são usadas para aplicar uma função a elementos de uma lista, vetor, matriz ou array. Ela funciona similar ao for em termos de repetições.

  • apply(): Aplica uma função qualquer a uma matriz, array ou data.frame ao longo de margens específicas (linhas 1 ou colunas 2). O output de um apply() é um vetor.
matriz1 <- matrix(1:6, nrow = 2)
soma_linhas <- apply(matriz1, 1, sum)
soma_colunas <- apply(matriz1, 2, sum)
  • lapply(): Aplica uma função a cada elemento de uma lista e retorna uma lista com os resultados.
minha_lista <- list(a = c(1, 2, 3), b = c(4, 5, 6))
resultados <- lapply(minha_lista, mean)
resultados
$a
[1] 2

$b
[1] 5

sapply(): A função sapply() é semelhante ao lapply(), mas tenta simplificar o resultado em um vetor ou matriz sempre que possível. Se todos os resultados forem do mesmo comprimento, será retornado um vetor. Caso contrário, será retornada uma matriz.

minha_lista <- list(a = c(1, 2, 3), b = c(4, 5, 6), c=c(7,6,8))
resultados <- sapply(minha_lista, mean)
resultados
a b c 
2 5 7 

mapply(): Aplica uma função a vários argumentos. Ele oferece uma maneira eficiente de realizar essas operações de forma flexível e controlada.

resultado <- mapply(soma_dois_dados, 
                    dado, 
                    dado)

print(resultado)
[1]  2  4  6  8 10 12

E se eu quisesse somar todas as combinações como no for aninhado? Para isso podemos expandir o grid de busca, utilizando o expand.grid.

dois_dados = expand.grid(dado, dado)
dois_dados
   Var1 Var2
1     1    1
2     2    1
3     3    1
4     4    1
5     5    1
6     6    1
7     1    2
8     2    2
9     3    2
10    4    2
11    5    2
12    6    2
13    1    3
14    2    3
15    3    3
16    4    3
17    5    3
18    6    3
19    1    4
20    2    4
21    3    4
22    4    4
23    5    4
24    6    4
25    1    5
26    2    5
27    3    5
28    4    5
29    5    5
30    6    5
31    1    6
32    2    6
33    3    6
34    4    6
35    5    6
36    6    6
resultado <- mapply(soma_dois_dados, 
                    dois_dados$Var1, 
                    dois_dados$Var2)

print(resultado)
 [1]  2  3  4  5  6  7  3  4  5  6  7  8  4  5  6  7  8  9  5  6  7  8  9 10  6
[26]  7  8  9 10 11  7  8  9 10 11 12

5.1 Exercícios

  1. Crie uma função que calcule os n primeiros números da sequência de Fibonacci. A sequência de Fibonacci começa com 0 e 1, e os números subsequentes são a soma dos dois anteriores (0, 1, 1, 2, 3, 5, 8, …).

  2. Crie uma função que verifique se um número n é primo.

  3. Calcule o 1o. e 2o. Quartil para todas as variáveis numéricas de mtcars. Se uma variável não for numérica retorne NA. Lembre-se cyl, vs e am são categóricas, e não numéricas.

5.2 Paralelização

A paralelização de código é uma técnica utilizada para melhorar a eficiência de programas, especialmente quando lidamos com tarefas intensivas em processamento.

5.2.1 Por que Paralelizar um Código?

A paralelização é necessária para aproveitar ao máximo os recursos de hardware disponíveis e melhorar o desempenho de um programa.

  1. Aproveitamento de Recursos: A maioria dos computadores modernos possui processadores multi-core, o que significa que eles têm várias unidades de processamento disponíveis. Para aproveitar ao máximo esses recursos, podemos dividir uma tarefa em partes menores que podem ser executadas simultaneamente em diferentes núcleos do processador.

  2. Redução de Tempo de Execução: Tarefas que seriam demoradas se executadas em sequência podem ser aceleradas significativamente por meio da paralelização. Isso é especialmente importante em tarefas que envolvem grandes conjuntos de dados ou cálculos complexos.

  3. Melhoria da Escalabilidade: Em situações onde a carga de trabalho pode variar, a paralelização permite que o programa adapte a quantidade de recursos necessários. Quando há mais trabalho a ser feito, mais threads[^Uma thread é uma unidade básica de processamento que pode ser executada em um programa. Ela é uma sequência de instruções que pode ser executada pelo processador de um computador.] ou processos paralelos podem ser alocados para acelerar o processamento.

  4. Resposta em Tempo Real: Em certos cenários, como em sistemas de controle ou simulações em tempo real, a paralelização pode ser necessária para garantir que as respostas sejam geradas dentro de limites de tempo estritos.

5.2.2 Quando Precisamos Paralelizar um Código?

Nem todos os programas precisam ser paralelizados. A decisão de quando paralelizar um código depende de vários fatores, incluindo:

  1. Natureza da Tarefa: Tarefas que podem ser divididas em partes independentes são candidatas naturais para a paralelização. Por exemplo, cálculos em um grande conjunto de dados podem ser paralelizados porque os dados podem ser divididos entre várias unidades de processamento.

  2. Recursos Disponíveis: A paralelização é mais eficaz quando há recursos de hardware adequados, como processadores multi-core ou clusters de computadores disponíveis. Paralelizar em um ambiente com apenas um núcleo de CPU pode não resultar em benefícios significativos.

  3. Tempo de Execução Crítico: Se o tempo de execução de um programa é crítico e precisa ser reduzido para cumprir prazos ou requisitos de desempenho, a paralelização pode ser uma solução viável.

  4. Complexidade Computacional: Tarefas que envolvem cálculos intensivos ou operações repetitivas podem se beneficiar da paralelização para acelerar o processamento.

  5. Escalabilidade Futura: Se você prevê que a carga de trabalho aumentará no futuro, a paralelização pode ser implementada desde o início para garantir que o programa possa lidar com cargas maiores.

5.2.3 Importante

  • Threads são unidades de execução dentro de um processo, enquanto multiprocessadores são sistemas de hardware com vários processadores independentes.

  • Threads compartilham o mesmo espaço de endereço, enquanto processadores em um multiprocessador executam tarefas independentes com seus próprios espaços de endereço.

5.2.4 Medindo a Qualidade da Paralelização

  1. Speedup: O speedup é uma medida que compara o tempo de execução de um programa paralelizado com o tempo de execução do mesmo programa executado de forma sequencial (em um único processador). O speedup é calculado da seguinte forma:

    \[ s = \frac{\text{Tempo Sequencial}}{\text{Tempo em Paralelo}} \]

    • Um speedup de 2, por exemplo, indica que o programa paralelizado é duas vezes mais rápido do que o programa sequencial.
  2. Eficiência: A eficiência é uma medida relacionada ao speedup, mas considera o fator de escalabilidade. Ela é calculada como a razão entre o speedup e o número de threads ou processadores utilizados:

    \[ E = \frac{\text{s}}{\text{número de threads}} \]

    • A eficiência ideal é 1, o que significa que a adição de mais threads ou processadores resulta em um aumento proporcional no desempenho.
    • O cálculo da eficiência não leva em consideração o tempo gasto para iniciar e encerrar as threads ou processos, chamado de overhead de paralelização. Em algumas situações, o overhead pode ser significativo e afetar a eficiência geral.

5.3 Paralelização em R

Existem inúmeras bibliotecas capazes de paralelizar códigos em R. As principais são: parallel, doParallel e a foreach. A primeira opção é a mais poderosa e vem integrada com o R, portanto, será o nosso foco aqui.

library(parallel)

# Quantos cores disponíveis?
detectCores()
[1] 10
# Cria um cluster de processos com 4 núcleos
cluster <- makeCluster(4)

# Define uma função que será executada em paralelo
funcao <- function(x) {
  return(x^2)
}

# Distribui a tarefa entre os processos no cluster (roda e espera)
resultados <- clusterApply(cluster, 1:10, funcao)

# Distribui a tarefa entre os processos no cluster (roda sem espera)
resultados2 <- clusterApplyLB(cluster, 1:10, funcao)


# Encerra o cluster
stopCluster(cluster)

# Resultados contêm os resultados das operações executadas em paralelo
print(resultados)
[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 9

[[4]]
[1] 16

[[5]]
[1] 25

[[6]]
[1] 36

[[7]]
[1] 49

[[8]]
[1] 64

[[9]]
[1] 81

[[10]]
[1] 100

A função clusterCall() permite que você execute uma função em paralelo em todos os processos de um cluster e recolha os resultados.

cl <- makeCluster(4)
## Chama a função em paralelo, 
## todas as vezes com os mesmos argumentos
clusterCall(cl, funcao, 1:10) 
[[1]]
 [1]   1   4   9  16  25  36  49  64  81 100

[[2]]
 [1]   1   4   9  16  25  36  49  64  81 100

[[3]]
 [1]   1   4   9  16  25  36  49  64  81 100

[[4]]
 [1]   1   4   9  16  25  36  49  64  81 100
## Exporta para todas as threads 
## os objetos
clusterExport(cl, "funcao")


fc <- function(a,b){
  return(a+b)
}
clusterExport(cl, "fc")
## Avalia a função nos argumentos definidos
## Cada thread re-avalia o resultado, mesmos argumentos
clusterEvalQ(cl, fc(1:10,1:10))
[[1]]
 [1]  2  4  6  8 10 12 14 16 18 20

[[2]]
 [1]  2  4  6  8 10 12 14 16 18 20

[[3]]
 [1]  2  4  6  8 10 12 14 16 18 20

[[4]]
 [1]  2  4  6  8 10 12 14 16 18 20
## Avalia a função nos argumentos definidos
## Cada thread re-avalia o resultado, argumentos distintos
clusterMap(cl, fc, 1:10, 1:10)
[[1]]
[1] 2

[[2]]
[1] 4

[[3]]
[1] 6

[[4]]
[1] 8

[[5]]
[1] 10

[[6]]
[1] 12

[[7]]
[1] 14

[[8]]
[1] 16

[[9]]
[1] 18

[[10]]
[1] 20

5.4 Exercícios

  1. Utilizando os dados GlobalIndicators, agrupe os países em duas categorias: alto e baixo. A primeira categoria deve conter os países com o último HDI > 0.7, a segunda os demais. Em seguida, para todas as variáveis (com excessão das HDIs) calcule: a média, a mediana, o desvio padrão e o intervalo interquartilíco. Teste se há diferença entre os dois grupos para cada uma das variáveis.
    • Dicas: Crie uma função que receba como argumento o grupo e uma variável.
    • Verifique se a distribuição é normal, utilize o shapiro.test(). Caso seja, utilize um teste-t (t.test()), caso contrário, utilize o teste de Mann-Whitney (wilcox.test(..., paired = F)).
Loading required package: data.table

Attaching package: 'data.table'
The following objects are masked from 'package:dplyr':

    between, first, last
Loading required package: tidyr

Attaching package: 'tidyr'
The following object is masked from 'package:magrittr':

    extract
co2 = data.table::fread("../data/GlobalIndicators/co2_production.csv")
gni = data.table::fread("../data/GlobalIndicators/gross_national_income_per_capital.csv")
hdi = data.table::fread("../data/GlobalIndicators/human_development_index.csv")
le = data.table::fread("../data/GlobalIndicators/life_expectancy_by_birth.csv")

df = full_join(co2, gni) %>% 
  full_join(hdi) %>% 
  full_join(le)
Joining with `by = join_by(ISO3, Country, hdicode, region, hdi_rank_2021)`
Joining with `by = join_by(ISO3, Country, hdicode, region, hdi_rank_2021)`
Joining with `by = join_by(ISO3, Country, hdicode, region, hdi_rank_2021)`