generated from jtr13/bookdown-template
-
Notifications
You must be signed in to change notification settings - Fork 24
/
05_tidyverse.Rmd
1427 lines (1046 loc) · 75.7 KB
/
05_tidyverse.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
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Tidyverse {#cap5}
## Pré-requisitos do capítulo {-}
Pacotes e dados que serão utilizados neste capítulo.
```{r eval=FALSE}
## Pacotes
library(tidyverse)
library(here)
library(ggplot2)
library(purrr)
library(tibble)
library(dplyr)
library(tidyr)
library(stringr)
library(readr)
library(forcats)
library(palmerpenguins)
library(lubridate)
## Dados
penguins <- palmerpenguins::penguins
penguins_raw <- palmerpenguins::penguins_raw
tidy_anfibios_locais <- ecodados::tidy_anfibios_locais
```
## Contextualização
Como todo idioma, a linguagem R vem passando por transformações nos últimos anos. Grande parte dessas mudanças estão dentro do paradigma de Ciência de Dados (*Data Science*), uma nova área de conhecimento que vem se moldando a partir do desenvolvimento da sociedade em torno da era digital e da grande quantidade de dados gerados e disponíveis pela internet, de onde advém os pilares das inovações tecnológicas: *Big Data*, *Machine Learning* e *Internet of Things*. A grande necessidade de computação para desenvolver esse novo paradigma colocaram o [R](https://www.r-project.org/) e o [python](https://www.python.org/) como as principais linguagens de programação frente a esses novos desafios. Apesar de não serem as únicas ferramentas utilizadas para esse propósito, elas rapidamente se tornaram uma das melhores escolhas, dado vários fatores como: ser de código-aberto e gratuitas, possuir grandes comunidades contribuidoras, ser linguagens de interpretação (orientadas a objeto) e relativamente fáceis de serem aprendidas e aplicadas.
Essas mudanças e expansões na utilização da linguagem R para a Ciência de Dados começaram a ser implementadas principalmente devido a um pesquisador: [Hadley Wickham](http://hadley.nz/), que iniciou sua contribuição à comunidade R com o desenvolvimento do já consagrado pacote `ggplot2` [@wickham2016] para a composição de gráficos no R (ver mais no Capítulo \@ref(cap6)), baseado na gramática de gráficos [@wilkinson2005]. Depois disso, Wickham dedicou-se ao desenvolvimento do pensamento de uma nova abordagem dentro da manipulação de dados, denominada **Tidy Data** (Dados organizados) [@wickham2014], na qual focou na limpeza e organização dos mesmos. A ideia postula que dados estão `tidy` quando: i) variáveis estão nas colunas, ii) observações estão nas linhas e iii) valores estão nas células, sendo que para esse último, não deve haver mais de um valor por célula.
A partir dessas ideias, o [*tidyverse*](https://www.tidyverse.org/) foi operacionalizado no R como uma coleção de pacotes que atuam no fluxo de trabalho comum da ciência de dados: importação, manipulação, exploração, visualização, análise e comunicação de dados e análises [@tidyverse2019] (Figura \@ref(fig:fig-r-tidyverse)). O principal objetivo do *tidyverse* é aproximar a linguagem para melhorar a interação entre ser humano e computador sobre dados, de modo que os pacotes compartilham uma filosofia de design de alto nível e gramática, além da estrutura de dados de baixo nível [@tidyverse2019]. As principais leituras sobre o tema no R são os artigos "Tidy Data" [@wickham2014] e "Welcome to the Tidyverse" [@tidyverse2019], e o livro ["R for Data Science"](https://r4ds.had.co.nz/) [@wickham2017], além do [Tidyverse](https://www.tidyverse.org/) que possui muito mais informações.
```{r fig-r-tidyverse, echo=FALSE, fig.cap="Modelo das ferramentas necessárias em um projeto típico de ciência de dados: importar, organizar, entender (transformar, visualizar, modelar) e comunicar, envolto à essas ferramentas está a programação. Adaptado de: Wickham & Grolemund [-@wickham2017]."}
knitr::include_graphics("img/cap05_fig01.png")
```
## *tidyverse*
Uma vez instalado e carregado, o pacote `tidyverse` disponibiliza um conjunto de ferramentas através de vários pacotes. Esses pacotes compartilham uma filosofia de design, gramática e estruturas. Podemos entender o *tidyverse* como um "dialeto novo" para a linguagem R, onde *tidy* quer dizer organizado, arrumado, ordenado, e *verse* é universo. A seguir, listamos os principais pacotes e suas funcionalidades.
- [`readr`](https://readr.tidyverse.org/): importa dados tabulares (e.g. `.csv` e `.txt`)
- [`tibble`](https://tibble.tidyverse.org/): implementa a classe `tibble`
- [`tidyr`](https://tidyr.tidyverse.org/): transformação de dados para `tidy`
- [`dplyr`](https://dplyr.tidyverse.org/): manipulação de dados
- [`stringr`](https://github.com/tidyverse/stringr): manipulação de caracteres
- [`forcats`](https://github.com/hadley/forcats): manipulação de fatores
- [`ggplot2`](https://ggplot2.tidyverse.org/): possibilita a visualização de dados
- [`purrr`](https://purrr.tidyverse.org/): disponibiliza ferramentas para programação funcional
Além dos pacotes principais, fazemos também menção a outros pacotes que estão dentro dessa abordagem e que trataremos ainda neste capítulo, em outro momento do livro, ou que você leitor(a) deve se familiarizar. Alguns pacotes compõem o tidyverse outros são mais gerais, entretanto, todos estão envolvidos de alguma forma com ciência de dados.
- [`readxl`](https://readxl.tidyverse.org/) e [`writexl`](https://cran.r-project.org/package=writexl): importa e exporta dados tabulares (.xlsx)
- [`janitor`](http://sfirke.github.io/janitor/): examina e limpa dados sujos
- [`DBI`](https://github.com/rstats-db/DBI): interface de banco de dados R
- [`haven`](https://github.com/tidyverse/haven): importa e exporta dados do SPSS, Stata e SAS
- [`httr`](https://github.com/r-lib/httr): ferramentas para trabalhar com URLs e HTTP
- [`rvest`](https://github.com/tidyverse/rvest): coleta facilmente (raspagem de dados) páginas da web
- [`xml2`](https://github.com/r-lib/xml2): trabalha com arquivos XML
- [`jsonlite`](https://github.com/jeroen/jsonlite): um analisador e gerador JSON simples e robusto para R
- [`hms`](https://github.com/rstats-db/hms): hora do dia
- [`lubridate`](https://github.com/tidyverse/lubridate): facilita o tratamento de datas
- [`magrittr`](https://magrittr.tidyverse.org/): provê os operadores pipe (`%>%`, `%$%`, `%<>%`)
- [`glue`](https://github.com/tidyverse/glue): facilita a combinação de dados e caracteres
- [`rmarkdown`](https://rmarkdown.rstudio.com/): cria documentos de análise dinâmica que combinam código, saída renderizada (como figuras) e texto
- [`knitr`](https://yihui.org/knitr/): projetado para ser um mecanismo transparente para geração de relatórios dinâmicos com R
- [`shiny`](https://shiny.rstudio.com/): framework de aplicativo Web para R
- [`flexdashboard`](https://rmarkdown.rstudio.com/flexdashboard/): painéis interativos para R
- [`here`](https://here.r-lib.org/): facilita a definição de diretórios
- [`usethis`](https://usethis.r-lib.org/): automatiza tarefas durante a configuração e desenvolvimento de projetos (Git, 'GitHub' e Projetos RStudio)
- [`data.table`](https://rdatatable.gitlab.io/data.table/): pacote que fornece uma versão de alto desempenho do `data.frame` (importar, manipular e expotar)
- [`reticulate`](https://rstudio.github.io/reticulate/): pacote que fornece ferramentas para integrar Python e R
- [`sparklyr`](https://spark.rstudio.com/): interface R para Apache Spark
- [`broom`](https://github.com/tidymodels/broom): converte objetos estatísticos em tibbles organizados
- [`modelr`](https://github.com/tidyverse/modelr): funções de modelagem que funcionam com o pipe
- [`tidymodels`](https://www.tidymodels.org/): coleção de pacotes para modelagem e aprendizado de máquina usando os princípios do tidyverse
Destacamos a grande expansão e aplicabilidade dos pacotes [rmarkdown](https://rmarkdown.rstudio.com/), [knitr](https://yihui.org/knitr/) e [bookdown](https://bookdown.org/), que permitiram a escrita deste livro usando essas ferramentas e linguagem de marcação, chamada [Markdown](https://daringfireball.net/projects/markdown/).
Para instalar os principais pacotes que integram o *tidyverse* podemos instalar o pacote `tidyverse`.
```{r eval=FALSE}
## Instalar o pacote tidyverse
install.packages("tidyverse")
```
Quando carregamos o pacote `tidyverse` podemos notar uma mensagem indicando quais pacotes foram carregados, suas respectivas versões e os conflitos com outros pacotes.
```{r message=TRUE}
## Carregar o pacote tidyverse
library(tidyverse)
```
Podemos ainda listar todos os pacotes do *tidyverse* com a função `tidyverse::tidyverse_packages()`.
```{r}
## Listar todos os pacotes do tidyverse
tidyverse::tidyverse_packages()
```
Também podemos verificar se os pacotes estão atualizados, senão, podemos atualizá-los com a função `tidyverse::tidyverse_update()`.
```{r eval=FALSE}
## Verificar e atualizar os pacotes do tidyverse
tidyverse::tidyverse_update(repos = "http://cran.us.r-project.org")
```
Todas as funções dos pacotes tidyverse usam **fonte minúscula** e **`_` (*underscore*)** para separar os nomes internos das funções, seguindo a mesma sintaxe do Python ("Snake Case"). Neste sentido de padronização, é importante destacar ainda que existe um guia próprio para que os scripts sigam a recomendação de padronização, o [The tidyverse style guide](https://style.tidyverse.org/), criado pelo próprio Hadley Wickham. Para pessoas que desenvolvem funções e pacotes existe o [Tidyverse design guide](https://design.tidyverse.org/) criado pelo *Tidyverse team*.
```{r eval=FALSE}
## Funções no formato snake case
read_csv()
read_tsv()
as_tibble()
left_join()
group_by()
```
Por fim, para evitar possíveis conflitos de funções com o mesmo nome entre pacotes, recomendamos fortemente o hábito de usar as funções precedidas do operador `::` e o respectivo pacote. Assim, garante-se que a função utilizada é referente ao pacote daquela função. Segue um exemplo com as funções apresentadas anteriormente.
```{r eval=FALSE}
## Funções seguidas de seus respectivos pacotes
readr::read_csv()
readr::read_tsv()
tibble::as_tibble()
dplyr::left_join()
dplyr::group_by()
```
Seguindo essas ideias do novo paradigma da **Ciência de Dados**, outro conjunto de pacotes foi desenvolvido, chamado de [`tidymodels`](https://www.tidymodels.org/) que atuam no fluxo de trabalho da análise de dados em ciência de dados: separação e reamostragem, pré-processamento, ajuste de modelos e métricas de performasse de ajustes. Por razões de espaço e especificidade, não entraremos em detalhes desses pacotes.
Seguindo a estrutura da Figura \@ref(fig:fig-r-tidyverse), iremos ver nos itens das próximas seções como esses passos são realizados com funções de cada pacote.
## here
Dentro do fluxo de trabalho do tidyverse, devemos sempre trabalhar com **Projetos do RStudio** (ver Capítulo \@ref(cap4)). Junto com o projeto, também podemos fazer uso do pacote `here`. Ele permite construir caminhos para os arquivos do projeto de forma mais simples e com maior reprodutibilidade.
Esse pacote cobre o ponto de mudarmos o diretório de trabalho que discutimos no Capítulo \@ref(cap4), dado que muitas vezes mudar o diretório com a função `setwd()` tende a ser demorado e tedioso, principalmente quando se trata de um script em que várias pessoas estão trabalhando em diferentes computadores e sistemas operacionais. Além disso, ele elimina a questão da fragilidade dos scripts, pois geralmente um script está com os diretórios conectados exatamente a um lugar e a um momento. Por fim, ele também simplifica o trabalho com subdiretórios, facilitando importar ou exportar arquivos para subdiretórios.
Seu uso é relativamente simples: uma vez criado e aberto o RStudio pelo Projeto do RStudio, o diretório automaticamente é definido para o diretório do projeto. Depois disso, podemos usar a função `here::here()` para definir os subdiretórios onde estão os dados. O exemplo da aplicação fica para a seção seguinte, quando iremos de fato importar um arquivo tabular para o R. Logo abaixo, mostramos como instalar e carregar o pacote `here`.
```{r eval=FALSE}
## Instalar
install.packages("here")
## Carregar
library(here)
```
## readr, readxl e writexl
Dado que possuímos um conjunto de dados e que geralmente esse conjunto de dados estará no formato tabular com umas das extensões: .csv, .txt ou .xlsx, usaremos o pacote `readr` ou `readxl` para importar esses dados para o R. Esses pacotes leem e escrevem grandes arquivos de forma mais rápida, além de fornecerem medidores de progresso de importação e exportação, e imprimir a informação dos modos das colunas no momento da importação. Outro ponto bastante positivo é que também classificam automaticamente o modo dos dados de cada coluna, i.e., se uma coluna possui dados numéricos ou apenas texto, essa informação será considerada para classificar o modo da coluna toda. A classe do objeto atribuído quando lido por esses pacotes é automaticamente um `tibble`, que veremos melhor na seção seguinte. Todas as funções deste pacote são listadas na [página de referência](https://readr.tidyverse.org/reference/index.html) do pacote.
Usamos as funções `readr::read_csv()` e `readr::write_csv()` para importar e exportar arquivos .csv do R, respectivamente. Para dados com a extensão .txt, podemos utilizar as funções `readr::read_tsv()` ou ainda `readr::read_delim()`. Para arquivos tabulares com a extensão .xlsx, temos de instalar e carregar dois pacotes adicionais: `readxl` e `writexl`, dos quais usaremos as funções `readxl::read_excel()`, `readxl::read_xlsx()` ou `readxl::read_xls()` para importar dados, atentado para o fato de podermos indicar a aba com os dados com o argumento `sheet`, e `writexl::write_xlsx()` para exportar.
Se o arquivo .csv foi criado com separador de decimais sendo `.` e separador de colunas sendo `,`, usamos as funções listadas acima normalmente. Caso seja criado com separador de decimais sendo `,` e separador de colunas sendo `;`, devemos usar a função `readr::read_csv2()` para importar e `readr::write_csv2()` para exportar nesse formato, que é mais comum no Brasil.
Para exemplificar como essas funções funcionam, vamos importar novamente os dados de comunidades de anfíbios da Mata Atlântica [@vancine2018], que fizemos o download no Capítulo \@ref(cap4). Estamos usando a função `readr::read_csv()`, indicando os diretórios com a função `here::here()`, e a classe do arquivo é `tibble`. Devemos atentar para o argumento `locale = readr::locale(encoding = "latin1")`,que selecionamos aqui como `latin1` para corrigir um erro de caracteres, que o autor dos dados cometeu quando publicou esse data paper.
```{r message=TRUE}
## Importar locais
tidy_anfibios_locais <- readr::read_csv(
here::here("dados", "tabelas", "ATLANTIC_AMPHIBIANS_sites.csv"),
locale = readr::locale(encoding = "latin1")
)
```
Caso o download não funcione ou haja problemas com a importação, disponibilizamos os dados também no pacote `ecodados`.
```{r eval=FALSE}
## Importar os dados pelo pacote ecodados
tidy_anfibios_locais <- ecodados::tidy_anfibios_locais
head(tidy_anfibios_locais)
```
Para se aprofundar no tema, recomendamos a leitura do Capítulo [11 Data import](https://r4ds.had.co.nz/data-import.html) de Wickham & Grolemund [-@wickham2017].
## tibble
O `tibble` (`tbl_sf`) é uma versão aprimorada do data frame (`data.frame`). Ele é a classe aconselhada para que as funções do tidyverse funcionem melhor sobre conjuntos de dados tabulares importados para o R.
Geralmente, quando utilizamos funções tidyverse para importar dados para o R, é essa classe que os dados adquirem depois de serem importados. Além da importação de dados, podemos criar um tibble no R usando a função `tibble::tibble()`, semelhante ao uso da função `data.frame()`. Podemos ainda converter um `data.frame` para um `tibble` usando a função `tibble::as_tibble()`. Entretanto, em alguns momentos precisaremos da classe `data.frame` para algumas funções específicas, e podemos converter um `tibble` para `data.frame` usando a função `tibble::as_data_frame()`.
Existem duas diferenças principais no uso do `tibble` e do `data.frame`: impressão e subconjunto. Objetos da classe `tibbles` possuem um método de impressão que mostra a contagem do número de linhas e colunas, e apenas as primeiras 10 linhas e todas as colunas que couberem na tela no console, além dos modos ou tipos das colunas. Dessa forma, cada coluna ou variável, pode ser do modo numbers (`int` ou `dbl`), character (`chr`), logical (`lgl`), factor (`fctr`), date + time (`dttm`) e date (`date`), além de outras [inúmeras possibilidades](https://tibble.tidyverse.org/articles/types.html).
Todas as funções deste pacote são listadas na [página de referência](https://tibble.tidyverse.org/reference/index.html) do pacote.
```{r}
## Tibble - impressão
tidy_anfibios_locais
```
Para o subconjunto, como vimos no Capítulo \@ref(cap4), para selecionar colunas e linhas de objetos bidimensionais podemos utilizar os operadores `[]` ou `[[]]`, associado com números separados por vírgulas ou o nome da coluna entre aspas, e o operador `$` para extrair uma coluna pelo seu nome. Comparando um `data.frame` a um `tibble`, o último é mais rígido na seleção das colunas: ele nunca faz correspondência parcial e gera um aviso se a coluna que você está tentando acessar não existe.
```{r, error=TRUE, warning=TRUE}
## Tibble - subconjunto
tidy_anfibios_locais$ref
```
Por fim, podemos "espiar" os dados utilizando a função `tibble::glimpse()` para ter uma noção geral de número de linhas, colunas, e conteúdo de todas as colunas. Essa é a função *tidyverse* da função *R Base* `str()`.
```{r}
## Espiar os dados
tibble::glimpse(tidy_anfibios_locais[, 1:10])
```
Para se aprofundar no tema, recomendamos a leitura do Capítulo [10 Tibbles](https://r4ds.had.co.nz/tibbles.html) de Wickham & Grolemund [-@wickham2017].
## magrittr (pipe - %>%)
O operador pipe `%>%` permite o encadeamento de várias funções, eliminando a necessidade de criar objetos para armazenar resultados intermediários. Dessa forma, pipes são uma ferramenta poderosa para expressar uma sequência de múltiplas operações.
O operador pipe `%>%` vem do pacote `magrittr`, entretanto, todos os pacotes no tidyverse automaticamente tornam o pipe disponível. Essa função torna os códigos em R mais simples, pois podemos realizar múltiplas operações em uma única linha. Ele captura o resultado de uma declaração e o torna a primeira entrada da próxima declaração, então podemos pensar como "EM SEGUIDA FAÇA" ao final de cada linha de código.
Todas as funções deste pacote são listadas na [página de referência](https://maggritr.tidyverse.org/reference/index.html) do pacote.
A principal vantagem do uso dos pipes é facilitar a depuração (*debugging* - achar erros) nos códigos, porque seu uso torna a linguagem R mais próxima do que falamos e pensamos, uma vez que evita o uso de funções dentro de funções (funções compostas, lembra-se do fog e gof do ensino médio? Evitamos eles aqui também).
Digitar `%>%` é um pouco chato, dessa forma, existe um atalho para sua inserção nos scripts: `Ctrl + Shift + M`.
Para deixar esse tópico menos estranho a quem possa ver essa operação pela primeira vez, vamos fazer alguns exemplos.
```{r}
## R Base - sem pipe
sqrt(sum(1:100))
## Tidyverse - com pipe
1:100 %>%
sum() %>%
sqrt()
```
Essas operações ainda estão simples, vamos torná-las mais complexas com várias funções compostas. É nesses casos que a propriedade organizacional do uso do pipe emerge: podemos facilmente ver o encadeamento de operações, onde cada função é disposta numa linha. Apenas um adendo: a função `set.seed()` fixa a amostragem de funções que geram valores aleatórios, como é o caso da função `rpois()`.
```{r}
## Fixar amostragem
set.seed(42)
## R Base - sem pipe
ve <- sum(sqrt(sin(log10(rpois(100, 10)))))
ve
## Fixar amostragem
set.seed(42)
## Tidyverse - com pipe
ve <- rpois(100, 10) %>%
log10() %>%
sin() %>%
sqrt() %>%
sum()
ve
```
O uso do pipe vai se tornar especialmente útil quando seguirmos para os pacotes das próximas duas seções: `tidyr` e `dplyr`. Com esses pacotes faremos operações em linhas e colunas de nossos dados tabulares, então podemos encadear uma série de funções para manipulação, limpeza e análise de dados.
Há ainda três outras variações do pipe que podem ser úteis em alguns momentos, mas que para funcionar precisam que o pacote `magrittr` esteja carregado:
- `%T>%`: retorna o lado esquerdo em vez do lado direito da operação
- `%$%`: "explode" as variáveis em um quadro de dados
- `%<>%`: permite atribuição usando pipes
Para se aprofundar no tema, recomendamos a leitura do Capítulo [18 Pipes](https://r4ds.had.co.nz/pipes.html) de Wickham & Grolemund [-@wickham2017].
::: {.alert .alert-info}
<strong> 📝 Importante </strong>\
A partir da versão do R 4.1+ (18/05/2021), o operador pipe se tornou nativo do R. Entretanto, o operador foi atualizado para `|>`, podendo ser inserido com o mesmo atalho `Ctrl + Shift + M`, mas necessitando uma mudança de opção em `Tools > Global Options > Code > [x] Use native pipe operator, |> (requires R 4.1+)`, requerendo que o RStudio esteja numa versão igual ou superior a 1.4.17+.
:::
## tidyr
Um conjunto de dados `tidy` (organizados) são mais fáceis de manipular, modelar e visualizar. Um conjunto de dados está no formato `tidy` ou não, dependendo de como linhas, colunas e células são combinadas com observações, variáveis e valores. Nos dados tidy, as variáveis estão nas colunas, observações estão nas linhas e valores estão nas células, sendo que para esse último, não deve haver mais de um valor por célula (Figura \@ref(fig:fig-r-dados-tidy)).
1. Cada variável em uma coluna
2. Cada observação em uma linha
3. Cada valor como uma célula
```{r fig-r-dados-tidy, echo=FALSE, fig.cap="As três regras que tornam um conjunto de dados *tidy*. Adaptado de: Wickham & Grolemund [-@wickham2017])."}
knitr::include_graphics("img/cap05_fig02.png")
```
Todas as funções deste pacote são listadas na [página de referência](https://tidyr.tidyverse.org/reference/index.html) do pacote.
Para realizar diversas transformações nos dados, a fim de ajustá-los ao formato `tidy` existe uma série de funções para: unir colunas, separar colunas, lidar com valores faltantes (`NA`), transformar a base de dados de formato longo para largo (ou vice-e-versa), além de outras [funções específicas](https://tidyr.tidyverse.org/reference/index.html).
- `unite()`: junta dados de múltiplas colunas em uma coluna
- `separate()`: separa caracteres em múltiplas colunas
- `separate_rows()`: separa caracteres em múltiplas colunas e linhas
- `drop_na()`: retira linhas com `NA` do conjunto de dados
- `replace_na()`: substitui `NA` do conjunto de dados
- `pivot_wider()`: transforma um conjunto de dados longo (*long*) para largo (*wide*)
- `pivot_longer()`: transforma um conjunto de dados largo (*wide*) para longo (*long*)
### palmerpenguins
Para exemplificar o funcionamento dessas funções, usaremos os dados de medidas de pinguins chamados [*palmerpenguins*](https://allisonhorst.github.io/palmerpenguins), disponíveis no pacote `palmerpenguins`.
```{r eval=FALSE}
## Instalar o pacote
install.packages("palmerpenguins")
```
Esses dados foram coletados e disponibilizados pela [Dra. Kristen Gorman](https://www.uaf.edu/cfos/people/faculty/detail/kristen-gorman.php) e pela [Palmer Station, Antarctica LTER](https://pal.lternet.edu/), membro da Long Term Ecological Research Network.
O pacote `palmerpenguins` contém dois conjuntos de dados. Um é chamado de `penguins` e é uma versão simplificada dos dados brutos. O segundo conjunto de dados é `penguins_raw` e contém todas as variáveis e nomes originais. Ambos os conjuntos de dados contêm dados para 344 pinguins, de três espécies diferentes, coletados em três ilhas no arquipélago de Palmer, na Antártica. Destacamos também a versão traduzida desses dados para o português, disponível no pacote [`dados`](https://cienciadedatos.github.io/dados/).
Vamos utilizar principalmente o conjunto de dados `penguins_raw`, que é a versão dos dados brutos.
```{r}
## Carregar o pacote palmerpenguins
library(palmerpenguins)
```
Podemos ainda verificar os dados, pedindo uma ajuda de cada um dos objetos.
```{r eval=FALSE}
## Ajuda dos dados
?penguins
?penguins_raw
```
### glimpse()
Primeiramente, vamos observar os dados e utilizar a função `tibble::glimpse()` para ter uma noção geral dos dados.
```{r}
## Visualizar os dados
penguins_raw
## Espiar os dados
dplyr::glimpse(penguins_raw)
```
### unite()
Primeiramente, vamos exemplificar como juntar e separar colunas. Vamos utilizar a função `tidyr::unite()` para unir as colunas. Há diversos parâmetros para alterar como esta função funciona, entretanto, é importante destacar três deles: `col` nome da coluna que vai receber as colunas unidas, `sep` indicando o caractere separador das colunas unidas, e `remove` para uma resposta lógica se as colunas unidas são removidas ou não. Vamos unir as colunas "Region" e "Island" na nova coluna "region_island".
```{r}
## Unir colunas
penguins_raw_unir <- tidyr::unite(data = penguins_raw,
col = "region_island",
Region:Island,
sep = ", ",
remove = FALSE)
head(penguins_raw_unir[, c("Region", "Island", "region_island")])
```
### separate()
De forma contrária, podemos utilizar as funções `tidyr::separate()` e `tidyr::separate_rows()` para separar elementos de uma coluna em mais colunas. Respectivamente, a primeira função separa uma coluna em novas colunas conforme a separação, e a segunda função separa uma coluna, distribuindo os elementos nas linhas. Novamente, há diversos parâmetros para mudar o comportamento dessas funções, mas destacaremos aqui quatro deles: `col` coluna a ser separada, `into` os nomes das novas colunas, `sep` indicando o caractere separador das colunas, e `remove` para uma resposta lógica se as colunas separadas são removidas ou não. Vamos separar a coluna "Stage" nas colunas "stage" e "egg_stage".
```{r}
## Separar colunas
penguins_raw_separar <- tidyr::separate(data = penguins_raw,
col = Stage,
into = c("stage", "egg_stage"),
sep = ", ",
remove = FALSE)
head(penguins_raw_separar[, c("Stage", "stage", "egg_stage")])
## Separar colunas em novas linhas
penguins_raw_separar_linhas <- tidyr::separate_rows(data = penguins_raw,
Stage,
sep = ", ")
head(penguins_raw_separar_linhas[, c("studyName", "Sample Number", "Species",
"Region", "Island", "Stage")])
```
### drop_na() e replace_na()
*Valor faltante* (`NA`) é um tipo especial de elemento que são discutidos no Capítulo \@ref(cap4) e são relativamente comuns em conjuntos de dados. Em *R Base*, vimos algumas formas de lidar com esse tipo de elemento. No formato `tidyverse`, existem também várias formas de lidar com eles, mas aqui focaremos nas funções `tidyr::drop_na()` e `tidyr::replace_na()`, para retirar linhas e substituir esses valores, respectivamente.
```{r, cache=TRUE}
## Remover todas as linhas com NAs
penguins_raw_todas_na <- tidyr::drop_na(data = penguins_raw)
head(penguins_raw_todas_na)
## Remover linhas de colunas específicas com NAs
penguins_raw_colunas_na <- tidyr::drop_na(data = penguins_raw,
any_of("Comments"))
head(penguins_raw_colunas_na[, "Comments"])
## Substituir NAs por outro valor
penguins_raw_subs_na <- tidyr::replace_na(data = penguins_raw,
list(Comments = "Unknown"))
head(penguins_raw_subs_na[, "Comments"])
```
### pivot_longer() e pivot_wider()
Por fim, trataremos da pivotagem ou remodelagem de dados. Veremos como mudar o formato do nosso conjunto de dados de longo (*long*) para largo (*wide*) e vice-versa. Primeiramente, vamos ver como partir de um dado longo (*long*) e criar um dado largo (*wide*). Essa é uma operação semelhante à "Tabela Dinâmica" das planilhas eletrônicas. Consiste em usar uma coluna para distribuir seus valores em outras colunas, de modo que os valores dos elementos são preenchidos corretamente, reduzindo assim o número de linhas e aumentando o número de colunas. Essa operação é bastante comum em Ecologia de Comunidades, quando queremos transformar uma lista de espécies em uma matriz de comunidades, com várias espécies nas colunas. Para realizar essa operação, usamos a função `tidyr::pivot_wider()`. Dos diversos parâmetros que podem compor essa função, dois deles são fundamentais: `names_from` que indica a coluna de onde os nomes serão usados e `values_from` que indica que indica a coluna com os valores.
```{r}
## Selecionar colunas
penguins_raw_sel_col <- penguins_raw[, c(2, 3, 13)]
head(penguins_raw_sel_col)
## Pivotar para largo
penguins_raw_pivot_wider <- tidyr::pivot_wider(data = penguins_raw_sel_col,
names_from = Species,
values_from = `Body Mass (g)`)
head(penguins_raw_pivot_wider)
```
De modo oposto, podemos partir de um conjunto de dados largo (*wide*), ou seja, com várias colunas, e queremos que essas colunas preencham uma única coluna, e que os valores antes espalhados nessas várias colunas sejam adicionados um embaixo do outro, numa única coluna, no formato longo (*long*). Para essa operação, podemos utilizar a função `tidyr::pivot_longer()`. Novamente, dos diversos parâmetros que podem compor essa função, três deles são fundamentais: `cols` indicando as colunas que serão usadas para serem pivotadas, `names_to` que indica a coluna de onde os nomes serão usados e `values_to` que indica a coluna com os valores.
```{r}
## Selecionar colunas
penguins_raw_sel_col <- penguins_raw[, c(2, 3, 10:13)]
head(penguins_raw_sel_col)
## Pivotar para largo
penguins_raw_pivot_longer <- tidyr::pivot_longer(data = penguins_raw_sel_col,
cols = `Culmen Length (mm)`:`Body Mass (g)`,
names_to = "medidas",
values_to = "valores")
head(penguins_raw_pivot_longer)
```
Para se aprofundar no tema, recomendamos a leitura do Capítulo 12 [Tidy data](https://r4ds.had.co.nz/tidy-data.html) de Wickham & Grolemund [-@wickham2017].
## dplyr
O `dplyr` é um pacote que facilita a manipulação de dados, com uma gramática simples e flexível (por exemplo, como filtragem, reordenamento, seleção, entre outras). Ele foi construído com o intuito de obter uma forma mais rápida e expressiva de manipular dados tabulares. O `tibble` é a versão de data frame mais conveniente para se usar com pacote `dplyr`.
Todas as funções deste pacote são listadas na [página de referência](https://dplyr.tidyverse.org/reference/index.html) do pacote.
### Gramática
Sua gramática simples contém funções verbais para manipulação de dados, baseada em:
- Verbos: `mutate()`, `select()`, `filter()`, `arrange()`, `summarise()`, `slice()`, `rename()`, etc.
- Replicação: `across()`, `if_any()`, `if_all()`, `where()`, `starts_with()`, `ends_with()`, `contains()`, etc.
- Agrupamento: `group_by()` e `ungroup()`
- Junções: `inner_join()`, `full_join()`, `left_join()`, `right_join()`, etc.
- Combinações: `bind_rows()` e `bind_cols()`
- Resumos, contagem e seleção: `n()`, `n_distinct()`, `first()`, `last()`, `nth()`, etc.
Existe uma série de funções para realizar a manipulação dos dados, com diversas finalidades: manipulação de uma tabela, manipulação de duas tabelas, replicação, agrupamento, funções de vetores, além de muitas outras [funções específicas](https://dplyr.tidyverse.org/reference/index.html).
- `relocate()`: muda a ordem das colunas
- `rename()`: muda o nome das colunas
- `select()`: seleciona colunas pelo nome ou posição
- `pull()`: seleciona uma coluna como vetor
- `mutate()`: adiciona novas colunas ou resultados em colunas existentes
- `arrange()`: reordena as linhas com base nos valores de colunas
- `filter()`: seleciona linhas com base em valores de colunas
- `slice()`: seleciona linhas de diferente formas
- `distinct()`: remove linhas com valores repetidos com base nos valores de colunas
- `count()`: conta observações para um grupo com base nos valores de colunas
- `group_by()`: agrupa linhas pelos valores das colunas
- `summarise()`: resume os dados através de funções considerando valores das colunas
- `*_join()`: funções que juntam dados de duas tabelas através de uma coluna chave
### Sintaxe
As funções do `dplyr` podem seguir uma mesma sintaxe: o `tibble` será sempre o primeiro argumento dessas funções, seguido de um operador pipe (`%>%`) e pelo nome da função que irá fazer a manipulação nesses dados. Isso permite o encadeamento de várias operações consecutivas mantendo a estrutura do dado original e acrescentando mudanças num encadeamento lógico.
Sendo assim, as funções verbais não precisam modificar necessariamente o tibble original, sendo que as operações de manipulações podem e devem ser atribuídas a um novo objeto.
```{r eval=FALSE}
## Sintaxe
tb_dplyr <- tb %>%
funcao_verbal1(argumento1, argumento2, ...) %>%
funcao_verbal2(argumento1, argumento2, ...) %>%
funcao_verbal3(argumento1, argumento2, ...)
```
Além de `data.frames` e `tibbles`, a manipulação pelo formato `dplyr` torna o trabalho com outros formatos de classes e dados acessíveis e eficientes como `data.table`, SQL e Apache Spark, para os quais existem pacotes específicos.
- [*dtplyr*](https://dtplyr.tidyverse.org/): manipular conjuntos de dados `data.table`
- [*dbplyr*](https://dbplyr.tidyverse.org/): manipular conjuntos de dados SQL
- [*sparklyr*](https://spark.rstudio.com/): manipular conjuntos de dados no Apache Spark
### palmerpenguins
Para nossos exemplos, vamos utilizar novamente os dados de pinguins [*palmerpenguins*](https://allisonhorst.github.io/palmerpenguins). Esses dados estão disponíveis no pacote `palmerpenguins`. Vamos utilizar principalmente o conjunto de dados `penguins`, que é a versão simplificada dos dados brutos `penguins_raw`.
```{r}
## Carregar o pacote palmerpenguins
library(palmerpenguins)
```
### relocate()
Primeiramente, vamos reordenar as colunas com a função `dplyr::relocate()`, onde simplesmente listamos as colunas que queremos mudar de posição e para onde elas devem ir. Para esse último passo há dois argumentos: `.before` que indica a coluna onde a coluna realocada deve se mover antes, e o argumento `.after` indicando onde deve se mover depois. Ambos podem ser informados com os nomes ou posições dessas colunas com números.
```{r}
## Reordenar colunas - nome
penguins_relocate_col <- penguins %>%
dplyr::relocate(sex, year, .after = island)
head(penguins_relocate_col)
## Reordenar colunas - posição
penguins_relocate_ncol <- penguins %>%
dplyr::relocate(sex, year, .after = 2)
head(penguins_relocate_ncol)
```
### rename()
Podemos renomear colunas facilmente com a função `dplyr::rename()`, onde primeiramente informamos o nome que queremos que a coluna tenha, seguido do operador `=` e a coluna do nosso dado ("nova_coluna = antiga_coluna"). Também podemos utilizar a função `dplyr::rename_with()`, que faz a mudança do nome em múltiplas colunas, que pode depender ou não de resultados booleanos.
```{r}
## Renomear as colunas
penguins_rename <- penguins %>%
dplyr::rename(bill_length = bill_length_mm,
bill_depth = bill_depth_mm,
flipper_length = flipper_length_mm,
body_mass = body_mass_g)
head(penguins_rename)
## mudar o nome de todas as colunas
penguins_rename_with <- penguins %>%
dplyr::rename_with(toupper)
head(penguins_rename_with)
```
### select()
Outra operação bastante usual dentro da manipulação de dados tabulares é a seleção de colunas. Podemos fazer essa operação com a função `dplyr::select()`, que seleciona colunas pelo nome ou pela sua posição. Aqui há uma série de possibilidades de seleção de colunas, desde utilizar operadores como `:` para selecionar intervalos de colunas, `!` para tomar o complemento (todas menos as listadas), além de funções como `dplyr::starts_with()`, `dplyr::ends_with()`, `dplyr::contains()` para procurar colunas com um padrão de texto do nome da coluna.
```{r}
## Selecionar colunas por posição
penguins_select_position <- penguins %>%
dplyr::select(3:6)
head(penguins_select_position)
## Selecionar colunas por nomes
penguins_select_names <- penguins %>%
dplyr::select(bill_length_mm:body_mass_g)
head(penguins_select_names)
## Selecionar colunas por padrão
penguins_select_contains <- penguins %>%
dplyr::select(contains("_mm"))
head(penguins_select_contains)
```
### pull()
Quando usamos a função `dplyr::select()`, mesmo que para apenas uma coluna, o retorno da função é sempre um `tibble`. Caso precisemos que essa coluna se torne um vetor dentro do encadeamento dos `pipes`, usamos a função `dplyr::pull()`, que extrai uma única coluna como vetor.
```{r}
## Coluna como vetor
penguins_select_pull <- penguins %>%
dplyr::pull(bill_length_mm)
head(penguins_select_pull, 15)
```
### mutate()
Uma das operações mais úteis dentre as operações para colunas é adicionar ou atualizar os valores de colunas. Para essa operação, usaremos a função `dplyr::mutate()`. Podemos ainda usar os argumentos `.before` e `.after` para indicar onde a nova coluna deve ficar, além do parâmetro `.keep` com diversas possibilidades de manter colunas depois de usar a função `dplyr::mutate()`. Por fim, é fundamental destacar o uso das funções de replicação: `dplyr::across()`, `dplyr::if_any()` e `dplyr::if_all()`, para os quais a função fará alterações em múltiplas colunas de uma vez, dependendo de resultados booleanos.
```{r}
## Adicionar colunas
penguins_mutate <- penguins %>%
dplyr::mutate(body_mass_kg = body_mass_g/1e3, .before = sex)
head(penguins_mutate)
## Modificar várias colunas
penguins_mutate_across <- penguins %>%
dplyr::mutate(across(where(is.factor), as.character))
head(penguins_mutate_across)
```
### arrange()
Além de operações em colunas, podemos fazer operações em linhas. Vamos começar com a reordenação das linhas com base nos valores das colunas. Para essa operação, usamos a função `dplyr::arrange()`. Podemos reordenar por uma ou mais colunas de forma crescente ou decrescente usando a função `desc()` ou o operador `-` antes da coluna de interesse. Da mesma forma que na função `dplyr::mutate()`, podemos usar as funções de replicação para ordenar as linhas para várias colunas de uma vez, dependendo de resultados booleanos.
```{r}
## Reordenar linhas - crescente
penguins_arrange <- penguins %>%
dplyr::arrange(body_mass_g)
head(penguins_arrange)
## Reordenar linhas - decrescente
penguins_arrange_desc <- penguins %>%
dplyr::arrange(desc(body_mass_g))
head(penguins_arrange_desc)
## Reordenar linhas - decrescente
penguins_arrange_desc_m <- penguins %>%
dplyr::arrange(-body_mass_g)
head(penguins_arrange_desc_m)
## Reordenar linhas - multiplas colunas
penguins_arrange_across <- penguins %>%
dplyr::arrange(across(where(is.numeric)))
head(penguins_arrange_across)
```
### filter()
Uma das principais e mais usuais operações que podemos realizar em linhas é a seleção de linhas através do filtro por valores de uma ou mais colunas, utilizando a função `dplyr::filter()`. Para realizar os filtros utilizaremos grande parte dos operadores relacionais e lógicos que listamos na Tabela \@ref(tab:tab-operadores) no Capítulo \@ref(cap4), especialmente os lógicos para combinações de filtros em mais de uma coluna. Além desses operadores, podemos utilizar a função `is.na()` para filtros em elementos faltantes, e as funções `dplyr::between()` e `dplyr::near()` para filtros entre valores, e para valores próximos com certa tolerância, respectivamente. Por fim, podemos usar as funções de replicação para filtro das linhas para mais de uma coluna, dependendo de resultados booleanos.
```{r}
## Filtrar linhas
penguins_filter <- penguins %>%
dplyr::filter(species == "Adelie")
head(penguins_filter)
## Filtrar linhas
penguins_filter_two <- penguins %>%
dplyr::filter(species == "Adelie" & sex == "female")
head(penguins_filter_two)
## Filtrar linhas
penguins_filter_in <- penguins %>%
dplyr::filter(species %in% c("Adelie", "Gentoo"),
sex == "female")
head(penguins_filter_in)
## Filtrar linhas - NA
penguins_filter_na <- penguins %>%
dplyr::filter(!is.na(sex) == TRUE)
head(penguins_filter_na)
## Filtrar linhas - intervalos
penguins_filter_between <- penguins %>%
dplyr::filter(between(body_mass_g, 3000, 4000))
head(penguins_filter_between)
## Filtrar linhas por várias colunas
penguins_filter_if <- penguins %>%
dplyr::filter(if_all(where(is.integer), ~ . > 200))
head(penguins_filter_if)
```
### slice()
Além da seleção de linhas por filtros, podemos fazer a seleção das linhas por intervalos, indicando quais linhas desejamos, usando a função `dplyr::slice()`, e informando o argumento `n` para o número da linha ou intervalo das linhas. Essa função possui variações no sufixo muito interessantes: `dplyr::slice_head()` e `dplyr::slice_tail()` seleciona as primeiras e últimas linhas, `dplyr::slice_min()` e `dplyr::slice_max()` seleciona linhas com os maiores e menores valores de uma coluna, e `dplyr::slice_sample()` seleciona linhas aleatoriamente.
```{r}
## Seleciona linhas
penguins_slice <- penguins %>%
dplyr::slice(n = c(1, 3, 300:n()))
head(penguins_slice)
## Seleciona linhas - head
penguins_slice_head <- penguins %>%
dplyr::slice_head(n = 5)
head(penguins_slice_head)
## Seleciona linhas - max
penguins_slice_max <- penguins %>%
dplyr::slice_max(body_mass_g, n = 5)
head(penguins_slice_max)
## Seleciona linhas - sample
penguins_slice_sample <- penguins %>%
dplyr::slice_sample(n = 30)
head(penguins_slice_sample)
```
### distinct()
A última operação que apresentaremos para linhas é a retirada de linhas com valores repetidos com base nos valores de uma ou mais colunas, utilizando a função `dplyr::distinct()`. Essa função por padrão retorna apenas a(s) coluna(s) utilizada(s) para retirar as linhas com valores repetidos, sendo necessário acrescentar o argumento `.keep_all = TRUE` para retornar todas as colunas. Por fim, podemos usar as funções de replicação para retirar linhas com valores repetidos para mais de uma coluna, dependendo de resultados booleanos.
```{r}
## Retirar linhas com valores repetidos
penguins_distinct <- penguins %>%
dplyr::distinct(body_mass_g)
head(penguins_distinct)
## Retirar linhas com valores repetidos - manter as outras colunas
penguins_distinct_keep_all <- penguins %>%
dplyr::distinct(body_mass_g, .keep_all = TRUE)
head(penguins_distinct_keep_all)
## Retirar linhas com valores repetidos para várias colunas
penguins_distinct_keep_all_across <- penguins %>%
dplyr::distinct(across(where(is.integer)), .keep_all = TRUE)
head(penguins_distinct_keep_all_across)
```
### count()
Agora entraremos no assunto de resumo das observações. Podemos fazer contagens resumos dos nossos dados, utilizando para isso a função `dplyr::count()`. Essa função contará valores de uma ou mais colunas, geralmente para variáveis categóricas, semelhante à função *R Base* `table()`, mas num contexto *tidyverse*.
```{r}
## Contagens de valores para uma coluna
penguins_count <- penguins %>%
dplyr::count(species)
penguins_count
## Contagens de valores para mais de uma coluna
penguins_count_two <- penguins %>%
dplyr::count(species, island)
penguins_count_two
```
### group_by()
Uma grande parte das operações feitas nos dados são realizadas em grupos definidos por valores de colunas com dados categóricas. A função `dplyr::group_by()` transforma um `tibble` em um `tibble grouped`, onde as operações são realizadas "por grupo". Essa função é utilizada geralmente junto com a função `dplyr::summarise()`, que veremos logo em seguida. O agrupamento não altera a aparência dos dados (além de informar como estão agrupados). A função `dplyr::ungroup()` remove o agrupamento. Podemos ainda usar funções de replicação para fazer os agrupamentos para mais de uma coluna, dependendo de resultados booleanos.
```{r}
## Agrupamento
penguins_group_by <- penguins %>%
dplyr::group_by(species)
head(penguins_group_by)
## Agrupamento de várias colunas
penguins_group_by_across <- penguins %>%
dplyr::group_by(across(where(is.factor)))
head(penguins_group_by_across)
```
### summarise()
Como dissemos, muitas vezes queremos resumir nossos dados, principalmente para ter uma noção geral das variáveis (colunas) ou mesmo começar a análise exploratória resumindo variáveis contínuas por grupos de variáveis categóricas. Dessa forma, ao utilizar a função `dplyr::summarise()` teremos um novo `tibble` com os dados resumidos, que é a agregação ou resumo dos dados através de funções. Da mesma forma que outras funções, podemos usar funções de replicação para resumir valores para mais de uma coluna, dependendo de resultados booleanos.
```{r}
## Resumo
penguins_summarise <- penguins %>%
dplyr::group_by(species) %>%
dplyr::summarize(body_mass_g_mean = mean(body_mass_g, na.rm = TRUE),
body_mass_g_sd = sd(body_mass_g, na.rm = TRUE))
penguins_summarise
## Resumo para várias colunas
penguins_summarise_across <- penguins %>%
dplyr::group_by(species) %>%
dplyr::summarize(across(where(is.numeric), ~ mean(.x, na.rm = TRUE)))
penguins_summarise_across
```
### bind_rows() e bind_cols()
Muitas vezes teremos de combinar duas ou mais tabelas de dados. Podemos utilizar as funções *R Base* `rbind()` e `cbind()`, como vimos no Capítulo \@ref(cap4). Entretanto, pode ser interessante avançar para as funções `dplyr::bind_rows()` e `dplyr::bind_cols()` do formato *tidyverse*. A ideia é muito semelhante: a primeira função combina dados por linhas e a segunda por colunas. Entretanto, há algumas vantagens no uso dessas funções, como a identificação das linhas pelo argumento `.id` para a primeira função, e a conferência do nome das colunas pelo argumento `.name_repair` para a segunda função.
```{r}
## Selecionar as linhas para dois tibbles
penguins_01 <- dplyr::slice(penguins, 1:5)
penguins_02 <- dplyr::slice(penguins, 51:55)
## Combinar as linhas
penguins_bind_rows <- dplyr::bind_rows(penguins_01, penguins_02, .id = "id")
head(penguins_bind_rows)
## Combinar as colunas
penguins_bind_cols <- dplyr::bind_cols(penguins_01, penguins_02, .name_repair = "unique")
head(penguins_bind_cols)
```
### *_join()
Finalmente, veremos o último conjunto de funções do pacote `dplyr`, a junção de tabelas. Nessa operação, fazemos a combinação de pares de conjunto de dados tabulares por uma ou mais colunas chaves. Há dois tipos de junções: junção de modificação e junção de filtragem. A junção de modificação primeiro combina as observações por suas chaves e, em seguida, copia as variáveis (colunas) de uma tabela para a outra. É fundamental destacar a importância da coluna chave, que é indicada pelo argumento `by`. Essa coluna deve conter elementos que sejam comuns às duas tabelas para que haja a combinação dos elementos.
Existem quatro tipos de junções de modificações, que são realizadas pelas funções: `dplyr::inner_join()`, `dplyr::left_join()`, `dplyr::full_join()` e `dplyr::right_join()`, e que podem ser representadas na Figura \@ref(fig:fig-r-join).
```{r fig-r-join, echo=FALSE, fig.cap="Diferentes tipos de joins, representados com um diagrama de Venn. Adaptado de: Wickham & Grolemund [-@wickham2017]."}
knitr::include_graphics("img/cap05_fig03.png")
```
Considerando a nomenclatura de duas tabelas de dados por `x` e `y`, temos:
- `inner_join(x, y)`: mantém apenas as observações em `x` e em `y`
- `left_join(x, y)`: mantém todas as observações em `x`
- `right_join(x, y)`: mantém todas as observações em `y`
- `full_join(x, y)`: mantém todas as observações em `x` e em `y`
Aqui, vamos demostrar apenas a função `dplyr::left_join()`, combinando um `tibble` de coordenadas geográficas das ilhas com o conjunto de dados do penguins.
```{r}
## Adicionar uma coluna chave de ids
penguin_islands <- tibble(
island = c("Torgersen", "Biscoe", "Dream", "Alpha"),
longitude = c(-64.083333, -63.775636, -64.233333, -63),
latitude = c(-64.766667, -64.818569, -64.733333, -64.316667))
## Junção - left
penguins_left_join <- dplyr::left_join(penguins, penguin_islands, by = "island")
head(penguins_left_join)
```
Já o segundo tipo de junção, a junção de filtragem combina as observações da mesma maneira que as junções de modificação, mas afetam as observações (linhas), não as variáveis (colunas). Existem dois tipos.
- `semi_join(x, y)`: mantém todas as observações em `x` que têm uma correspondência em `y`
- `anti_join(x, y)`: elimina todas as observações em `x` que têm uma correspondência em `y`
De forma geral, *semi-joins* são úteis para corresponder tabelas de resumo filtradas de volta às linhas originais, removendo as linhas que não estavam antes do join. Já *anti-joins* são úteis para diagnosticar incompatibilidades de junção, por exemplo, ao verificar os elementos que não combinam entre duas tabelas de dados.
### Operações de conjuntos e comparação de dados
Temos ainda operações de conjuntos e comparação de dados.
- `union(x, y)`: retorna todas as linhas que aparecem em `x`, `y` ou mais dos conjuntos de dados
- `interesect(x, y)`: retorna apenas as linhas que aparecem em `x` e em `y`
- `setdiff(x, y)`: retorna as linhas que aparecem `x`, mas não em `y`
- `setequal(x, y)`: retorna se `x` e `y` são iguais e quais suas diferenças
Para se aprofundar no tema, recomendamos a leitura do Capítulo [13 Relational data](https://r4ds.had.co.nz/relational-data.html) de Wickham & Grolemund [-@wickham2017].
## stringr
O pacote `stringr` fornece um conjunto de funções para a manipulação de caracteres ou strings. O pacote concentra-se nas funções de manipulação mais importantes e comumente usadas. Para funções mais específicas, recomenda-se usar o pacote `stringi`, que fornece um conjunto mais abrangente de funções. As funções do `stringr` podem ser agrupadas em algumas operações para tarefas específicas como: i) correspondência de padrões, ii) retirar e acrescentar espaços em branco, iii) mudar maiúsculas e minúsculas, além de muitas outras operações com caracteres.
Todas as funções deste pacote são listadas na [página de referência](https://stringr.tidyverse.org/reference/index.html) do pacote.
Demonstraremos algumas funções para algumas operações mais comuns, utilizando um vetor de um elemento, com o string "penguins".
Podemos explorar o comprimento de strings com a função `stringr::str_length()`.
```{r}
## Comprimento
stringr::str_length(string = "penguins")
```
Extrair um string por sua posição usando a função `stringr::str_sub()` ou por um padrão com `stringr::str_extract()`.
```{r}
## Extrair pela posição
stringr::str_sub(string = "penguins", end = 3)
## Extrair por padrão
stringr::str_extract(string = "penguins", pattern = "p")
```
Substituir strings por outros strings com `stringr::str_replace()`.
```{r}
## Substituir
stringr::str_replace(string = "penguins", pattern = "i", replacement = "y")
```
Separar strings por um padrão com a função `stringr::str_split()`.
```{r}
## Separar
stringr::str_split(string = "p-e-n-g-u-i-n-s", pattern = "-", simplify = TRUE)
```
Inserir espaços em brancos pela esquerda, direita ou ambos com a função `stringr::str_pad()`.
```{r}
## Inserir espacos em branco
stringr::str_pad(string = "penguins", width = 10, side = "left")
stringr::str_pad(string = "penguins", width = 10, side = "right")
stringr::str_pad(string = "penguins", width = 10, side = "both")
```
Também podemos remover espaços em branco da esquerda, direita ou ambos, utilizando `stringr::str_trim()`.
```{r}
## Remover espacos em branco
stringr::str_trim(string = " penguins ", side = "left")
stringr::str_trim(string = " penguins ", side = "right")
stringr::str_trim(string = " penguins ", side = "both")
```
Podemos também alterar minúsculas e maiúsculas em diferentes posições do string, com várias funções.
```{r}
## Alterar minúsculas e maiúsculas
stringr::str_to_lower(string = "Penguins")
stringr::str_to_upper(string = "penguins")
stringr::str_to_sentence(string = "penGuins")
stringr::str_to_title(string = "penGuins")
```
Podemos ainda ordenar os elementos de um vetor por ordem alfabética de forma crescente ou decrescente, usando `stringr::str_sort()`.
```{r}
## Ordenar
stringr::str_sort(x = letters)
stringr::str_sort(x = letters, dec = TRUE)
```
Podemos ainda utilizar essas funções em complemento com o pacote `dplyr`, para alterar os strings de colunas ou nome das colunas.
```{r}
## Alterar valores das colunas
penguins_stringr_valores <- penguins %>%
dplyr::mutate(species = stringr::str_to_lower(species))
## Alterar nome das colunas
penguins_stringr_nomes <- penguins %>%
dplyr::rename_with(stringr::str_to_title)
```
Para se aprofundar no tema, recomendamos a leitura do Capítulo [14 Strings](https://r4ds.had.co.nz/strings.html) de Wickham & Grolemund [-@wickham2017].
## forcats
O pacote `forcats` fornece um conjunto de ferramentas úteis para facilitar a manipulação de fatores. Como dito no Capítulo \@ref(cap4), usamos fatores geralmente quando temos dados categóricos, que são variáveis que possuem um conjunto de valores fixos e conhecidos. As funções são utilizadas principalmente para: i) mudar a ordem dos níveis, ii) mudar os valores dos níveis, iii) adicionar e remover níveis, iv) combinar múltiplos níveis, além de outras operações.
Todas as funções deste pacote são listadas na [página de referência](https://forcats.tidyverse.org/reference/index.html) do pacote.
Vamos utilizar ainda os dados `penguins` e `penguins_raw` para exemplificar o uso do pacote `forcats`.
```{r}
## Carregar o pacote palmerpenguins
library(palmerpenguins)
```
Primeiramente, vamos converter dados de string para fator, utilizando a função `forcats::as_factor()`.
```{r}
## String
forcats::as_factor(penguins_raw$Species) %>% head()
```
Podemos facilmente mudar o nome dos níveis utilizando a função `forcats::fct_recode()`.
```{r}
## Mudar o nome dos níveis
forcats::fct_recode(penguins$species, a = "Adelie", c = "Chinstrap", g = "Gentoo") %>% head()
```
Para inverter os níveis, usamos a função `forcats::fct_rev()`.
```{r}
## Inverter os níveis
forcats::fct_rev(penguins$species) %>% head()
```
Uma operação muito comum com fatores é mudar a ordem dos níveis. Quando precisamos especificar a ordem dos níveis, podemos fazer essa operação manualmente com a função `forcats::fct_relevel()`.
```{r}
## Especificar a ordem dos níveis
forcats::fct_relevel(penguins$species, "Chinstrap", "Gentoo", "Adelie") %>% head()
```
Como vimos, a reordenação dos níveis pode ser feita manualmente. Mas existem outras formas automáticas de reordenação seguindo algumas regras, para as quais existem funções específicas.
- `forcats::fct_inorder()`: pela ordem em que aparecem pela primeira vez
- `forcats::fct_infreq()`: por número de observações com cada nível (decrescente, i.e., o maior primeiro)
- `forcats::fct_inseq()`: pelo valor numérico do nível
```{r}
## Níveis pela ordem em que aparecem
forcats::fct_inorder(penguins$species) %>% head()
## Ordem (decrescente) de frequência
forcats::fct_infreq(penguins$species) %>% head()
```
Por fim, podemos fazer a agregação de níveis raros em um nível utilizando a função `forcats::fct_lump()`.
```{r}
## Agregação de níveis raros em um nível
forcats::fct_lump(penguins$species) %>% head()
```
Podemos ainda utilizar essas funções em complemento com o pacote `dplyr` para fazer manipulações de fatores nas colunas de `tibbles`.
```{r}
## Transformar várias colunas em fator
penguins_raw_multi_factor <- penguins_raw %>%
dplyr::mutate(across(where(is.character), forcats::as_factor))
```
Para se aprofundar no tema, recomendamos a leitura do Capítulo [15 Factors](https://r4ds.had.co.nz/factors.html) de Wickham & Grolemund [-@wickham2017].
## lubridate
O pacote `lubridate` fornece um conjunto de funções para a manipulação de dados de data e horário. Dessa forma, esse pacote facilita a manipulação dessa classe de dado no R, pois geralmente esses dados não são intuitivos e mudam dependendo do tipo de objeto de data e horário. Além disso, os métodos que usam datas e horários devem levar em consideração fusos horários, anos bissextos, horários de verão, além de outras particularidades. Existem diversas funções nesse pacote, sendo as mesmas focadas em: i) transformações de data/horário, ii) componentes, iii) arredondamentos, iv) durações, v) períodos, vi) intervalos, além de muitas outras funções específicas.
Todas as funções deste pacote são listadas na [página de referência](https://lubridate.tidyverse.org/reference/index.html) do pacote.
Apesar de estar inserido no escopo do *tidyverse*, este pacote não é carregado com os demais, requisitando seu carregamento solo.
```{r}
## Carregar
library(lubridate)
```
Existem três tipos de dados data/horário:
- **Data**: tempo em dias, meses e anos `<date>`
- **Horário**: tempo dentro de um dia `<time>`
- **Data-horário**: tempo em um instante (data mais tempo) `<dttm>`
Para trabalhar exclusivamente com horários, podemos utilizar o pacote `hms`.
É fundamental também destacar que algumas letras terão um significado temporal, sendo abreviações de diferentes períodos em inglês: **y**ear (ano), **m**onth (mês), **w**eak (semana), **d**ay (dia), **h**our (hora), **m**inute (minuto), e **s**econd (segundo).
Para acessar a informação da data e horários atuais podemos utilizar as funções `lubridate::today()` e `lubridate::now()`.
```{r}
## Extrair a data nesse instante
lubridate::today()
## Extrair a data e tempo nesse instante
lubridate::now()
```
Além dessas informações instantâneas, existem três maneiras de criar um dado de data/horário.
- De um string
- De componentes individuais de data e horário
- De um objeto de data/horário existente
Os dados de data/horário geralmente estão no formato de strings. Podemos transformar os dados especificando a ordem dos seus componentes, ou seja, a ordem em que ano, mês e dia aparecem no string, usando as letras `y` (ano), `m` (mês) e `d` (dia) na mesma ordem, por exemplo, `lubridate::dmy()`.
```{r}
## Strings e números para datas