Apostila Unidade 4 - Curso R
Apostila Unidade 4 - Curso R
Apostila Unidade 4 - Curso R
129
130
library(dplyr)
library(tidyverse)
Logo, você precisa chamar pelo pacote com a função library() para ter acesso a estas funções.
Dentre os vários pacotes tragos pelo tidyverse, está o dplyr. Portanto, você também pode acessar
as funções descritas aqui, ao chamar pelo pacote tidyverse. Mas antes de entrarmos nas funções
do pacote dplyr, vou introduzílo ao pipe.
Quando você está aplicando várias etapas e transformações em sequência sobre os seus dados, caso
você não utilizar o operador pipe (%>%) você tem basicamente duas opções: salvar os resultados
intermediários de suas operações em objetos intermediários; ou agrupar as funções de cada etapa
em uma só.
Vamos começar com a primeira opção, ao salvar os resultados intermediários. Vamos supor que
você esteja pegando os dados da base mpg, que contém dados sobre o consumo de vários modelos
de carro. O seu objetivo é calcular a média de quilômetros por litro (hwy) de cada classe (class) de
carro.
Para isso, você tem que: primeiro, agrupar a base por cada classe de carro (com a função
group_by()), e depois, utilizar a função summarise() para calcular a média de cada grupo.
Suponha que você queira ainda ordenar (com a função arrange()) essa tabela gerada, para que
você veja rapidamente quais são as 10 maiores médias.
Temos então 3 etapas diferentes. Porém, sem o uso do pipe, para realizarmos cada uma delas temos
que salvar o resultado da etapa anterior em algum objeto, antes de passarmos para a próxima. Logo,
o código ficaria dessa maneira:
arrange(tabela_final, desc(Media))
## # A tibble: 7 x 2
## class Media
## <chr> <dbl>
## 1 compact 28.3
## 2 subcompact 28.1
## 3 midsize 27.3
## 4 2seater 24.8
## 5 minivan 22.4
## 6 suv 18.1
## 7 pickup 16.9
Essa alternativa, gera um código claro, onde as etapas estão separadas umas das outras, e com isso,
fica fácil de se entender o que cada uma delas faz. Porém, o código é “verboso” (ou seja, você
escreve mais do que o necessário para realizar uma mesma ação), além de que você está gastando
desnecessariamente a memória do seu computador. Com o uso do pipe você não precisa salvar os
resultados intermediários (logo, você está economizando memória), além de aumentar ainda mais
a clareza de seu código.
Agora, vamos para a segunda alternativa, que é agrupar as etapas em uma função só. Essa alternativa
é o seu pior cenário, pois veja o código abaixo, e tente entender o que exatamente ele faz.
arrange(
summarise(
.data = group_by(
.data = mpg,
class
),
Media = mean(hwy)
),
desc(Media)
)
Depois de uns minutos olhando para ele, você talvez tenha identificado que ele faz exatamente o
mesmo trabalho do código anterior em que utilizamos a primeira alternativa. Entretanto, caso você
não tenha entendido o que este código faz, você com certeza percebeu que ele está muito mais
confuso, e muito mais difícil de se ler.
Por isso essa segunda alternativa é ruim. Ela evita que você salve resultados intermediários, pois
nessas situações, o R irá se preocupar em calcular uma função de cada vez, começando pela função
mais “interna”, indo em direção a função mais “externa”. Mas essa alternativa gera um código
muito difícil de se ler, pois você tem que lêlo de “dentro para fora”.
Ou seja, você primeiro deve encontrar a função mais “interna” dessa cadeia de código. No caso
acima, é a função group_by(), que está pegando a base mpg, agrupando pela variável class. O
resultado group_by() vai para dentro da função summarise(), que calcula a média de cada grupo
da variável class. Em seguida, o resultado de summarise() é ordenado por arrange().
O operador %>% funciona como uma “ponte”. Ele pega o resultado da função que está antes dele, e
insere esse resultado como o primeiro argumento da próxima função. Veja o exemplo abaixo:
Tudo que o pipe está fazendo é pegando o valor do objeto y e inserindo como o primeiro argumento
da função f(), em seguida, ele pega o resultado de f() e insereo como primeiro argumento da
função g(). Isso seria equivalente a escrevermos g(f(y)). Para ter acesso ao operador pipe, você
precisa do pacote magrittr, portanto, lembrese de chamar por ele através da função library().
Caso você tenha o pacote tidyverse, ele já contém o pacote magrittr inserido, logo, você também
pode chamar por ele.
Tendo isso em mente, poderíamos reescrever o código anterior de forma mais suscinta e muito mais
clara. Para criar um pipe você pode escrevêlo na mão (%>%), ou caso você esteja trabalhando no RS
tudio, você pode utilizar o atalho Ctrl + Shift + M, que um pipe será gerado automaticamente.
Veja o código abaixo:
tabela_final
## # A tibble: 7 x 2
## class Media
## <chr> <dbl>
## 1 compact 28.3
## 2 subcompact 28.1
## 3 midsize 27.3
## 4 2seater 24.8
## 5 minivan 22.4
## 6 suv 18.1
## 7 pickup 16.9
O primeiro argumento de todas as funções acima, se trata da base de dados utilizada pela função em
seus cálculos. Logo, no código acima, estamos pegando a base mpg, inserindoa em group_by(),
para que ele agrupe essa base pela variável class; depois inserimos o resultado agrupado em
summarise(), para que ele calcule a coluna Media que contém a média de quilometragem (hwy)
de cada um destes grupos; e por último, o pipe insere o resultado em arrange(), para que a função
reordene a base de forma decrescente, se baseando nos valores da coluna Media.
Dessa forma, temos o melhor de ambas alternativas. Economizamos memória, pois não precisamos
salvar os resultados intermediários, e o código gerado, além de suscinto é muito claro quanto a sua
intenção. Apesar destes benefícios que o pipe traz ao seu trabalho, você nem sempre consegue
utilizálo. Principalmente porque muitas funções não possuem como primeiro argumento, a base
de dados utilizada pela função.
Um exemplo simples é a função de regressão linear lm(), onde o primeiro argumento é a fórmula,
ou a equação que o cálculo da regressão deve seguir. A base de dados sobre a qual a função irá
calcular, se encontra no segundo argumento da função (data). Neste caso, você pode utilizar o
ponto final “.” como uma forma de contornar esta situação. O que o ponto faz, é definir em qual
argumento o resultado transportado pelo pipe, deve ser inserido.
No exemplo abaixo, estou pegando a base flights que contém dados de diversos voôs realiza
dos em um aeroporto de Nova York. A intenção, é calcularmos uma regressão linear onde temos
distância (coluna distance) como variável independente, e o tempo de atraso na chegada (coluna
arr_delay) como a variável dependente. Logo, utilizo o ponto “.” na função lm() para inserir
a base flights no argumento data da função, e levar o resultado dessa expressão para a função
summary() que fica responsável por nos mostrar as estatísticas geradas pelo modelo.
library(nycflights13)
flights %>%
lm(arr_delay ~ distance, data = .) %>%
summary()
##
## Call:
## lm(formula = arr_delay ~ distance, data = .)
##
## Residuals:
## Min 1Q Median 3Q Max
## -87.20 -24.03 -11.84 7.19 1279.87
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 10.8291976 0.1355211 79.91 <0.0000000000000002 ***
## distance -0.0037523 0.0001058 -35.47 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 44.55 on 327344 degrees of freedom
## (9430 observations deleted due to missingness)
## Multiple R-squared: 0.003828, Adjusted R-squared: 0.003825
## F-statistic: 1258 on 1 and 327344 DF, p-value: < 0.00000000000000022
tidyverse. Logo, para ter acesso a esta função, basta chamar por um destes dois pacotes com
library().
A função filter() possui dois argumentos principais. A base de dados a ser filtrada (.data), e as
condições que a função deve seguir ao filtrar a base (...). Para criar as condições de filtro, você
deve utilizar os operadores lógicos, e por isso, deixei abaixo uma tabela descrevendo cada um deles:
Operador Descrição
< menor que
<= menor ou igual a
> maior que
>= maior ou igual a
== exatamente igual
!= não é igual a
!x não se encaixa na condição definida em x
x|y x ou y
x&y xey
x %in% y tudo de x que está incluso em y
Para demostrar essa função, vamos utilizar uma base com várias transferências bancárias (transf).
A coluna Usuário nos mostra qual dos agentes do banco que criou a transferência. Temos também
a data e o horário (Data) da transferência, além de seu país de destino (País).
transf
## # A tibble: 20,006 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-06 22:19:19 Eduardo 599. 116241629 Alemanha NA
## 2 2018-12-06 22:10:34 Júlio 4611. 115586504 Alemanha NA
## 3 2018-12-06 21:59:50 Nathália 4418. 115079280 Alemanha NA
## 4 2018-12-06 21:54:13 Júlio 2740. 114972398 Alemanha NA
## 5 2018-12-06 21:41:27 Ana 1408. 116262934 Alemanha NA
## 6 2018-12-06 21:18:40 Nathália 5052. 115710402 Alemanha NA
## 7 2018-12-06 20:54:32 Eduardo 5665. 114830203 Alemanha NA
## 8 2018-12-06 20:15:46 Sandra 1474. 116323455 Alemanha NA
## 9 2018-12-06 20:04:35 Armando 8906. 115304382 Alemanha NA
## 10 2018-12-22 20:00:56 Armando 18521. 114513684 Alemanha NA
## # ... with 19,996 more rows
O setor de Compliance do banco, é composto por 3 pessoas (Ana, Júlio Cesar e Armando). Por
tanto, caso você quisesse saber todas as transações autorizadas por esse setor, você poderia filtrar
a base em relação a esses nomes na coluna Usuário. A melhor forma de criar este filtro, é uti
lizando o operador %in%. Com ele, a função filter() irá procurar todas as linhas onde o valor
na coluna Usuário está incluso dentro do objeto compliance. Ao salvar os nomes da equipe do
Compliance em um objeto, você pode utilizálo para se referir novamente a equipe, caso precise em
outro momento de sua análise.
## # A tibble: 7,475 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-06 21:41:27 Ana 1408. 116262934 Alemanha NA
## 2 2018-12-06 20:04:35 Armando 8906. 115304382 Alemanha NA
## 3 2018-12-22 20:00:56 Armando 18521. 114513684 Alemanha NA
## 4 2018-12-06 19:48:39 Ana 4589. 116281690 Alemanha NA
## 5 2018-12-06 18:48:29 Ana 8748. 114255039 Alemanha NA
## 6 2018-12-21 18:46:59 Júlio Cesar 16226. 116279014 Alemanha NA
## 7 2018-12-06 18:45:32 Ana 3285. 115459852 Alemanha NA
## 8 2018-12-06 18:09:15 Júlio Cesar 388. 114894102 Alemanha NA
## 9 2018-12-06 17:58:17 Ana 14890. 115036903 Alemanha NA
## 10 2018-12-06 17:34:49 Armando 5726. 114489509 Alemanha NA
## # ... with 7,465 more rows
Eu posso utilizar o símbolo “!” para localizar as transferências que não se encaixam em uma con
dição lógica específica. Ou seja, o símbolo “!” muda o comportamento de filter(), pois com ele
a função irá pegar as linhas que possuem o valor FALSE para a condição lógica definida, ao invés
de TRUE.
Um exemplo clássico do uso do símbolo “!” nessas condições, é para retirar as linhas que possuem
valores não disponíveis ou vazios (valores NA). A função is.na() aplica um teste lógico sobre
a coluna. Caso a linha dessa coluna possua um valor NA, a função nos retorna TRUE para aquela
linha, porém, caso a linha possua um valor qualquer definido, ela retorna FALSE. Ao colocarmos o
símbolo “!” antes dessa função, o filter() irá procurar pelas linhas que possuem valores FALSE.
tab %>%
filter(!is.na(valor))
## grupo valor
## 1 a 21
## 2 a 14
## 3 b 32
Tendo isso em mente, se eu quisesse saber as transferências que não são do Brasil, eu poderia utilizar
o símbolo “!” na frente da condição lógica que utilizaríamos para sabermos justamente o contrário
(quais são as transferências destinadas para o Brasil).
transf %>%
filter(!Pais == "Brasil")
## # A tibble: 17,822 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-06 22:19:19 Eduardo 599. 116241629 Alemanha NA
## 2 2018-12-06 22:10:34 Júlio 4611. 115586504 Alemanha NA
## 3 2018-12-06 21:59:50 Nathália 4418. 115079280 Alemanha NA
## 4 2018-12-06 21:54:13 Júlio 2740. 114972398 Alemanha NA
## 5 2018-12-06 21:41:27 Ana 1408. 116262934 Alemanha NA
## 6 2018-12-06 21:18:40 Nathália 5052. 115710402 Alemanha NA
## 7 2018-12-06 20:54:32 Eduardo 5665. 114830203 Alemanha NA
## 8 2018-12-06 20:15:46 Sandra 1474. 116323455 Alemanha NA
## 9 2018-12-06 20:04:35 Armando 8906. 115304382 Alemanha NA
## 10 2018-12-22 20:00:56 Armando 18521. 114513684 Alemanha NA
## # ... with 17,812 more rows
Você poderia fazer o mesmo processo, através do símbolo “!=”, que realiza um trabalho diferente
do símbolo “!” no início de uma condição lógica. O símbolo “!=” significa “diferente de”, e ele não
vai alterar o comportamento da função filter(), que portanto, irá filtrar as linhas que possuem
valor TRUE para a condição. Logo, o filter() com este símbolo, irá filtrar todas as linhas que são
diferentes de um valor.
transf %>%
filter(Pais != "Brasil")
## # A tibble: 17,822 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-06 22:19:19 Eduardo 599. 116241629 Alemanha NA
## 2 2018-12-06 22:10:34 Júlio 4611. 115586504 Alemanha NA
Vamos dar um pouco de contexto a nossa análise. Suponha que houve um ataque terrorista em
Berlim no Natal (dia 24 de dezembro) de 2018. Você como parte do compliance de uma instituição
financeira que realiza transferências internacionais, deve ter certeza de que a sua instituição não
colaborou com o terrorismo internacional de alguma forma. Segundo a polícia, a munição usada no
ataque custa em média mais de $15.000, e foi comprada entre os dias 20 e 23.
O seu primeiro instinto, será provavelmente procurar por linhas em nossa base transf que possuem
características como essas. Perceba que são no mínimo três condições que a transferência deve
atender. Primeiro, possuir um valor maior do que 15 mil. Segundo, estar destinada para a Alemanha.
Terceiro, ter sido realizada entre os dias 20 e 23 de dezembro de 2018.
Ou seja, essas condições são dependentes, pois todas elas devem ser satisfeitas ao mesmo tempo.
Para dizermos isso à função filter(), conectamos essas condições lógicas pelo símbolo do laço
(“&”). Vale também destacar o uso da função as.Date() em between(). A coluna Data em
transf é uma coluna de date-time, e utilizo a função as.Date() para extrair apenas a data
da coluna. A função between() é uma forma rápida de criar uma condição lógica de intervalo.
No primeiro argumento, você dá a coluna onde a função deve procurar o intervalo; o segundo
argumento, se trata do limite inferior do intervalo; e o terceiro argumento, o limite superior do
intervalo. No caso de um intervalo de datas, você deve utilizar a função as.Date() sobre os limites
do intervalo, para que a função between() compreenda que se trata de um intervalo de datas.
transf %>%
filter(
Valor > 15000 &
Pais == "Alemanha" &
between(as.Date(Data), as.Date("2018-12-20"), as.Date("2018-12-23"))
)
## # A tibble: 132 x 6
## Data Usuario Valor TransferID Pais Descricao
Bem, são 156 transferências suspeitas, e você terá que conferir os dados dos donos de cada uma
dessas transferências, um baita trabalho. Porém, você acaba lembrando que dentro das regras do
banco, não é exigido do cliente nenhum comprovante de endereço ou de origem dos fundos para
transferências de até $200, apenas a identidade (que pode ser falsificada).
transf %>%
filter(
Valor <= 200 &
Pais == "Alemanha" &
between(as.Date(Data), as.Date("2018-12-20"), as.Date("2018-12-23"))
)
## # A tibble: 5 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-20 00:31:17 Júlio 193 115555598 Alemanha NA
## 2 2018-12-22 06:30:01 Sandra 100 116400001 Alemanha NA
## 3 2018-12-22 06:35:00 Sandra 200 116400002 Alemanha NA
## 4 2018-12-22 06:42:12 Eduardo 200 116400005 Alemanha NA
## 5 2018-12-22 06:55:54 Eduardo 150 116400009 Alemanha NA
Interessante, reduzimos a nossa amostra para cinco transferências. O dono da primeira transferência
é um senhor de 67 anos, que mora no centro de Berlim, e que estava comprando um remédio nada
barato de uma farmácia. Porém, as próximas quatro transferências levantam bastante suspeitas.
As transferências foram de clientes diferentes (mas com poucos minutos de diferença). Ao questio
nar Sandra e Eduardo, eles indicam que todos os clientes apresentaram identidades francesas (será
que os clientes estavam testando quais eram as regras do banco para com identidades francesas?).
Ao pesquisar por mais clientes com identidades francesas você chega a uma estranha transferência
de $20.000 realizada 4 minutos depois da última, e com uma prova de fundos discutível. Talvez o
banco, quem autorizou essas transações, você e seus colegas também estejam em maus lençóis.
transf %>%
inner_join(
identidade,
by = "TransferID"
) %>%
filter(
Pais == "Alemanha" &
between(as.Date(Data), as.Date("2018-12-20"), as.Date("2018-12-23")) &
Identi_Nacion == "França"
)
## # A tibble: 5 x 7
## Data Usuario Valor TransferID Pais Descricao Identi_Nacion
## <dttm> <chr> <dbl> <dbl> <chr> <lgl> <chr>
## 1 2018-12-22 06:30:01 Sandra 100 116400001 Alemanha NA França
## 2 2018-12-22 06:35:00 Sandra 200 116400002 Alemanha NA França
## 3 2018-12-22 06:42:12 Eduardo 200 116400005 Alemanha NA França
## 4 2018-12-22 06:55:54 Eduardo 150 116400009 Alemanha NA França
## 5 2018-12-22 06:59:07 Eduardo 20000 116400010 Alemanha NA França
Portanto, inicialmente no exemplo anterior, estávamos procurando por uma transferência de valor
alto (> $15.000) para a Alemanha e que fosse entre os dias 20 e 23 de dezembro de 2018. As condi
ções dependiam uma da outra, e para que o filter() entenda isso, essas condições são conectadas
pelo operador “&”.
Porém, e se essas condições não dependessem uma da outra? Caso estivéssemos falando por exem
plo, de uma transferência que era maior do que $15.000 OU que foi para a Alemanha OU que foi
feita entre os dias 20 e 23 de dezembro de 2018, o filter() deveria filtrar toda linha que atenda
pelo menos uma dessas condições. Neste caso, devemos separar as condições por um outro operador
lógico, uma barra vertical ( | ).
transf %>%
filter(
Valor > 15000 |
Pais == "Alemanha" |
between(as.Date(Data), as.Date("2018-12-20"), as.Date("2018-12-23"))
)
## # A tibble: 2,444 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-06 22:19:19 Eduardo 599. 116241629 Alemanha NA
## 2 2018-12-06 22:10:34 Júlio 4611. 115586504 Alemanha NA
## 3 2018-12-06 21:59:50 Nathália 4418. 115079280 Alemanha NA
## 4 2018-12-06 21:54:13 Júlio 2740. 114972398 Alemanha NA
## 5 2018-12-06 21:41:27 Ana 1408. 116262934 Alemanha NA
## 6 2018-12-06 21:18:40 Nathália 5052. 115710402 Alemanha NA
## 7 2018-12-06 20:54:32 Eduardo 5665. 114830203 Alemanha NA
## 8 2018-12-06 20:15:46 Sandra 1474. 116323455 Alemanha NA
## 9 2018-12-06 20:04:35 Armando 8906. 115304382 Alemanha NA
## 10 2018-12-22 20:00:56 Armando 18521. 114513684 Alemanha NA
## # ... with 2,434 more rows
Vamos continuar usando o data.frame transf do banco como exemplo. Vamos supor que você
está a procura das transferências realizadas por Nathália do setor de crédito pessoal. Neste caso,
você pode usar o operador “==” que procura valores “iguais a”.
transf %>%
filter(
Usuario == "Nathália"
)
## # A tibble: 2,507 x 6
## Data Usuario Valor TransferID Pais Descricao
## <dttm> <chr> <dbl> <dbl> <chr> <lgl>
## 1 2018-12-06 21:59:50 Nathália 4418. 115079280 Alemanha NA
## 2 2018-12-06 21:18:40 Nathália 5052. 115710402 Alemanha NA
## 3 2018-12-21 17:41:48 Nathália 17583. 115748273 Alemanha NA
## 4 2018-12-06 17:41:45 Nathália 2112. 115975046 Alemanha NA
## 5 2018-12-06 14:55:06 Nathália 2469. 114816281 Alemanha NA
## 6 2018-12-06 13:40:48 Nathália 1213. 116063458 Alemanha NA
## 7 2018-12-06 12:57:36 Nathália 5819. 115237461 Alemanha NA
## 8 2018-12-06 09:37:04 Nathália 1740. 115549066 Alemanha NA
## 9 2018-12-06 09:00:57 Nathália 856. 114781229 Alemanha NA
## 10 2018-12-06 06:34:15 Nathália 7241. 116323536 Alemanha NA
## # ... with 2,497 more rows
Mas quando estamos falando de texto, devemos ter cuidado com este operador, pois ele irá procurar
valores que são “exatamente iguais a”. Qualquer detalhe diferente em um texto, o torna comple
tamente diferente para este operador. Logo “nathália” é completamente diferente de “Nathália”,
mesmo que a primeira letra seja a única diferença. No código abaixo, estou listando todos os dife
rentes nomes que aparecem na coluna Usuário. Veja que temos pelo menos seis formas diferentes
de Nathália ao longo da base de dados.
## # A tibble: 8 x 2
## # Groups: Usuario [8]
## Usuario n
## <chr> <int>
## 1 Sandra 2487
## 2 Nathália 2507
## 3 nathalia 2552
## 4 Júlio Cesar 2529
## 5 Júlio 2533
## 6 Eduardo 2452
## 7 Armando 2468
## # ... with 1 more row
Nestes casos, você pode utilizar um REGEX (regular expression) para encontrar textos próximos
a um padrão. Essa é uma poderosa ferramenta, que está presente em quase todas as linguagens de
programação.
O pacote stringr traz ao R, funções importantes para manipulações de texto, como concatena
ção str_c(), e subset str_sub(). Além de ferramentas para expressões regulares (REGEX),
sendo a função str_detect() a responsável por detectar esses padrões em textos. Esse pacote
provavelmente já lhe satisfaz, e para ter acesso as suas funções, podemos chamar tanto pelo pacote
diretamente, quanto pelo pacote tidyverse.
Talvez você precise de mais transformações e funções de texto. Nestes casos, o pacote stringi traz
um arsenal de funções muito maior do que o stringr. Como disse anteriormente, a principal função
que vamos utilizar dentro do filter(), é a str_detect(). A função possui dois argumentos
principais: string, que é a coluna onde estão os textos pelo qual você irá filtrar; e pattern, que é
o padrão que a função deve encontrar dentro dos textos que você deu a ela.
Ao identificarmos todos os nomes presentes na base, podemos ver melhor quais textos queremos
filtrar, e podemos identificar pelo menos seis versões diferentes de Nathália presentes. Vamos fazer
alguns testes antes:
transf %>%
filter(
str_detect(Usuario, "N")
) %>%
group_by(Usuario) %>%
count()
## # A tibble: 1 x 2
## # Groups: Usuario [1]
## Usuario n
## <chr> <int>
## 1 Nathália 2507
No exemplo acima, o str_detect() detectou todas as linhas, onde o valor na coluna Usuário
possui um “N” maiúsculo em algum lugar da palavra. Vemos acima, que há apenas dois nomes que
possuem essa letra, ao longo de toda a base. Nomes como “nathalia” foram descartados, por não
possuírem o “N” maiúsculo em algum lugar.
Podemos utilizar alguns caracteres para definir essa busca. Por exemplo, em expressões regulares
no R, o ponto (.) representa qualquer caractere. Portanto, se dermos o padrão “.a.” à função
str_detect(), ela irá encontrar nomes que possuem a letra “a” entre dois caracteres quaisquer.
No exemplo abaixo, o padrão detectado foi: Armando ou Eduardo.
transf %>%
filter(
str_detect(Usuario, ".a.")
) %>%
group_by(Usuario) %>%
count() %>%
print(n = 5)
## # A tibble: 6 x 2
## # Groups: Usuario [6]
## Usuario n
## <chr> <int>
## 1 Armando 2468
## 2 Eduardo 2452
## 3 Júlio Cesar 2529
## 4 nathalia 2552
## 5 Nathália 2507
## # ... with 1 more row
Por isso nomes como “Ana” foram ignorados pelo filter(). Pois mesmo possuindo a letra “a”,
ela não está entre dois caracteres. Um padrão que detectaria o nome “Ana” seria “.a”, ou seja, o
padrão detectado seria Ana. Dessa forma, o filter() pegaria todo nome que possui um caractere
qualquer à esquerda de uma letra “a”.
Outros caracteres que podemos utilizar para definirmos como a busca pelo padrão ocorre, são o
acento circunflexo (^) e o cifrão ($). Estes caracteres definem os extremos do texto, onde o ^ define
o início do texto, e o $ define o final do texto. Portanto, se dermos o padrão “s$” apenas o nome
“mnathalias” aparece, pois só ele contém um “s” ao final do texto. Logo, nomes como “Júlio Cesar”
não são detectados por esse padrão. Mas caso substituíssemos este padrão por “ˆA” por exemplo,
nomes como “Ana” e “Armando” seriam detectados, pois possuem uma letra “a” maiúscula em seu
ínicio, enquanto outros como “NATHÁLIA##NATAL_30%”, seriam descartados.
transf %>%
filter(
str_detect(Usuario, "s$")
) %>%
group_by(Usuario) %>%
count()
## # A tibble: 0 x 2
## # Groups: Usuario [0]
## # ... with 2 variables: Usuario <chr>, n <int>
E se quiséssemos filtrar todas as linhas com nomes de até três caracteres? Uma solução seria utilizar
a função str_legth() que retorna o número de caracteres presentes em um texto. Mas poderíamos
realizar o mesmo trabalho com REGEX, através de um padrão de três pontos (…). Porém, ainda
falta algo neste padrão, pois caso você adicione este padrão à str_detect(), você irá perceber que
ele retorna todos os nomes possíveis na base!
Pare e pense por um instante, no que o ponto significa. Como eu disse, ele representa qualquer
caractere, logo, estes três pontos estão representando três caracteres quaisquer. O motivo pelo qual
este padrão em str_detect() retorna todos os nomes possíveis na base, é que todos esses nomes
possuem pelo menos três caracteres quaisquer ao longo de seu textos. Então, o que precisamos é
definir o limite dessa pesquisa, para que ela encontre textos de apenas três caracteres quaisquer, e
este trabalho é feito pelos símbolos ^ e $.
transf %>%
filter(
str_length(Usuario) == 3
)
###### Ou então utilizando 3 pontos entre ^ e $ como o padrão.
transf %>%
filter(
str_detect(Usuario, "^...$")
)
Com tudo o que vimos até aqui, como podemos detectar todas as versões de Nathália de uma só vez?
Temos que levar em conta que em 4 versões temos Nathália com letras minúsculas, e em outras 2
versões temos o nome com pelo menos uma letra em maiúsculo, além de uma letra “a” com acento.
nomes[c(1, 2, 4:7),]
## # A tibble: 6 x 2
## # Groups: Usuario [6]
## Usuario n
## <chr> <int>
## 1 Sandra 2487
## 2 Nathália 2507
## 3 Júlio Cesar 2529
## 4 Júlio 2533
## 5 Eduardo 2452
## 6 Armando 2468
Não podemos filtrar todos os nomes ignorando esse fato. Se colocássemos o padrão “nathalia”
apenas as 4 versões com letras minúsculas seriam detectados, enquanto as de letras maiúsculas
não. Neste caso, temos duas alternativas: criarmos dois padrões (um para maiúsculo e outro para
minúsculo), ou então transfromar todos os caracteres dos nomes para minúsculo (ou para maíusculo)
com a função str_to_lower() (ou str_to_upper()).
Perceba que podemos filtrar as versões de Nathália em maiúsculo, apenas com o padrão “N”, sim
plesmente pelo fato de que não há outro nome na base com “n” maiúsculo. Caso não fosse esse o
caso, poderíamos utilizar o padrão mais geral abaixo. Utilizamos o padrão com um ponto (.) jus
tamente onde temos o “a” com acento nas versões em maíusculo. Dessa forma o padrão detectaria
tanto “nathalia” como “nathália”.
transf %>%
filter(
str_detect(Usuario, "nathalia") |
str_detect(Usuario, "N")
)
###### Ou transformando todos os caracteres em minúsculo
transf %>%
filter(
str_detect(
str_to_lower(Usuario),
"nath.lia"
)
)
iris %>%
select(Sepal.Length, 3, ends_with("Width")) %>%
head(n = 8)
O select() irá selecionar as colunas na ordem em que você as fornece à função. No exemplo
acima, ele alocou a terceira coluna (Petal.Length representada pelo índice 3 em select())
como a segunda coluna do data.frame resultante. A função ends_with() irá procurar por todas
as colunas nodata.frame, em que o nome termina com o padrão “Width”. A sua função irmã
(starts_with()) realiza exatamente o mesmo trabalho, porém ela procura o padrão que você dá
a ela, no início do nome da coluna.
O primeiro método de seleção que mencionei, funciona exatamente da mesma forma que em [, e fica
particularmente útil, quando você utiliza sequências, produzidas pela função :. No exemplo abaixo,
estou selecionando todas as colunas entre a primeira e a quarta coluna. Outra opção interessante
quando você possui uma base com várias colunas que deseja jogar fora, é utilizar str_detect() em
colnames(), para encontrar o nome das colunas que possuem um padrão específico, e em seguida,
fornecer este vetor para select() dentro de all_of().
iris %>%
select(1:4) %>%
head(n = 8)
iris %>%
select(all_of(col_select)) %>%
head(n = 8)
## Petal.Length Petal.Width
## 1 1.4 0.2
## 2 1.4 0.2
## 3 1.3 0.2
## 4 1.5 0.2
## 5 1.4 0.2
## 6 1.7 0.4
## 7 1.4 0.3
## 8 1.5 0.2
O quinto método que citei também é bem útil, mas há alguns cuidados que são interessantes de
se tomar ao utilizálo. Caso você quisesse selecionar todas as colunas em iris, que contenham
fatores (factor), você pode inserir apenas o nome da função de teste correspondente a este tipo
(is.factor()). Dessa forma, o código ficaria: select(is.factor). Porém, este código pode ser
um pouco confuso para aqueles que não estão acostumados com essa funcionalidade de select().
Por isso, é recomendado que você envolva o nome da função com where(), deixando assim a
intenção de seu código mais claro para o leitor.
iris %>%
select(where(is.factor)) %>%
head(n = 8)
## Species
## 1 setosa
## 2 setosa
## 3 setosa
## 4 setosa
## 5 setosa
## 6 setosa
## 7 setosa
## 8 setosa
base. Por padrão, a função ordena de forma crescente a coluna. Logo, caso a coluna seja numérica
(double ou integer), a função organiza a coluna a partir dos menores valores, indo até os maiores
valores. Caso a coluna seja de texto (character), a função utiliza ordenação alfabética (de A a Z).
Mas se a coluna for composta de fatores (factor), a função irá ordenar os valores, de acordo com
a ordem definida no atributo levels() destes fatores.
iris %>%
arrange(Sepal.Length) %>%
head(n = 10)
Caso você deseje ordenar a coluna segundo uma ordem decrescente (ou uma ordem alfabética de Z
a A), você deve utilizar a função desc().
iris %>%
arrange(desc(Sepal.Length))%>%
head(n = 10)
Um outro detalhe, é que ao dar mais de uma coluna para arrange(), ele vai aplicar a ordenação
sobre as colunas, de acordo com a ordem em que você dá elas à função. No exemplo abaixo, o
arrange() primeiro ordena de forma decrescente a coluna Sepal.Length, e em seguida, ele or
dena a coluna Sepal.Width em ordem crescente dentro de cada faixa, ou grupo de Sepal.Length.
Ou seja, na hora de ordenar a próxima coluna, o arrange() tem de levar em conta os valores da
coluna que a função ordenou anteriormente.
iris %>%
arrange(desc(Sepal.Length), Sepal.Width) %>%
head(n = 10)
Você deve ter percebido que arrange() não possui toda a flexibilidade de select() na hora de
definir quais colunas serão utilizadas para ordenar a base. Mas nas novas versões do pacote dplyr,
essa possibilidade foi introduzida. Para ter acesso aos métodos utilizados em select(), basta
utilizar a função across() para definir quais colunas serão utilizadas.
iris %>%
arrange(across(starts_with("Sepal"))) %>%
head(n = 10)
Mais a frente, veremos como podemos agrupar, ou definir grupos em nossos dados com a função
group_by(). Essa é uma operação muito comum em análises de dados, e é importante frisar que
o arrange(), por padrão, não respeita estes grupos, caso você os defina. Por isso, se você quer
que a função passe a respeitálos, ordenando as linhas dentro de cada grupo, você deve adicionar o
argumento .by_group = TRUE à função.
1. Somatórios: somatório acumulado cumsum(); soma total de uma coluna sum(); soma
tório por linha, ao longo de algumas colunas operador +; somatório por linha, ao longo de
várias colunas rowSums().
2. Medidas de posição: média de uma coluna mean(); mediana de uma coluna median();
média por linha, ao longo de várias colunas rowMeans().
3. Medidas de dispersão: desvio padrão de uma coluna sd(); variância de uma coluna
var(); intervalo interquartil IQR(); desvio absoluto da mediana mad().
4. Operadores aritméticos: soma (+); subtração (-); divisão (/); multiplicação (*); potência,
ou elevar um número a x (^); restante da divisão (%%); apenas o número inteiro resultante da
divisão (%/%).
1
São cuidados simples, você deve utilizar algum pronome (o ponto “.”, da mesma forma em que o utilizamos na seção do
pipe), ou inserir um select() dentro da função (como a rowSums()) que esteja utilizando, para que ela funcione com
o mutate(). Para exemplos, veja este post do StackOverflow: https://stackoverflow.com/questions/27354734/dplyr
mutaterowsumscalculationsorcustomfunctions
5. Operadores lógicos: aplique um teste lógico por linha, e preencha essa linha com x caso o
teste resulte em TRUE, ou preencha com y caso o teste resulte em FALSE if_else(); quando
você quer aplicar uma operação parecida com if_else(), mas que há vários casos possíveis,
um exemplo típico seria criar uma coluna de faixas etárias case_when(); você pode utilizar
normalmente todos os operadores que vimos na seção de de filter(), para criar um teste
lógico sobre cada linha <, <=, >, >=, ==, !=, !, &, |.
7. Funções de defasagem e liderança: quando você precisa em algum cálculo naquela linha,
utilizar o valor da linha anterior lag(); ou ao invés do valor da linha anterior, você precisa
do valor da linha posterior lead().
Há várias outras funções que são oferecidas pelo pacote dplyr, mas a lista acima que inclue também
algumas funções do pacote base do R, é provavelmente representativa para a maioria dos problemas
e operações. Como eu disse, algumas funções terão comportamentos diferentes, e aquelas que
operam sobre linhas em diversas colunas (por exemplo rowSums) precisam de alguns cuidados
simples para funcionar com o mutate().
A função sum() por exemplo, calcula o total de uma coluna, e portanto, retorna um único valor
como resultado. Isso a torna extremamente útil e eficiente para calcular proporções. Eu poderia
por exemplo, pegar os dados de população dos municípios do estado de Minas Gerais, e calcular a
proporção de cada município sobre a população total do estado. A função mean(), da mesma forma
ao retornar a média de toda a coluna, sendo particularmente útil para calcular desvios em relação a
média do estado.
populacao %>%
mutate(
prop = `População` * 100 / sum(`População`),
desvio = `População` - mean(`População`)
)
## # A tibble: 853 x 9
## IBGE2 IBGE Munic População Ano PIB Intermediaria prop desvio
## <dbl> <dbl> <chr> <dbl> <dbl> <dbl> <chr> <dbl> <dbl>
## 1 10 310010 Abadia dos~ 6972 2017 3.34e7 Uberlândia 0.0331 -17695.
## 2 20 310020 Abaeté 23223 2017 9.62e7 Divinópolis 0.110 -1444.
## 3 30 310030 Abre Campo 13465 2017 2.91e7 Juíz de Fora 0.0640 -11202.
## 4 40 310040 Acaiaca 3994 2017 2.52e6 Juíz de Fora 0.0190 -20673.
Outras funções trabalham sobre vetores sem tentar reduzílos a um único valor. Ou seja, essas
funções pegam vetores (ou colunas) como entrada (input) e retornam um vetor como resultado
(output), ao invés de retornarem um único valor. Todos os operadores aritméticos são vetorizados
dessa forma. Eu poderia por exemplo, usar a função left_join()2 para trazer rapidamente à nossa
tabela de população, o PIB de cada município no ano de 2017, e calcular o PIB per capita destes
municípios.
Outro exemplo, seria se eu usasse if_else() para aplicar uma operação sobre um grupo específico
de municípios, e aplicar uma outra operação sobre os municípios restantes. No exemplo logo abaixo,
eu crio um teste onde if_else() vai conferir se o valor daquela linha na coluna IBGE, pode ser
encontrado dentre os códigos dos cinquenta municípios mais populosos do estado (definidos na
coluna IBGE de munic_50_mais_populosos). Quando o if_else() encontrar um município que
está entre esses cinquenta mais populosos, ele aplica a operação definida no segundo argumento
da função (true), que simplesmente nos retorna o PIB per capita (valor da coluna PIB_per_cap)
do município. Caso o município não esteja entre os cinquenta mais populosos, ele preenche aquela
linha com um valor “não disponível”, mais especificamente um NA do tipo double (NA_real_).
populacao %>%
left_join(
PIB_municipal %>% filter(Ano == 2017),
by = "IBGE"
) %>%
mutate(
PIB_per_cap = PIB / `População`,
PIB_50_populosos = if_else(
IBGE %in% munic_50_mais_populosos$IBGE,
PIB_per_cap,
NA_real_
2
Vou descrever essa função em maiores detalhes na aula sobre dados relacionais. Por enquanto, pense ela como uma
irmã do PROCV() no Excel, que realiza basicamente o mesmo trabalho dela.
)
)
## # A tibble: 853 x 10
## IBGE2 IBGE Munic População Ano.x Intermediaria Ano.y PIB PIB_per_cap
## <dbl> <dbl> <chr> <dbl> <dbl> <chr> <chr> <dbl> <dbl>
## 1 10 310010 Abad~ 6972 2017 Uberlândia 2017 3.34e7 4789.
## 2 20 310020 Abae~ 23223 2017 Divinópolis 2017 9.62e7 4142.
## 3 30 310030 Abre~ 13465 2017 Juíz de Fora 2017 2.91e7 2165.
## 4 40 310040 Acai~ 3994 2017 Juíz de Fora 2017 2.52e6 631.
## 5 50 310050 Açuc~ 9575 2017 Ipatinga 2017 1.53e7 1593.
## 6 60 310060 Água~ 13600 2017 Teófilo Otoni 2017 3.00e7 2205.
## 7 70 310070 Água~ 2005 2017 Uberaba 2017 7.48e7 37292.
## 8 80 310080 Agua~ 4448 2017 Varginha 2017 1.54e7 3472.
## 9 90 310090 Água~ 19166 2017 Teófilo Otoni 2017 1.12e7 586.
## 10 100 310100 Água~ 13477 2017 Teófilo Otoni 2017 4.81e7 3568.
## # ... with 843 more rows, and 1 more variable: PIB_50_populosos <dbl>
## Using ',' as decimal and '.' as grouping mark. Use read_delim() for more control.
Perceba que em if_else(), eu estou me referindo a uma coluna que acaba de ser criada
(PIB_per_cap) no mesmo mutate(). Ou seja, podemos em um mesmo mutate(), construir uma
coluna e utilizála como base de cálculo para outra coluna. Apesar dessa liberdade, eu recomendo
que você crie no máximo quatro colunas em um mesmo mutate(), para evitar erros de memória
(seu computador pode fazer muita coisa ao mesmo tempo, mas tudo tem um limite). Além disso,
um bloco muito grande dentro de mutate() pode se tornar confuso de se ler. Por isso, caso você
tenha que criar mais colunas, eu recomendo abrir um pipe e adicionar um novo mutate() em
seguida.
Agora, pode ser que você não queira necessariamente criar uma nova coluna em sua tabela. Talvez
você apenas queira utilizar as colunas de seu data.frame para calcular um vetor, ou um conjunto
de colunas, e não está interessado em manter o restante de seu data.frame. Se este é o seu caso,
você pode utilizar a função transmute(), que realiza exatamente o mesmo trabalho da mutate(),
mas que lhe retorna apenas a coluna calculada (ou colunas calculadas).
Na linguagem R, possuímos alguns valores especiais. Por exemplo, se você calcular no console a
divisão entre 1 e 0, ao invés de retornar um erro, o console irá lhe retornar o valor Inf, que se refere
a infinito. Mas se você tentar dividir 0 por ele mesmo, o console vai lhe retornar NaN, que significa
“not a number”, ou seja, o valor resultante da divisão não é um número.
Um outro valor especial, seria o NA, que significa not avaliable. Este valor é geralmente resultado de
observações não disponíveis em sua base de dados, ou então, quando você realiza alguma conversão,
por exemplo, converter valores de texto para números com as.double(). Se você está importando
um arquivo do Excel, onde em sua tabela há células vazias, essas células vazias serão preenchidas
em um data.frame do R, com valores NA.
Porque estou falando desses valores? Porque caso você, por exemplo, calcule a soma de uma coluna
que contém um NA, o resultado da operação será um NA. Da mesma forma, se a coluna possui um
valor NaN, o resultado será um valor NaN.
## [1] NA
Por isso, é importante que você saiba se a sua coluna contém algum destes valores especiais, para
que você possa tratar apropriadamente deles. Várias funções que operam sobre a coluna e retornam
um único valor (como sum() e mean()), possuem um argumento na.rm, que define se a função
deve ignorar esses valores especiais em seus cálculos. Portanto, caso a sua coluna possua esses
valores especiais, e você precisa ignorálos, sete este argumento para TRUE.
## [1] 10
populacao_intermed
## # A tibble: 853 x 7
## IBGE2 IBGE Munic População Ano PIB Intermediaria
## <dbl> <dbl> <chr> <dbl> <dbl> <dbl> <chr>
## 1 10 310010 Abadia dos Dourados 6972 2017 33389769 Uberlândia
## 2 20 310020 Abaeté 23223 2017 96201158 Divinópolis
## 3 30 310030 Abre Campo 13465 2017 29149429 Juíz de Fora
## 4 40 310040 Acaiaca 3994 2017 2521892 Juíz de Fora
## 5 50 310050 Açucena 9575 2017 15250077 Ipatinga
## 6 60 310060 Água Boa 13600 2017 29988906 Teófilo Otoni
## 7 70 310070 Água Comprida 2005 2017 74771408 Uberaba
## 8 80 310080 Aguanil 4448 2017 15444038 Varginha
## 9 90 310090 Águas Formosas 19166 2017 11236696 Teófilo Otoni
## 10 100 310100 Águas Vermelhas 13477 2017 48088397 Teófilo Otoni
## # ... with 843 more rows
Portanto, ao definir no exemplo abaixo que esses municípios estão agrupados por região interme
diária, através da função group_by(), eu estou mudando a unidade de análise dos meus dados.
Dessa forma, todas as ações que eu aplicar sobre esses dados, serão aplicados “por grupo”, ou sobre
cada grupo separadamente. No exemplo abaixo, eu calculo novamente a proporção da população
de cada município, sobre o total da população. Porém agora, o cálculo não será sobre o total da
população do estado inteiro, mas sim, sobre o total da população da região intermediária a qual o
município pertence. Da mesma forma, o desvio da população em relação a média, será calculado
em relação a média da região intermediária correspondente.
populacao_intermed %>%
group_by(Intermediaria) %>%
mutate(
prop = `População` * 100 / sum(`População`),
desvio = `População` - mean(`População`)
)
## # A tibble: 853 x 9
## # Groups: Intermediaria [13]
## IBGE2 IBGE Munic População Ano PIB Intermediaria prop desvio
## <dbl> <dbl> <chr> <dbl> <dbl> <dbl> <chr> <dbl> <dbl>
## 1 10 310010 Abadia dos ~ 6972 2017 3.34e7 Uberlândia 0.600 -41424.
## 2 20 310020 Abaeté 23223 2017 9.62e7 Divinópolis 1.79 1901.
## 3 30 310030 Abre Campo 13465 2017 2.91e7 Juíz de Fora 0.577 -2525.
## 4 40 310040 Acaiaca 3994 2017 2.52e6 Juíz de Fora 0.171 -11996.
## 5 50 310050 Açucena 9575 2017 1.53e7 Ipatinga 0.937 -13661
## 6 60 310060 Água Boa 13600 2017 3.00e7 Teófilo Otoni 1.11 -610.
## 7 70 310070 Água Compri~ 2005 2017 7.48e7 Uberaba 0.250 -25595.
## 8 80 310080 Aguanil 4448 2017 1.54e7 Varginha 0.272 -15487.
## 9 90 310090 Águas Formo~ 19166 2017 1.12e7 Teófilo Otoni 1.57 4956.
## 10 100 310100 Águas Verme~ 13477 2017 4.81e7 Teófilo Otoni 1.10 -733.
## # ... with 843 more rows
Você pode identificar se os seus dados estão ou não agrupados, ao olhar para o cabeçalho da tabela,
onde estão dispostas algumas informações como a sua dimensão (no nosso caso, é uma tabela de
853x7, ou 853 linhas e 7 colunas), e os seus grupos. Vemos que os grupos desses dados se encontram
na coluna Intermediaria, e que há 13 grupos diferentes definidos nessa coluna.
Agora, para dar um contexto atual a nossa análise, vou usar o trabalho que eu (Pedro) e outros
pesquisadores com os quais trabalho na Fundação João Piheiro (FJP), temos desempenhado sobre
as estatísticas da COVID19. Resumindo, a FJP tem oferecido parte do seu corpo técnico para
dar suporte a Secretaria Estadual de Saúde no monitoramento das estatísticas de contaminação e
impacto do vírus.
Uma atividade muito comum com os dados de COVID19 seria gerar uma variável contendo a vari
ação diária do número de casos e mortes, já que esses números chegam muitas vezes já acumulados.
Na tabela abaixo, temos os números acumulados de mortes e de casos confirmados pelo vírus em
cada um dos 27 estados brasileiros, e em cada dia desde o início da pandemia.
covid
## # A tibble: 3,625 x 4
## data estado casos mortes
## <date> <chr> <dbl> <dbl>
## 1 2020-03-17 AC 3 0
## 2 2020-03-18 AC 3 0
## 3 2020-03-19 AC 4 0
## 4 2020-03-20 AC 7 0
## 5 2020-03-21 AC 11 0
## 6 2020-03-22 AC 11 0
## 7 2020-03-23 AC 17 0
## 8 2020-03-24 AC 21 0
## 9 2020-03-25 AC 23 0
## 10 2020-03-26 AC 23 0
## # ... with 3,615 more rows
Como eu disse, a intenção é criarmos uma coluna com a variação diária desses números que estão
acumulados. Como a base está ordenada em ordem crescente dos dias em cada estado, podemos
utilizar a função lag() para pegar em cada linha, o valor da linha anterior e simplesmente subtrair
mos um valor do outro. Porém, temos que fazer essa operação separada por cada estado, pois se
não, na linha do primeiro dia de cada estado, será utilizado o valor de casos/mortes do último dia
do estado anterior. É como se no dia 01 de abril do estado de São Paulo, eu estivesse usando no
cálculo dessa variação, o número de casos do dia 31 de junho do estado que vem antes dele na base
(no caso, o estado de Sergipe). Por isso, temos que dar a coluna estado para o group_by(), para
que mutate() realize essa operação para cada estado separadamente.
covid %>%
group_by(estado) %>%
mutate(
casos_var = casos - lag(casos),
mortes_var = mortes - lag(mortes)
)
## # A tibble: 3,625 x 6
## # Groups: estado [27]
Agora que você viu como o group_by() funciona, vamos partir para a função summarise(). Essa
função reduz o seu data.frame em poucas linhas, ou dito de outra forma, essa função produz um
novo data.frame onde teremos uma linha para cada combinação possível dentro das variáveis
de grupo (as variáveis que você definiu em group_by()). Você provavelmente está processando
essa descrição, e pensando porque diabos você gostaria de reduzir o seu data.frame por grupo. O
principal motivo pelo qual você usaria a função summarise(), seria para produzir tabelas com as
estatísticas sumárias ou descritivas destes grupos.
Ou seja, você irá utilizar o group_by() para definir os grupos de seus dados, e a função
summarise() para gerar um novo data.frame com uma linha para cada combinação possível
dentro da variável de grupo, onde você pode alocar o valor da estatística sumária de cada um desses
grupos. Você poderia utilizar este conjunto para saber, por exemplo, quantos carros na tabela mpg,
existem em cada grupo da variável cyl. Ou você pode voltar a base populacao_intermed, e
descobrir qual é a população total, a população média, e o número de municípios em cada um das
treze regiões intermediárias de Minas Gerais.
mpg %>%
group_by(cyl) %>%
summarise(contagem = n())
## # A tibble: 4 x 2
## cyl contagem
## <int> <int>
## 1 4 81
## 2 5 4
## 3 6 79
## 4 8 70
populacao_intermed %>%
group_by(Intermediaria) %>%
summarise(
Total = sum(`População`),
`Média` = mean(`População`),
`Número de municípios` = n()
)
## # A tibble: 13 x 4
## Intermediaria Total Média `Número de municípios`
## <chr> <dbl> <dbl> <int>
## 1 Barbacena 772694 15769. 49
## 2 Belo Horizonte 6237890 84296. 74
## 3 Divinópolis 1300658 21322. 61
## 4 Governador Valadares 771775 13306. 58
## 5 Ipatinga 1022384 23236 44
## 6 Juíz de Fora 2334530 15990. 146
## 7 Montes Claros 1673263 19457. 86
## 8 Patos de Minas 819435 24101. 34
## 9 Pouso Alegre 1289415 16118. 80
## 10 Teófilo Otoni 1222050 14210. 86
## 11 Uberaba 800412 27600. 29
## 12 Uberlândia 1161513 48396. 24
## 13 Varginha 1634643 19935. 82