-
Notifications
You must be signed in to change notification settings - Fork 7
/
072-dplyr.Rmd
747 lines (495 loc) · 25.1 KB
/
072-dplyr.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
## O pacote dplyr {#dplyr}
O `dplyr` é o pacote mais útil para realizar transformação de dados, aliando simplicidade e eficiência de uma forma elegante. Os scripts em R que fazem uso inteligente dos verbos `dplyr` e as facilidades do operador _pipe_ tendem a ficar mais legíveis e organizados, sem perder velocidade de execução.
As principais funções do `dplyr` são:
- `select()` - seleciona colunas
- `arrange()` - ordena a base
- `filter()` - filtra linhas
- `mutate()` - cria/modifica colunas
- `group_by()` - agrupa a base
- `summarise()` - sumariza a base
Todas essas funções seguem as mesmas características:
- O _input_ é sempre uma `tibble` e o _output_ é sempre uma `tibble`.
- Colocamos a `tibble` no primeiro argumento e o que queremos fazer nos outros argumentos.
- A utilização é facilitada com o emprego do operador `%>%`.
As principais vantagens de se usar o `dplyr` em detrimento das funções do R base são:
- Manipular dados se torna uma tarefa muito mais simples.
- O código fica mais intuitivo de ser escrito e mais simples de ser lido.
- O pacote `dplyr` utiliza `C` e `C++` por trás da maioria das funções, o que geralmente torna o código mais rápido.
- É possível trabalhar com diferentes fontes de dados, como bases relacionais (SQL) e `data.table`.
Se você ainda não tiver o `dplyr` instalado, rode o código abaixo.
```{r, eval = FALSE}
install.packages("dplyr")
library(dplyr)
```
Neste capítulo, vamos trabalhar com uma base de filmes do IMDB. Essa base pode ser baixada [clicando aqui](https://github.com/curso-r/livro-material/raw/master/assets/data/imdb.rds).
Assim, utilizaremos o objeto `imdb` para acessar os dados.
```{r, include=FALSE}
library(dplyr)
imdb <- readr::read_rds("assets/data/imdb.rds")
```
```{r, eval = FALSE}
imdb <- readr::read_rds("imdb.rds")
imdb
```
```{r, echo = FALSE}
imdb
```
Agora, vamos avaliar com mais detalhes as principais funções do pacote `dplyr`.
### Selecionando colunas
Para selecionar colunas, utilizamos a função `select()`.
O primeiro argumento da função é a base de dados e os demais argumentos são os nomes das colunas que você gostaria de selecionar. Repare que você não precisa colocar o nome da coluna entre aspas.
```{r}
select(imdb, titulo)
```
Você também pode selecionar várias colunas.
```{r}
select(imdb, titulo, ano, orcamento)
```
O operador `:` é muito útil para selecionar colunas consecutivas.
```{r}
select(imdb, titulo:generos)
```
O `dplyr` possui um conjunto de funções auxiliares muito úteis para seleção de colunas. As principais são:
- `starts_with()`: para colunas que começam com um texto padrão
- `ends_with()`: para colunas que terminam com um texto padrão
- `contains()`: para colunas que contêm um texto padrão
Selecionamos a seguir todas as colunas que começam com o texto "num".
```{r}
select(imdb, starts_with("num"))
```
Para retirar colunas da base, base acrescentar um `-` antes da seleção.
```{r}
select(imdb, -ano, -direcao)
select(imdb, -starts_with("num"))
```
#### Exercícios {-}
Utilize a base `imdb` nos exercícios a seguir.
**1.** Teste aplicar a função `glimpse()` do pacote `{dplyr}` à base `imdb`. O que ela faz?
**2.** Crie uma tabela com apenas as colunas `titulo`, `direcao`, e `orcamento.` Salve em um objeto chamado `imdb_simples`.
**3.** Selecione apenas as colunas `duracao`, `direcao`, `descricao` e `producao` usando a função auxiliar `contains()`.
**4.** Usando a função `select()` (e suas funções auxiliares), escreva códigos que retornem a base IMDB sem as colunas `num_avaliacoes`, `num_criticas_publico` e `num_criticas_critica`. Escreva todas as soluções diferentes que você conseguir pensar.
### Ordenando a base
Para ordenar linhas, utilizamos a função `arrange()`. O primeiro argumento é a base de dados. Os demais argumentos são as colunas pelas quais queremos ordenar as linhas. No exemplo a seguir, ordenamos as linhas da base por ordem crescente de orçamento.
```{r}
arrange(imdb, orcamento)
```
Também podemos ordenar de forma decrescente usando a função `desc()`.
```{r}
arrange(imdb, desc(orcamento))
```
E claro, ordenar segundo duas ou mais colunas.
```{r}
arrange(imdb, desc(ano), desc(orcamento))
```
#### Exercícios {-}
Utilize a base `imdb` nos exercícios a seguir.
**1.** Ordene os filmes em ordem crescente de `ano` e decrescente de `receita` e salve em um objeto chamado `filmes_ordenados`.
**2.** Selecione apenas as colunas `titulo` e `orcamento` e então ordene de forma decrescente pelo `orcamento.`
### O pipe em ação
Na grande maioria dos casos, vamos aplicar mais de uma função de manipulação em uma base para obtermos a tabela que desejamos. Poderíamos, por exemplo, querer uma tabela apenas com o título e ano dos filmes, ordenada de forma crescente de lançamento. Para fazer isso, poderíamos aninhar as funções
```{r}
arrange(select(imdb, titulo, ano), ano)
```
ou criar um objeto intermediário
```{r}
tab_titulo_ano <- select(imdb, titulo, ano)
arrange(tab_titulo_ano, ano)
```
Os dois códigos funcionam e levam ao mesmo resultado, mas não são muito bons.
A primeira alternativa é ruim de escrever (já que precisamos escrever primeiro a função que roda por último) e de ler (pois é difícil identificar qual argumento pertence a qual função).
A segunda alternativa também é ruim, pois exige a criação de objetos auxiliares. Se quiséssimos aplicar 10 operações na base, precisaríamos criar 9 objetos intermediários.
A solução para aplicar diversas operações de manipulação em uma base de dados é aplicar o operador pipe: `%>%`.
```{r}
imdb %>%
select(titulo, ano) %>%
arrange(ano)
```
O que está sendo feito no código com pipe? Da primeira para a segunda linha, estamos aplicando a função `select()` à base imdb. Da segunda para a terceira, estamos aplicando a função `arrange()` à base resultante da função `select()`.
O resultado desse código é idêntico às tentativas sem pipe, com a vantagem de termos escrito o código na ordem em que as funções são aplicadas, de termos um código muito mais legível e de não precisarmos utilizar objetos intermediários.
### Filtrando linhas
Para filtrar valores de uma coluna da base, utilizamos a função `filter()`.
```{r}
imdb %>% filter(nota_imdb > 9)
```
Podemos selecionar apenas as colunas título e nota para visualizarmos as notas:
```{r}
imdb %>%
filter(nota_imdb > 9) %>%
select(titulo, nota_imdb)
```
Podemos estender o filtro para duas ou mais colunas. Para isso, separamos cada operação por uma vírgula.
```{r}
imdb %>% filter(ano > 2010, nota_imdb > 8.5)
```
Também podemos fazer operações com as colunas da base dentro da função filter. O código abaixo devolve uma tabela apenas com os filmes que lucraram.
```{r}
imdb %>% filter(receita - orcamento > 0)
```
Naturalmente, podemos filtrar colunas categóricas. O exemplo abaixo retorna uma tabela apenas com os filmes dirigidos por Quentin Tarantino ou Steven Spielberg.
```{r}
imdb %>%
filter(direcao %in% c("Quentin Tarantino", "Steven Spielberg"))
```
Para filtrar textos sem correspondência exata, podemos utilizar a função auxiliar `str_detect()` do pacote `{stringr}`. Ela serve para verificar se cada string de um vetor contém um determinado padrão de texto.
```{r}
library(stringr)
str_detect(
string = c("a", "aa","abc", "bc", "A", NA),
pattern = "a"
)
```
Podemos utilizá-la para filtrar apenas os filmes que contêm o gênero Drama.
```{r}
# A coluna gêneros apresenta todos os gêneros dos filmes concatenados
imdb$generos[1:6]
# Podemos detectar se o gênero Drama aparece na string
str_detect(
string = imdb$generos[1:6],
pattern = "Drama"
)
# Aplicamos essa lógica dentro da função filter, para a coluna completa
imdb %>% filter(str_detect(generos, "Drama"))
```
#### Exercícios {-}
Utilize a base `imdb` nos exercícios a seguir.
**1.** Crie um objeto chamado `filmes_ingles` apenas com filmes que sejam apenas no idioma inglês (`English`).
**2.** Crie um objeto chamado `curtos_legais` com filmes de 90 minutos ou menos de duração e nota no imdb maior do que 8.5.
**3.** Retorne tabelas (`tibbles`) apenas com:
- **a.** filmes de ação anteriores a 1950;
- **b.** filmes dirigidos por "Woody Allen" ou "Wes Anderson";
- **c.** filmes do "Steven Spielberg" ordenados de forma decrescente por ano, mostrando apenas as colunas `titulo` e `ano`;
- **d.** filmes que tenham "Action" **ou** "Comedy" entre os seus gêneros;
- **e.** filmes que tenham "Action" **e** "Comedy" entre os seus gêneros e tenha `nota_imdb` maior que 8;
- **f.** filmes que não possuem informação tanto de receita quanto de orçamento (isto é, possuem `NA` em ambas as colunas).
### Modificando e criando novas colunas
Para modificar uma coluna existente ou criar uma nova coluna, utilizamos a função `mutate()`. O código abaixo divide os valores da coluna duração por 60, mudando a unidade de medida dessa variável de minutos para horas.
```{r}
imdb %>% mutate(duracao = duracao/60)
```
Também poderíamos ter criado essa variável em uma nova coluna. Repare que a nova coluna `duracao_horas` é colocada no final da tabela.
```{r}
imdb %>% mutate(duracao_horas = duracao/60)
```
Podemos fazer qualquer operação com uma ou mais colunas. A única regra é que o resultado da operação retorne um vetor com comprimento igual ao número de linhas da base (ou com comprimento 1 para distribuir um mesmo valor em todas as linhas). Você também pode criar/modificar quantas colunas quiser dentro de um mesmo `mutate`.
```{r}
imdb %>%
mutate(lucro = receita - orcamento, pais = "Estados Unidos") %>%
select(titulo, lucro, pais)
```
#### Exercícios {-}
Utilize a base `imdb` nos exercícios a seguir.
**1.** Crie uma coluna chamada `prejuizo` (`orcamento - receita`) e salve a nova tabela em um objeto chamado `imdb_prejuizo`. Em seguida, filtre apenas os filmes que deram prejuízo e ordene a tabela por ordem crescente de prejuízo.
**2.** Fazendo apenas uma chamada da função mutate(), crie as seguintes colunas novas na base `imdb`:
- **a.** `lucro = receita - orcamento`
- **b.** `lucro_medio`
- **c.** `lucro_relativo = (lucro - lucro_medio)/lucro_medio`
- **d.** `houve_lucro = ifelse(lucro > 0, "sim", "não")`
**3.** Crie uma nova coluna que classifique o filme em `"recente"` (posterior a 2000) e `"antigo"` (de 2000 para trás).
### Sumarizando a base
Sumarização é a técnica de se resumir um conjunto de dados utilizando alguma métrica de interesse. A média, a mediana, a variância, a frequência, a proporção, por exemplo, são tipos de sumarização que trazem diferentes informações sobre uma variável.
Para sumarizar uma coluna da base, utilizamos a função `summarize()`. O código abaixo resume a coluna orçamento pela sua média.
```{r}
imdb %>% summarize(media_orcamento = mean(orcamento, na.rm = TRUE))
```
Repare que a saída da função continua sendo uma tibble.
Podemos calcular diversas sumarizações diferentes em um mesmo `summarize`. Cada sumarização será uma coluna da nova base.
```{r}
imdb %>% summarise(
media_orcamento = mean(orcamento, na.rm = TRUE),
mediana_orcamento = median(orcamento, na.rm = TRUE),
variancia_orcamento = var(orcamento, na.rm = TRUE)
)
```
E também sumarizar diversas colunas.
```{r}
imdb %>% summarize(
media_orcamento = mean(orcamento, na.rm = TRUE),
media_receita = mean(receita, na.rm = TRUE),
media_lucro = mean(receita - orcamento, na.rm = TRUE)
)
```
Muitas vezes queremos sumarizar uma coluna agrupada pelas categorias de uma segunda coluna. Para isso, além do `summarize`, utilizamos também a função `group_by()`.
O código a seguir calcula a receita média dos filmes para cada categoria da coluna "cor".
```{r}
imdb %>%
filter(!is.na(producao), !is.na(receita)) %>%
group_by(producao) %>%
summarise(receita_media = mean(receita, na.rm = TRUE))
```
A única alteração que a função `group_by()` faz na base é a marcação de que a base está agrupada.
```{r}
imdb %>% group_by(producao)
```
#### Exercícios {-}
Utilize a base `imdb` nos exercícios a seguir.
**1.** Calcule a duração média e mediana dos filmes da base.
**2.** Calcule o lucro médio dos filmes com duração maior que 2 horas (ou seja, 120 minutos).
**3.** Apresente na mesma tabela o lucro médio dos filmes com duracao menor que 120 minutos e o lucro médio dos filmes com duracao maior ou igual a 120 minutos.
**4.** Retorne tabelas (`tibbles`) apenas com:
- **a.** a nota IMDB média dos filmes por ano de lançamento;
- **b.** a receita média e mediana dos filmes por ano;
- **c.** apenas o nome das pessoas que dirigiram mais de 10 filmes.
### Juntando duas bases
Podemos juntar duas tabelas a partir de uma (coluna) chave utilizando a função `left_join()`. Como exempo, vamos inicialmente calcular o lucro médio dos filmes de cada direção e salvar no objeto `tab_lucro_direcao`.
```{r}
tab_lucro_direcao <- imdb %>%
group_by(direcao) %>%
summarise(lucro_medio = mean(receita - orcamento, na.rm = TRUE))
tab_lucro_direcao
```
E se quisermos colocar essa informação na base original? Basta usar a função `left_join()` utilizando a coluna `direcao` como chave. Observe que a coluna `lucro_medio` aparece agora no fim da tabela.
```{r}
imdb_com_lucro_medio <- left_join(imdb, tab_lucro_direcao, by = "direcao")
imdb_com_lucro_medio
```
Na tabela `imdb_com_lucro_medio`, como na tabela `imdb`, cada linha continua a representar um filme diferente, mas agora temos também a informação do lucro médio da direção de cada filme.
A primeira linha, por exemplo, traz as informações do filme Avatar. O valor do `lucro_medio` nessa linha representa o lucro médio de todos os filmes do James Cameron, que é o diretor de Avatar. Com essa informação, podemos calcular o quanto o lucro do Avatar se afasta do lucro médio do James Cameron.
```{r}
imdb_com_lucro_medio %>%
mutate(
lucro = receita - orcamento,
lucro_relativo = (lucro - lucro_medio)/lucro_medio,
lucro_relativo = scales::percent(lucro_relativo)
) %>%
select(titulo, direcao, lucro, lucro_medio, lucro_relativo)
```
Observamos então que o Avatar obteve um lucro aproximadamente 169% maior que a média dos filmes do James Cameron.
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('assets/img/manipulacao/joins.png')
```
#### Exercícios {-}
**1.** Utilize a base `imdb` para resolver os itens a seguir.
**a.** Salve em um novo objeto uma tabela com a
nota média dos filmes de cada direção. Essa tabela
deve conter duas colunas (`direcao` e `nota_imdb_media`)
e cada linha deve ser uma direção diferente.
**b.** Use o `left_join()` para trazer a coluna
`nota_imdb_media` da tabela do item anterior
para a tabela `imdb` original.
### dplyr 1.0
A versão 1.0 do pacote `dplyr` foi oficialmente lançada em junho de 2020 e contou com diversas novidades. Vamos falar das principais mudanças:
- A nova função `across()`, que facilita aplicar uma mesma operação em várias colunas.
- A repaginada função `rowwise()`, para fazer operações por linha.
- Novas funcionalidades das funções `select()` e `rename()` e a nova função `relocate()`.
Para trabalhar essas funções, vamos utilizar a base `casas` do pacote `dados`. Para instalar esse pacote, rode os códigos abaixo:
```{r, eval = FALSE}
install.packages("remotes")
remotes::install_github("cienciadedatos/dados")
```
Para trazer os dados para o nosso *environment*, podemos rodar:
```{r}
casas <- dados::casas
```
A base `casas` possui dados de venda de casas na cidade de Ames, nos Estados Unidos. São 2930 linhas e 77 colunas, sendo que cada linha corresponde a uma casa vendida e cada coluna a uma característica da casa ou da venda. Essa versão é uma tradução da base original, que pode ser encontrada no pacote `AmesHousing`:
```{r, eval = FALSE}
install.packages("AmesHousing")
data(ames_raw, package = "AmesHousing")
```
#### A função `across()` {-}
A função `across()` substitui a família de verbos `_all()`, `_if` e `_at()`. A ideia é facilitar a aplicação de uma operação a diversas colunas da base. Para sumarizar a base para mais de uma variável, antigamente poderíamos fazer:
```{r}
casas %>%
group_by(geral_qualidade) %>%
summarise(
lote_area_media = mean(lote_area, na.rm = TRUE),
venda_valor_medio = mean(venda_valor, na.rm = TRUE)
)
```
Ou então utilizar a função `summarise_at()`:
```{r}
casas %>%
group_by(geral_qualidade) %>%
summarise_at(
.vars = vars(lote_area, venda_valor),
list(mean),
na.rm = TRUE
)
```
Com a nova função `across()`, fazemos:
```{r}
casas %>%
group_by(geral_qualidade) %>%
summarise(across(
.cols = c(lote_area, venda_valor),
.fns = mean,
na.rm = TRUE
))
```
A sintaxe é parecida com a função `summarise_at()`, mas agora não precisamos mais usar a função `vars()` e nem usar `list(nome_da_funcao)`para definir a função aplicada nas colunas.
Usando `across()`, podemos facilmente aplicar uma função em todas as colunas da nossa base. Abaixo, calculamos o número de valores distintos para todas as variáveis da base `casas`. O padrão do parâmetro `.cols` é `everything()`, que representa "todas as colunas".
```{r}
casas %>%
summarise(across(.fns = n_distinct))
```
Para fazer essa mesma operação, antes utilizaríamos a função `summarise_all()`.
```{r}
casas %>%
summarise_all(.funs = ~n_distinct(.x))
```
Se quisermos selecionar as colunas a serem modificadas a partir de um teste lógico, utilizamos o ajudante `where()`.
No exemplo abaixo, calculamos o número de valores distintos das colunas de categóricas.
```{r}
casas %>%
summarise(across(where(is.character), n_distinct))
```
Todas as colunas da base resultante eram colunas com classe `character` na base `casas`.
Anteriormente, utilizávamos a função `summarise_if()`.
```{r}
casas %>%
summarise_if(is.character, n_distinct)
```
Com o `across()`, podemos combinar os efeitos de um `summarise_if()` e um `summarise_at()` em um único `summarise()`. A seguir, calculamos as áreas médias, garantindo que pegamos apenas variáveis numéricas.
```{r}
casas %>%
summarise(across(where(is.numeric) & contains("_area"), mean, na.rm = TRUE))
```
Além disso, com a função `across()`, podemos fazer sumarizações complexas que não seria possível utilizando apenas as funções `summarise()`, `summarise_if()` e `summarise_at()`. No exemplo a seguir, calculamos a média das áreas, o número de `NAs` de variáveis categóricas e o número de casas para cada tipo de fundação. Tudo em um mesmo `summarise()`!
```{r}
casas %>%
group_by(fundacao_tipo) %>%
summarise(
across(where(is.numeric) & contains("area"), mean, na.rm = TRUE),
across(where(is.character), ~sum(is.na(.x))),
n_obs = n(),
) %>%
select(1:4, n_obs)
```
Embora a nova sintaxe, usando `across()`, não seja muito diferente do que fazíamos antes, realizar sumarizações complexas não é a única vantagem desse novo *framework*.
O `across()` pode ser utilizado em todos os verbos do `{dplyr}` (com exceção do `select()` e `rename()`, já que ele não trás vantagens com relação ao que já podia ser feito) e isso unifica o modo de fazermos essas operações no `dplyr`. Em vez de termos uma família de funções para cada verbo, temos agora apenas o próprio verbo e a função `across()`.
Vamos ver um exemplo para o `mutate()` e para o `filter()`.
O código abaixo transforma todas as variáveis que possuem "area" no nome, passando os valores de pés quadrados para metros quadrados.
```{r, eval = FALSE}
casas %>%
mutate(across(
contains("area"),
~ .x / 10.764
))
```
Já o código a seguir filtra apenas as casas que possuem varanda aberta, cerca e lareira (o `NA` nessa base significa que a casa não possui tal característica).
```{r, eval = FALSE}
casas %>%
filter(across(
c(varanda_aberta_area, cerca_qualidade, lareira_qualidade),
~!is.na(.x)
))
```
Não precisamos do `across()` na hora de selecionar colunas. A função `select()` já usa naturalmente o mecanismo de seleção de colunas que o `across()` proporciona.
```{r}
casas %>%
select(where(is.numeric))
```
O mesmo vale para o `rename()`. Se quisermos renomer várias colunas, a partir de uma função, utilizamos o `rename_with()`.
```{r}
casas %>%
rename_with(toupper, contains("venda"))
```
#### A função `relocate()` {-}
O `{dplyr}` possui agora uma função própria para reorganizar colunas: `relocate()`. Por padrão, ela coloca uma ou mais colunas no começo da base.
```{r}
casas %>%
relocate(venda_valor, venda_tipo)
```
Podemos usar os argumentos `.after` e `.before` para fazer mudanças mais complexas.
O código baixo coloca a coluna `venda_ano` depois da coluna `construcao_ano`.
```{r, eval = FALSE}
casas %>%
relocate(venda_ano, .after = construcao_ano)
```
O código baixo coloca a coluna `venda_ano` antes da coluna `construcao_ano`.
```{r, eval = FALSE}
casas %>%
relocate(venda_ano, .before = construcao_ano)
```
#### A função `rowwise()` {-}
Por fim, vamos discutir operações feitas por linha. Tome como exemplo a tabela abaixo. Ela apresenta as notas de alunos em quatro provas.
```{r}
tab_notas <- tibble(
student_id = 1:5,
prova1 = sample(0:10, 5),
prova2 = sample(0:10, 5),
prova3 = sample(0:10, 5),
prova4 = sample(0:10, 5)
)
tab_notas
```
Se quisermos gerar uma coluna com a nota média de cada aluno nas quatro provas, não poderíamos usar o `mutate()` diretamente.
```{r}
tab_notas %>% mutate(media = mean(c(prova1, prova2, prova3, prova4)))
```
Neste caso, todas as colunas estão sendo empilhadas e gerando uma única média, passada a todas as linhas da coluna `media`.
Para fazermos a conta para cada aluno, podemos agrupar por aluno. Agora sim a média é calculada apenas nas notas de cada estudante.
```{r}
tab_notas %>%
group_by(student_id) %>%
mutate(media = mean(c(prova1, prova2, prova3, prova4)))
```
Também podemos nos aproveitar da sintaxe do `across()` neste caso. Para isso, precisamos substutir a função `c()` pela função `c_across()`.
```{r}
tab_notas %>%
group_by(student_id) %>%
mutate(media = mean(c_across(starts_with("prova"))))
```
Equivalentemente ao `group_by()`, neste caso, podemos usar a função `rowwise()`.
```{r}
tab_notas %>%
rowwise(student_id) %>%
mutate(media = mean(c_across(starts_with("prova"))))
```
Ela é muito útil quando queremos fazer operação por linhas, mas não temos uma coluna de identificação. Por padrão, se não indicarmos nenhuma coluna, cada linha será um "grupo".
```{r}
tab_notas %>%
rowwise() %>%
mutate(media = mean(c_across(starts_with("prova"))))
```
Veja que `student_id` não é passada para a função `rowwise()`. Não precisaríamos dessa coluna na base para reproduzir a geração da columa `media` neste caso.
#### Exercícios {-}
A base `casas` abaixo pode ser encontrada a partir do código abaixo:
```{r, eval = FALSE}
remotes::install_github("cienciadedatos/dados")
dados::casas
```
**1.** Reescreva os códigos abaixo utilizando as funções `across()` e `where()`.
**a.**
```{r, eval = FALSE}
casas %>%
group_by(geral_qualidade) %>%
summarise(
acima_solo_area_media = mean(acima_solo_area, na.rm = TRUE),
garagem_area_media = mean(garagem_area, na.rm = TRUE),
valor_venda_medio = mean(venda_valor, na.rm = TRUE)
)
```
**b.**
```{r, eval = FALSE}
casas %>%
filter_at(
vars(porao_qualidade, varanda_fechada_area, cerca_qualidade),
~!is.na(.x)
)
```
**c.**
```{r, eval = FALSE}
casas %>%
mutate_if(is.character, ~tidyr::replace_na(.x, replace = "Não possui"))
```
**2.** Utilizando a base `casas`, resolva os itens a seguir.
- **a.** Usando o `case_when()` crie um código para categorizar a variável venda_valor da seguinte maneira:
- **barata**: \$0 a \$129.500
- **preço mediano**: \$129.500 a \$180.796
- **cara**: \$ 180.796 a \$213.500
- **muito cara**: maior que \$213.500
<div style = "height: 10px;"></div>
- **b.** Utilize o código feito na letra (a) para agrupar a base `casas` pela variável venda_valor categorizada e calcular todas as áreas médias para cada uma dessas categorias.
**3.** Escreva um código que receba a base `casas` e retorne uma tabela com apenas
- **a.** as colunas referentes à garagem da casa.
- **b.** as colunas referentes a variáveis de qualidade.
- **c.** colunas numéricas que representam áreas da casa e do terreno.
- **d.** colunas numéricas.
- **e.** colunas referentes à piscina, porão e o valor de venda.
**4.** Usando a função `rename_with()`, troque todos os `"_"` dos nomes das colunas por um espaço `" "`.
**5.** Escreva um código para colocar todas as colunas relativas a venda no começo da base `casas`.
**6.** 5. Escreva um código para colocar todas as colunas numéricas da base `casas` no começo da tabela e todas as colunas categóricas no final.