-
Notifications
You must be signed in to change notification settings - Fork 0
/
01-recovery-basics-joins.Rmd
902 lines (566 loc) · 36.9 KB
/
01-recovery-basics-joins.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
# Revisão básica e Joins {#intro}
Script da aula do Módulo intro abaixo.
```{r, echo=FALSE}
xfun::embed_file('Scripts/aula1.Rmd')
```
## Programação basica
### Controle de Fluxo
Os controles de fluxo são operações definidas em todas as linguagens de programação, como por exemplo Python, C, C++, Java, Fortran, Pascal, etc. Como não podia deixar de ser, tais operações também estão definidas dentro da linguagem R.
Cada linguagem de programação tem a sua própria sintaxe, isto é, sua própria regra de como essas operações devem ser usadas. Nesta aula será abordada a sintaxe e mais detalhes sobre alguns controles de fluxo para a linguagem R.
### Estrutura Condicional IF-ELSE
Uma instrução `if` permite que você execute código condicionalmente, ou seja, apenas uma parte do código será executada e *apenas se alguma condição for atendida*. E se parece com isso:
```{r, eval=FALSE}
if (condicao) {
#comandos caso condicao seja verdadeira (TRUE)
} else {
#comandos caso condicao seja falsa (FALSE)
}
```
ou simplesmente
```{r, eval=FALSE}
if (condicao) {
#comandos caso condicao seja verdadeira (TRUE)
}
```
Essa instrução lógica funciona analisando cada condição. O par de parênteses seguidos do `if` tem que ter um `objeto` do tipo `"logical"`. Os comandos dentro do primeiro par de chaves serão executados caso o `objeto condicao` seja `TRUE`. Caso contrário, os comandos de dentro do par de chaves depois do `else` serão executados. O comando `else` é opcional, como foi mostrado anteriormente. No caso do `else` não aparecer, nada será executado caso o `objeto condicao` seja `FALSE`.
#### _Exemplos_
O exemplo a seguir terá como resultado texto impresso na tela. O que será impresso depende do valor guardado na variável x. Se x receber o valor menor que 5, um texto será impresso, caso contrário, outro texto aparecerá na tela.
```{r}
#Neste caso x recebe um valor entre 1 e 10
x <- sample(1:10, 1)
if( x < 5 ){
print(paste(x,"é menor que",5))
} else {
print(paste(x,"é maior ou igual a",5))
}
```
A função `print()` é responsável por imprimir na tela e a função `paste()` por concatenar textos e criar um único objeto do tipo `"character"`. Para mais detalhes digite no `console` do `R` o comando `help(print)` e `help(paste)`.
Agora considere a seguinte sequência de comandos, qual o valor da variável y ao final desse código?
```{r}
x <- 3
if (x > 7){
y <- 2**x
} else {
y <- 3**x
}
```
A resposta para esse problema é 27. O controle de fluxo `if/else` será usado na maioria das vezes dentro de funções ou iteradores, como será visto adiante.
É possível encadear diversos `if() else` em sequência:
```{r}
numero <- 5
if (numero == 1) {
print("o número é igual a 1")
} else if (numero == 2) {
print("o número é igual a 2")
} else {
print("o número não é igual nem a 1 nem a 2")
}
```
### Iteradores
A iteração ajuda quando você precisa fazer a mesma coisa com várias entradas, como por exemplo: repetir a mesma operação em colunas diferentes ou em conjuntos de dados diferentes.
Existem ferramentas como loops `for` e `while`, que são um ótimo lugar para começar porque tornam a iteração muito explícita, então é óbvio o que está acontecendo.
#### FOR Loops
No entanto, os loops `for` podem ser bastante prolixos e requerem um pouco de código que é duplicado para cada loop `for` o que não é algo prático de dar manutenção.
Imagine que temos um _dataframe_ simples como:
```{r}
df <- tibble(
a = sample(1:10000, 10),
b = sample(1:10000, 10),
c = sample(1:10000, 10),
d = sample(1:10000, 10)
)
```
Para computar a mediana de cada coluna. Deve ser feito o uso da função `median``
```{r}
median(df$a)
median(df$b)
median(df$c)
median(df$d)
```
Mas isso quebra nossa regra de ouro: nunca copie e cole mais de duas vezes. Em vez disso, poderíamos usar um loop for:
```{r}
medianas <- vector("double", ncol(df)) # 1. saída (output)
for (i in seq_along(df)) { # 2. sequência (sequence)
medianas[i] <- median(df[[i]]) # 3. corpo (body)
}
medianas
```
Todo `for` loop tem três componentes:
1 - A saída: `saida <- vector("double", length (x))`. Antes de iniciar o loop, você deve sempre alocar espaço suficiente para a saída. Isso é muito importante para a eficiência: se você aumentar o loop for a cada iteração usando `c()` (por exemplo), seu loop for será muito lento.
Uma maneira geral de criar um vetor vazio de determinado comprimento é a função `vector()`. Que possui dois argumentos: o tipo do vetor (“logical”, “integer”, “double”, “character”, etc) e o comprimento do vetor.
2 - A sequência: `i` em `seq_along(df)`. Isso determina sobre o que fazer o loop: cada execução do loop for atribuirá `i` a um valor diferente de `seq_along(df)`.
Você pode não ter visto `seq_along()` antes. É uma versão segura do familiar `1:length(l)`, com uma diferença importante: se você tem um vetor de comprimento zero, `seq_along()` faz a coisa certa:
```{r}
y <- vector("double", 0)
seq_along(y)
#> integer(0)
1:length(y)
#> [1] 1 0
```
Claro que não vai ser criado um vetor de comprimento zero deliberadamente, mas é fácil criá-los acidentalmente. Se você usar `1:length(x)` em vez de `seq_along(x)`, é provável que receba uma mensagem de erro confusa.
3 - O corpo: `saida[i] <- median(df[[i]])`. Este é o código que faz o trabalho. É executado repetidamente, cada vez com um valor diferente para `i`. A primeira iteração executará a `saída[1] <- median(df[[1]])`, a segunda executará a `saída[2] <- median(df[[2]])` e assim por diante.
Isso é tudo que existe para o loop `for`! Agora é momento para praticar!
#### Variações em FOR Loops
Depois de ter o loop for básico em seu currículo, existem algumas variações das quais você deve estar ciente.
Existem quatro variações sobre o tema básico do loop for:
* Modificar um objeto existente, em vez de criar um novo objeto.
* Loop sobre nomes ou valores, em vez de índices.
* Tratamento de saídas de comprimento desconhecido.
* Manipulação de sequências de comprimento desconhecido.
#### Modificando um objeto existente
Às vezes, você deseja usar um loop `for` para modificar um objeto existente. Por exemplo:
```{r}
df <- tibble(
a = sample(1:10000, 10),
b = sample(1:10000, 10),
c = sample(1:10000, 10),
d = sample(1:10000, 10)
)
df$a <- scale(df$a)
df$b <- scale(df$b)
df$c <- scale(df$c)
df$d <- scale(df$d)
```
Para resolver isso com um loop `for`, novamente pensamos sobre os três componentes:
1 - Saída: já temos a saída - é o mesmo que a entrada!
2 - Sequência: podemos pensar em um quadro de dados como uma lista de colunas, então podemos iterar sobre cada coluna com `seq_along(df)`.
3 - Corpo: aplicar `scale()`
```{r}
for (i in seq_along(df)) {
df[[i]] <- scale(df[[i]])
}
```
Normalmente, você modificará uma lista ou quadro de dados com esse tipo de loop, então lembre-se de usar `[[`, não `[`.
#### For loops sobre nomes e valores ao invés de índices
Existem três maneiras básicas de fazer um loop em um vetor. Até agora eu mostrei o mais geral: looping sobre os índices numéricos com `for(i in seq_along(xs))` e extrair o valor com `x[[i]]`. Existem duas outras formas:
Faça um loop sobre os elementos: `for(x in xs)`. Isso é mais útil se você se preocupa apenas com os efeitos colaterais, como plotar ou salvar um arquivo, porque é difícil salvar a saída de forma eficiente.
Faça um loop sobre os nomes: `for(nm in names(xs))`. Isso fornece um nome, que você pode usar para acessar o valor com `x[[nm]]`. Isso é útil se você deseja usar o nome em um título de plotagem ou um nome de arquivo. Se você estiver criando uma saída nomeada, certifique-se de nomear o vetor de resultados da seguinte forma:
```{r, eval=FALSE}
results <- vector("list", length(x))
names(results) <- names(x)
```
A iteração sobre os índices numéricos é a forma mais geral, porque dada a posição, você pode extrair o nome e o valor:
```{r, eval=FALSE}
for (i in seq_along(x)) {
name <- names(x)[[i]]
value <- x[[i]]
}
```
#### For loop com comprimento de saída desconhecido
Às vezes, você pode não saber o tamanho da saída. Por exemplo, imagine que você deseja simular alguns vetores aleatórios de comprimentos aleatórios. Você pode ficar tentado a resolver esse problema aumentando progressivamente o vetor:
```{r}
means <- c(0, 1, 2)
output <- double()
for (i in seq_along(means)) {
n <- sample(100, 1)
output <- c(output, rnorm(n, means[[i]]))
}
str(output)
```
Mas isso não é muito eficiente porque em cada iteração, R tem que copiar todos os dados das iterações anteriores.
Uma solução melhor é salvar os resultados em uma lista e, em seguida, combiná-los em um único vetor após a conclusão do loop:
```{r}
out <- vector("list", length(means))
for (i in seq_along(means)) {
n <- sample(100, 1)
out[[i]] <- rnorm(n, means[[i]])
}
str(out)
str(unlist(out))
```
#### For loops com comprimento de sequência desconhecido ou While
Algumas vezes, você nem sabe por quanto tempo a sequência de entrada deve ser executada. Isso é comum ao fazer simulações. Por exemplo, você pode querer fazer um loop até obter três caras seguidas. Você não pode fazer esse tipo de iteração com o loop `for`. Em vez disso, você pode usar um loop `while`. Um loop `while` é mais simples do que loop `for` porque tem apenas dois componentes, uma condição e um corpo:
```{r, eval=FALSE}
while (condition) { #condição
# corpo (body)
}
```
Um loop `while` também é mais geral do que um loop `for`, porque você pode reescrever qualquer loop for como um loop `while`, mas não pode reescrever todo loop `while` como um loop `for`:
```{r, eval=FALSE}
for (i in seq_along(x)) {
# body
}
# Equivalent to
i <- 1
while (i <= length(x)) {
# body
i <- i + 1
}
```
Aqui está como poderíamos usar um loop `while` para descobrir quantas tentativas são necessárias para obter três caras em uma linha:
```{r}
flips <- 0
nheads <- 0
while (nheads < 3) {
if (sample(c("T", "H"), 1) == "H") {
nheads <- nheads + 1
} else {
nheads <- 0
}
flips <- flips + 1
}
flips
```
loops `while` foi mencionado apenas brevemente, porque é pouco usado. Eles são usados com mais frequência no contexto de simulação. No entanto, é bom saber que eles existem para que você esteja preparado para problemas em que o número de iterações não é conhecido com antecedência.
## Funções (Functions)
Uma das melhores maneiras de melhorar sua performance como cientista/analista de dados é escrever funções. As funções permitem automatizar tarefas comuns de uma forma mais poderosa e geral do que copiar e colar. Escrever uma função tem três grandes vantagens sobre o uso de copiar e colar:
1 - Você pode dar a uma função um nome elucidativo que torne seu código mais fácil de entender.
2 - Conforme os requisitos mudam, você só precisa atualizar o código em um lugar, em vez de muitos.
3 - Você elimina a chance de cometer erros incidentais ao copiar e colar (ou seja, atualizar o nome de uma variável em um lugar, mas não em outro).
Escrever boas funções é uma jornada para a vida toda. O objetivo deste módulo não é ensinar todos os detalhes esotéricos das funções, mas dar a você alguns direcionamentos pragmáticos que você pode aplicar imediatamente.
Além disso, algumas sugestões sobre como definir o estilo de seu código. Um bom estilo de código é como a pontuação correta. Você pode gerenciar sem ele, mas com certeza ele torna as coisas mais fáceis de ler! Tal como acontece com os estilos de pontuação, existem muitas variações possíveis.
### Quando eu devo escrever uma função?
Você deve considerar escrever uma função sempre que copiou e colou um bloco de código mais de duas vezes (ou seja, agora você tem três cópias do mesmo código).
```{r}
3 > 5
7 > 9
19 > 10
maior <- function(a,b){
if(a>b){
return(a)
}else{
return(b)
}
}
```
Depois da função definida e compilada podemos chamá-la sem ter que digitar todo o código novamente. Veja o que acontece quando a função é chamada no `console` do R.
```{r}
maior(3,2)
```
```{r}
maior(-1,4)
```
```{r}
maior(10,10)
```
Uma segunda função pode ser criada com o intuito de receber como argumento um número natural `n` e retorna um `array` com os `n` primeiros múltiplos de 3.
```{r}
multiplos_3 <- function(n){
vet <- NULL
for(i in 1:n){
vet[i] <- 3*i
}
return(vet)
}
multiplos_3(10)
```
```{r}
multiplos_3(15)
```
Existem três etapas principais para criar uma nova função:
1 - Você precisa escolher um nome para a função. Que faça sentido para o que a função executa.
2 - Você lista as entradas, ou argumentos, para a função dentro da função. Por exemplo uma chamada poderia ser a `function (x, y, z)`.
3 - Você coloca o código que desenvolveu no corpo da função, um {bloco que segue imediatamente a função (...).
Outra vantagem das funções é que, se nossos requisitos mudarem, só precisaremos fazer a mudança em um lugar.
Criar funções é uma parte importante do princípio _“do not repeat yourself” (or DRY)_. Quanto mais repetição você tiver em seu código, mais lugares você precisará se lembrar de atualizar quando as coisas mudarem (e sempre mudam!) E maior será a probabilidade de você criar bugs com o tempo.
### Funções são para humanos e computadores
É importante lembrar que as funções não são apenas para o computador, mas também para humanos. `R` não se importa com o que sua função é chamada, ou quais comentários ela contém, mas estes são importantes para leitores humanos.
O nome de uma função é importante. Idealmente, o nome da sua função será curto, mas evocará claramente o que a função faz. Isso é difícil! Mas é melhor ser claro do que curto, pois o preenchimento automático do `RStudio` facilita a digitação de nomes longos.
Geralmente, os nomes das funções devem ser verbos e os argumentos devem ser substantivos. Existem algumas exceções: substantivos estão ok se a função calcula um substantivo muito conhecido (ou seja, `mean()` é melhor do que `compute_mean()`), ou acessar alguma propriedade de um objeto (ou seja, `coef()` é melhor do que `get_coefficients()`). Um bom sinal de que um substantivo pode ser uma escolha melhor é se você estiver usando um verbo muito amplo como "obter", "calcular" ou "determinar". Use seu bom senso e não tenha medo de renomear uma função se você descobrir um nome melhor mais tarde.
```{r, eval=FALSE}
# Muito curto
f()
# Não descritivo
my_awesome_function()
# Longo mas claro
impute_missing()
collapse_years()
```
Se o nome da sua função for composto por várias palavras, recomendo usar “snake_case”, onde cada palavra minúscula é separada por um _underscore_. "camelCase" é uma alternativa popular. Escolha uma e seja consistente. O `R` em si não é muito consistente, mas não há nada que você possa fazer sobre isso. Certifique-se de não cair na mesma armadilha, tornando seu código o mais consistente possível.
```{r, eval=FALSE}
# Não faça isso!
col_mins <- function(x, y) {}
rowMaxes <- function(y, x) {}
```
Se você tem uma família de funções que fazem coisas semelhantes, certifique-se de que eles tenham nomes e argumentos consistentes. Use um prefixo comum para indicar que eles estão conectados. Isso é melhor do que um sufixo comum porque o preenchimento automático permite que você digite o prefixo e veja todos os membros da família.
```{r, eval=FALSE}
# Faça isso
input_select()
input_checkbox()
input_text()
# Escolha não fazer isso
select_input()
checkbox_input()
text_input()
```
### Argumentos de função
Os argumentos para uma função normalmente se enquadram em dois conjuntos amplos: um conjunto fornece os dados para calcular e o outro fornece argumentos que controlam os detalhes do cálculo. Por exemplo:
* Em `log()`, os dados são `x`, e o detalhe é a base do logaritmo.
* Em `mean()`, os dados são `x` e os detalhes são quantos dados cortar das extremidades (trim) e como lidar com os valores ausentes (na.rm).
Em `str_c()` você pode fornecer qualquer número de strings para `...`, e os detalhes da concatenação são controlados por `sep` e `collapse`.
Geralmente, os argumentos de dados devem vir primeiro. Os argumentos de detalhes devem ir no final e geralmente devem ter valores padrão. Você especifica um valor padrão da mesma maneira que chama uma função com um argumento nomeado.
## Boas práticas
### Nomeando variáveis
Os nomes dos argumentos também são importantes. O `R` mais uma vez não se importa, mas os leitores de seu código (incluindo você-futuro!) sim. Geralmente você deve preferir nomes mais longos e descritivos, mas há um punhado de nomes muito comuns e muito curtos. Vale a pena memorizar estes:
* `x, y, z`: vetores.
* `w`: um vetor de pesos.
* `df`: um quadro de dados.
* `i, j`: índices numéricos (normalmente linhas e colunas).
* `n`: comprimento ou número de linhas.
* `p`: número de colunas.
Caso contrário, considere combinar nomes de argumentos em funções `R` existentes. Por exemplo, use `na.rm` para determinar se os valores ausentes devem ser removidos.
### Ambiente (Environment)
O último componente de uma função é seu ambiente. Isso não é algo que você precisa entender profundamente quando começa a escrever funções. No entanto, é importante saber um pouco sobre os ambientes porque eles são cruciais para o funcionamento das funções. O ambiente de uma função controla como R encontra o valor associado a um nome. Por exemplo, use esta função:
```{r}
soma_xy <- function(x) {
x + y
}
```
Em muitas linguagens de programação, isso seria um erro, porque `y` não é definido dentro da função. Em `R`, este é um código válido porque `R` usa regras chamadas escopo léxico para encontrar o valor associado a um nome. Uma vez que `y` não está definido dentro da função, `R` irá procurar no ambiente onde a função foi definida:
```{r}
y <- 100
soma_xy(10)
y <- 1000
soma_xy(10)
```
Este comportamento parece uma receita para _bugs_ e de fato, você deve evitar criar funções como esta, mas em geral não causa muitos problemas (especialmente se você reiniciar o `R` regularmente).
## Manipulação de dados com `dplyr`
Esta seção, trataremos do pacote `dplyr`, que é um dos pacotes mais importantes da coleção _tidyverse_. Ele traz uma "gramática" específica de manipulação de dados, provendo um conjunto de funções que ajudam a resolver os desafios mais comuns na manipulação de dados. O objetivo é que você se familiarize com as funções do pacote `dplyr`; com as tarefas que elas executam; e veja exemplos de como aplicá-las a *data.frames*.
<!-- Além das funções básicas de **dplyr**, teremos um enfoque na utilização do pacote **dplyr** para trabalhar com bancos de dados relacionais **SQL** em conjunto com os pacotes **DBI** e **dbplyr**. -->
<!-- Serão utilizados os conteúdos dos *vignettes* dos pacotes **dplyr** e **dbplyr** disponíveis no próprio **R** e em http://dplyr.tidyverse.org/articles/dplyr.html e http://dbplyr.tidyverse.org/articles/dbplyr.html. -->
Para tanto vamos utilizar três _datasets_ que vamos citar e descrever abaixo:
* Chess game dataset
Este é um conjunto de pouco mais de 20.000 jogos coletados de uma seleção de usuários no site `Lichess.org`.
```{r, echo=FALSE, message=FALSE, warning=FALSE}
library(readr)
```
```{r, eval=F}
tb_chess_game <- read_csv(file="https://raw.githubusercontent.com/brunolucian/cursoBasicoR/master/datasets/chess_games.csv")
```
```{r, echo=FALSE}
tb_chess_game <- read_csv(file="https://raw.githubusercontent.com/brunolucian/cursoBasicoR/master/datasets/chess_games.csv")
tb_chess_game
```
* Netflix dataset
Programas de TV e filmes listados no Netflix
Este conjunto de dados consiste em programas de TV e filmes disponíveis no Netflix a partir de 2019. O conjunto de dados é coletado do Flixable, um mecanismo de busca Netflix de terceiros.
Em 2018, eles lançaram um relatório interessante que mostra que o número de programas de TV na Netflix quase triplicou desde 2010. O número de filmes do serviço de streaming diminuiu em mais de 2.000 títulos desde 2010, enquanto seu número de programas de TV quase triplicou. Será interessante explorar o que todos os outros insights podem ser obtidos no mesmo conjunto de dados.
```{r, eval=FALSE}
tb_netflix <- read_csv("https://raw.githubusercontent.com/brunolucian/cursoBasicoR/master/datasets/netflix_titles.csv")
```
```{r, echo=FALSE}
tb_netflix <- read_csv("https://raw.githubusercontent.com/brunolucian/cursoBasicoR/master/datasets/netflix_titles.csv")
tb_netflix
```
* Games sales dataset
Este conjunto de dados contém uma lista de videogames com vendas superiores a 100.000 cópias.
```{r, eval=FALSE}
tb_game_sales <- read_csv("https://raw.githubusercontent.com/brunolucian/cursoBasicoR/master/datasets/vgsales.csv")
```
```{r, echo=FALSE}
tb_game_sales <- read_csv("https://raw.githubusercontent.com/brunolucian/cursoBasicoR/master/datasets/vgsales.csv")
tb_game_sales
```
Conforme os próprios autores do pacote apontam, quando trabalhamos com dados, nós precisamos:
* Descobrir o que desejamos fazer;
* Descrever essas tarefas na forma de um programa de computador;
* Executar o programa.
O pacote `dplyr` torna estes passos mais rápidos e fáceis de executar, pois:
- ao invés de disponibilizar uma imensidão de funções, igual temos no *R base* e outros pacotes, ele restringe nossas opções e com isso nos ajuda a raciocinar de forma mais direta sobre o que desejamos e podemos fazer com os dados;
- provém "verbos" (ou funções) mais simples, ou seja, funções que correspondem às tarefas mais comuns de manipulação de dados, ajudando-nos assim a traduzir pensamentos em código;
- utiliza backends (códigos de final de processo, ou seja, mais próximos ao usuário) eficientes, de modo que gastamos menos tempo esperando pelo computador.
O pacote `dplyr` proporciona uma função para cada "verbo" considerado importante em manipulação de dados:
- `filter()` para selecionar "casos" baseados em seus valores;
- `arrange()` para reordenar os "casos";
- `select()` e rename() para selecionar variáveis baseadas em seus nomes;
- `mutate()` e `transmute()` para adicionar novas variáveis que são funções de variáveis já existentes nos dados;
- `summarise()` ou `summarize()` para condensar multiplos valores em um único;
- `group_by()` embora não seja considerado um dos "verbos", serve para agruparmos os dados em torno de uma ou mais variáveis. As funções consideradas "verbos" podem ser utilizadas antes ou após o agrupamentodos dados.
Veremos agora alguns exemplos de aplicação destas funções.
```{r, message=FALSE}
library(dplyr)
```
Note que pelo _print_ que temos novamente um _tibble_, que é uma forma moderna de data.frame implementada pelo pessoal do **tidyverse** . Este formato é particularmente útil para grandes *datasets* porque só é impresso na tela as primeiras linhas e diversos resumos/informações sobre nossas variáveis. Para converter data.frames em tibbles, usamos `as_tibble()`.
### Filtrando linhas com `filter()`
`filter()` permite fazer um _subset_ das linhas de um tibble/dataframe. Como todos os verbos simples de `dplyr`, o primeiro argumento será um _tibble_ (ou data.frame). O segundo argumento e os subsequentes se referem a variáveis dentro do data.frame, em que se selecionam as linhas onde a expressão é verdadeira (TRUE).
Vamos selecionar todos as linhas em que o jogador que estava com as peças pretas foi o vencedor da partida:
```{r}
filter(tb_chess_game, winner == "black")
```
```{r}
filter(tb_chess_game, winner == "black", black_rating < 1500)
```
```{r}
filter(tb_chess_game, winner == "black" | opening_eco =="A10")
```
<!-- **DICA:** No `base R`, isso seria equivalente ao código: `tb_ibama[tb_ibama$TIPO_DESTINO == "CONSUMIDOR_FINAL" & tb_ibama$MUNICÌPIO_DESTINATÁRIO == "Brasil Novo", ]`. -->
### Ordenando linhas com `arrange()`
`arrange()` funciona de modo semelhante a _filter_, mas ao invés de filtrar e selecionar linhas, ele apenas as reordena de acordo com alguma condição que passamos. Essa função recebe um data.frame e um conjunto de _column names_ pelo qual vai ordenar. Se você fornecer mais de um nome de coluna, cada coluna adicional passada será usada como critério de desempate.
```{r}
arrange(tb_chess_game, white_rating)
```
Se quiser ordenar de forma decrescente, utilize a função `desc(nome_da_coluna)` dentro de `arrange()`. Isso seria particularmente interessante se você quisesse ordenar os dados na coluna final do maior volume para o maior.
```{r}
arrange(tb_chess_game, desc(white_rating), desc(black_rating) )
```
### Selecionando colunas com `select()`
Geralmente trabalhamos com grandes _datasets_ com muitas colunas, mas somente algumas poucas colunas serão de nosso interesse. `select()` nos permite rapidamente focar num subconjunto dos dados. O melhor é que podemos utilizar operações - que normalmente só funcionam com as posições das colunas - direto nos nomes das variáveis.
```{r}
# Seleção por nome
select(tb_netflix, title, country, rating)
# Selecionando todas as colunas num intervalo de colunas (inclusive)
select(tb_netflix, title:release_year)
# Selecionando todas as colunas exceto aquelas em um intervalo (inclusive)
select(tb_netflix, -(date_added:description))
```
**DICA:** Existem _helper functions_ que podemos usar dentro de `select()`. São funções que lembram o funcionamento de uma _regular expression_ (Vamos ver no curso avançado com mais detalhes) para identificarmos nomes de colunas que atendem a determinado critério. São muito úteis com grandes datasets: `starts_with()`, `ends_with()`, `matches()` e `contains()`.
Vamos por exemplo selecionar todas as colunas que começam com a letra `d`:
```{r}
select(tb_netflix, starts_with("d"))
```
`select()` pode ser usada inclusive para renomear variáveis:
```{r}
select(tb_netflix, listed_category = listed_in)
```
A nova variável será chamada *listed_category* e receberá toda a informação da original *listed_in*.
No entanto, `select()` "abandona" todas as demais variáveis quando você faz uma renomeação. É melhor então é usar `rename()`:
```{r}
rename(tb_netflix, listed_category = listed_in)
```
### Adicionando novas colunas com `mutate()`
Além de selecionar conjuntos de colunas existentes, é geralmente útil adicionar novas colunas que são funções de colunas já presentes no tibble/dataframe. Veja um exemplo com `mutate()`, onde queremos calcular o preço por unidade de volume:
```{r}
mutate(tb_game_sales,
part_na_sales = NA_Sales/Global_Sales,
)
```
``mutate()`` nos permite ainda nos referir a colunas que acabamos de criar no mesmo comando. Vamos salvar esta alteração em um novo _tibble_, chamado `tb_game_sales_trans`
```{r}
tb_game_sales_trans <- mutate(tb_game_sales,
part_na_sales = NA_Sales/Global_Sales,
part_eu_sales = EU_Sales/Global_Sales,
part_jp_sales = JP_Sales/Global_Sales,
part_os_sales = Other_Sales/Global_Sales
)
```
Se só nos interessarem as novas variáveis, usaríamos *transmute()*:
```{r}
transmute(tb_game_sales,
part_na_sales = NA_Sales/Global_Sales,
part_eu_sales = EU_Sales/Global_Sales,
part_jp_sales = JP_Sales/Global_Sales,
part_os_sales = Other_Sales/Global_Sales
)
```
### Modificando entradas com `mutate()` ou `transmute()` + `case_when()`
`case_when()` é uma função do pacote `dplyr` que nos permite modificar as variáveis a partir de uma sequência de condições que devem ser respeitadas.
```{sh, eval=FALSE}
SE CONDIÇÃO1 VERDADEIRA ~ FAÇA TAL COISA;
SENÃO ~ FAÇA OUTRA COISA
```
Ela substitui as estruturas condicionais nativas do R (função `ifelse()`) e é inspirada na declaração equivalente em _SQL_ `CASE WHEN`. Os argumentos da função `case_when()` obedecem à seguinte estrutura: `operação condicional ~ novo valor`. No lado esquerdo do `~`, você tem a comparação a ser feita. No lado direito, temos o novo valor a ser atribuído caso o resultado da comparação seja `TRUE`. Você pode tratar, inclusive, mais de uma condição, desde que parta do caso mais específico para o mais geral.
```{r, eval=FALSE}
case_when(
condição1 ~ "novo_valor1",
condição2 ~ "novo_valor2",
condição3 ~ "novo_valor3",
TRUE ~ "valor para os demais casos não atendidos pelas condições acima"
)
```
Geralmente, no contexto de análise de dados com `dplyr`, utilizamos `case_when()` dentro de uma função `mutate()` ou `transmute` (que traz a apenas a nova coluna criada), uma vez que pretendemos alterar as entradas de uma coluna, alterando, portanto, a própria coluna.
No _tibble_ **`tb_games_sales`**, vamos criar uma nova coluna de caracteres chamada `nivel`, em que classificaremos um valor em: `alto` se `NA_sales > mean(NA_sales)`; `baixo` se `NA_sales < mean(NA_sales)` ou `razoavel` nos demais casos:
```{r, eval=FALSE}
transmute(tb_game_sales,
nivel_na =
case_when(
NA_sales > mean(NA_Sales) ~ "alto",
NA_sales < mean(NA_Sales) ~ "baixo",
TRUE ~ "razoável"
))
```
<!-- **DICA:** Se os valores das colunas recém criadas forem exibidos em notação exponencial (científica), execute o código abaixo para forçar a exibição das casas decimais em modo convencional e depois imprima novamente as colunas. -->
<!-- ```{r, eval=FALSE} -->
<!-- # scipen é uma espécie de penalização para a utilização de notação em valores exponenciais -->
<!-- # valores positivos penalizam a exibição em notação exponencial -->
<!-- # valores negativos penalizam a exibição em notação fixa/regular -->
<!-- options(scipen=999) -->
<!-- ``` -->
### Sumarizando valores com `summarise()`
O último "verbo" de `dplyr` é `summarise()` (ou `summarize`). Ele colapsa um tibble/dataframe em uma única linha.
```{r}
summarise(tb_game_sales,
max_venda_na = max(NA_Sales, na.rm = TRUE)
)
```
**DICA:** O parâmetro `na.rm = TRUE` dentro da função `max()` serve para que esta desconsidere os valores falatantes (`NA`) ao calcular a máximo Do contrário, na existência de _missing values_ `NA`, a função sempre retornará `NA`. Isso também vale para outras funções matemáticas de funcionamento vetorizado, como `sum()`, `mean` e `min`, por exemplo.
Dependendo do seu objetivo, pode ser mais útil utilizar o "verbo" `group_by()` que veremos mais a frente. Com ele poderemos calcular o valor médio por plataforma por exemplo.
## Estrutura do `dplyr`
Note que a sintaxe e funcionamento de todos os verbos de `dplyr` apresentados até aqui são bem similares:
- o primeiro argumento é um tibble/dataframe;
- os argumentos subsequentes descrevem o que fazer com os dados. Podemos nos referir às colunas do tibble/dataframe diretamente sem a necessidade de usar `$` ou indexação por `[]`.
- o resultado é um novo tibble/dataframe.
Juntas, essas propriedades facilitam encadear múltiplos passos simples para alcançar um resultado complexo. O restante do que `dplyr` faz, vem de aplicar as 5 funções que vimos até aqui a diferentes tipos de dados. Ao invpes de trabalharmos com dados desagregados, vamos passar a trabalhar agora com dados agrupados por uma ou mais variáveis.
### Operações agrupadas
Os verbos de `dplyr` tornam-se ainda mais poderosos quando os aplicamos a grupos de observações dentro de um conjunto de dados. Fazemos isso com a função `group_by()`. Ela "quebra" o dataset em grupos específicos de linhas. No início, não vemos qualquer alteração. É como se elas ficassem em segundo plano. No entanto, ao aplicarmos algum dos verbos principais no dataset "alterado" por `group_by`, eles automaticamente serão aplicados por grupo ou "by group".
O uso de agrupamento afeta o resultado dos verbos principais da seguinte forma:
- `select()` agrupado é o mesmo que não agrupado, exceto pelo fato que as variáveis de agrupamento são sempre preservadas.
- `arrange()` agrupado é mesmo que não agrupado, a não ser que usemos `.by_group = TRUE`, caso em que ordena primeiro pelas variáveis de agrupamento;
- `mutate()` e `filter()` são bastante úteis em conjunto com window functions (como `rank()` ou `min(x) == x`) (Ver vignette de "window-functions" do `dplyr`);
- `summarise()` calcula o sumário para cada grupo.
No exemplo a seguir, nós separamos o dataset por `Platform`, contando o número de registros para cada um das plataformas (`count = n()`), computando a valor médio por plataforma (`valor_medio_plataforma = mean(Global_Sales, na.rm = TRUE`)).
```{r}
by_platform <- group_by(tb_game_sales, Platform)
valor_medio_plataforma <- summarise(
by_platform,
count = n(),
mvp = mean(Global_Sales, na.rm = TRUE)
)
```
Note que `summarise()` é normalmente utilizada com **aggregate functions**, as quais recebem um vetor de valores e retornam um único número. Há muito exemplos úteis do *base R* que podem ser utilizados, como `min()`, `max()`, `mean()`, `sum()`, `sd()`, `median()`, etc. `dplyr` fornece mais algumas outras bem úteis:
- `n()`: número de observações no grupo atual;
- `n_distinct(x)`: número de valores únicos em x;
- `first(x)`, `last(x)` e `nth(x, n)` funcionam de forma similar a `x[1]`, `x[length(x)]` e `x[n]`, mas nos dão maior controle sobre resultado caso algum valor seja _missing_.
<!-- ISSO SE CHAMA PROGRSSIVELY ROLLING UP SUMMARIES IN A DATA FRAME -->
<!-- COMO MEDIAS PONDERADAS E VARIANCIAS EH COMPLICADO FAZER ISSO PQ EH UM EQUIVOCO -->
### Cuidados com os nomes de variáveis
Uma das melhores características do pacote `dplyr` é que podemos nos referir as variáveis de um _tibble_ ou dataframe como se fossem variáveis regulares (aquelas que estão no _Global Environment_). No entanto, a sintaxe de referência para nomes de colunas escondem algumas diferenças entre os verbos. Por exemplo, um nome ou valor de coluna passado para `select()` não tem o mesmo significado do que teria em `mutate()`.
Veja formas equivalentes do ponto de vista de **dplyr**:
```{r}
select(tb_chess_game, victory_status)
select(tb_chess_game, 6)
```
Se houver uma variável no _Global Environment_ com o mesmo nome de uma coluna de nosso tibble/dataframe, o `dplyr` dará prioridade à variável que está no _tibble_.
```{r}
victory_status <- "Vencedor"
select(tb_chess_game, victory_status)
```
## Usando o Pipe `%>%`
**dplyr** é funcional no sentido de que os chamados às funções não tem efeitos colaterais. Ou seja, você sempre precisa salvar seus resultados. Isso faz com que não tenhámos um código tão elegante, especialmente quando vamos fazer várias operações.
Para dar uma solução elegante ao problema, **dplyr** utiliza o operador pipe `%>%` do pacote `magritrr`. `x %>% f(y)` equivale a `f(x, y)`. Então, podemos utilizar esse operador para reescrever múltiplas operações que podemos ler da esquerda para direita e de cima para baixo.
```{r}
tb_game_sales %>%
group_by(Platform) %>%
summarise(count = n(),
mvp = mean(Global_Sales, na.rm = TRUE))
```
**DICA:** Note que o nome do _tibble_ ou dataframe só precisa ser informado uma única vez logo ao início do processo.
## Juntando bases
Podemos juntar duas tabelas a partir de uma (coluna) `chave` utilizando a função `left_join()`.
```{r}
orders <- read_csv("https://raw.githubusercontent.com/brunolucian/CursoIntermediarioR/main/Dados/olist_orders_dataset.csv")
items <- read_csv("https://raw.githubusercontent.com/brunolucian/CursoIntermediarioR/main/Dados/olist_order_items_dataset.csv")
```
Basta usar a função `left_join()` utilizando a coluna `order_id` como `chave`. Observe que as colunas `itens` aparece agora no fim da tabela.
```{r}
orders_items <- left_join(orders, items, by="order_id")
orders_items
```
Além da função `left_join()`, também são muito utilizadas as funções `right_join()` e `full_join()`.
- `right_join()`: retorna todas as linhas da base `y` e todas as colunas das bases `x` e `y`. Linhas de `y` sem correspondentes em `x` receberão `NA` na nova base.
- `full_join()`: retorna todas as linhas e colunas de `x`e `y`. Valores sem correspondência entre as bases receberão `NA` na nova base.
A figura a seguir esquematiza as operações dessas funções:
```{r dplyr-joins, echo=FALSE, fig.align='center'}
knitr::include_graphics('fig/joins.png')
```
***
## Execícios
Usando os dados da Olist apresentados neste módulo responda:
1. Em que cidades os consumidores moram?
2. Qual o valor médio dos pedidos?
3. As pessoas preferem pagar com boleto ou cartão?
4. Em quantas parcelas?
5. Em que datas há mais vendas?
## Referências da seção
- Wickham H.; François, R.; Henry, L.; Müller K. (2019). _dplyr: A Grammar of Data Manipulation_. R package version 0.8.1. URL [https://CRAN.R-project.org/package=dplyr](https://CRAN.R-project.org/package=dplyr).
- Wickham H.; François, R.; Henry, L.; Müller K. (2020). _dplyr vignette: Introduction_. URL [http://dplyr.tidyverse.org](http://dplyr.tidyverse.org).
- Wickham, H.; Grolemund, G. (2016). _R for Data Science: Import, Tidy, Transform, Visualize, and Model Data_. O'Reilly Media. december 2016. 522 pages. Disponível em: [https://www.r4ds.co.nz](https://www.r4ds.co.nz).