Considerações iniciais

Esse é um documento feito para ensinar e para reforçar conteúdos de manipulação de dados usando dplyr. Qualquer comentário, erro ou sugestão, é só falar comigo entrando em contato através de qualquer uma das opções listadas em Contato.

O script em R está disponível aqui: https://github.com/GabrielReisR/R/blob/master/estrutura%20de%20dados/dplyr-parte-2.R

Essa publicação é uma continuação de Manipulando com dplyr - Parte 1: select() e mutate(): https://rpubs.com/reisrgabriel/dplyrPt1

Sobre o dplyr

O pacote dplyr possui várias funções interessantes que permitem:

  • Selecionar variáveis de maneira dinâmica e fácil.
  • Criar novas variáveis a partir de variáveis antigas.
  • Sumarizar vários valores em um só.
  • Filtrar e agrupar variáveis de interesse a partir de condições específicas.

Em suma, o dplyr é um pacote muito versátil e bastante útil para manipulação de dados em objetos data.frame.

Para maiores informações sobre o dplyr:


Usando bind()

Em muitas situações unir dois bancos de dados é uma necessidade. Assim como na Parte 1, vamos utilizar funções do dplyr que facilitam realizar essa manipulação.

Vamos começar com a situação mais simples: dois bancos de dados nos quais os casos estão ordenados e basta apenas juntá-los. Ou seja, o participante da linha 1 no banco 1 é o mesmo participante da linha 1 no banco 2, e assim por diante.

Esse é o caso mais simples de união de dois bancos. Para demonstrá-lo, não é necessário importar um banco de dados. Basta criar dois data.frames com algumas linhas de código.

bind_cols()

Vou criar 2 bancos, cada um com duas colunas e cinco casos.

col1 <- c(letters[1:5]) # letras de 1 a 5
col2 <- sample(c(1:5), size = 5, replace = FALSE) # numeros de 1 a 5
col3 <- sample(c(100:2500), size = 5, replace = TRUE) # numeros aleatorios de 100 a 2500
col4 <- sample(c(100:2500), size = 5, replace = TRUE) # numeros aleatorios de 100 a 2500

dataframe1 <- data.frame(col1, col2) # criando df1
dataframe2 <- data.frame(col3, col4) # criando df2

dataframe1 
dataframe2

Para juntar ambos os bancos pela sua posição, basta utilizar a função bind_cols() presente no pacote dplyr. Ela possui a forma bind_cols(df1, df2).

library(dplyr) # lendo dplyr
dataframe_completo <- bind_cols(dataframe1, dataframe2) # usando bind_cols()

dataframe_completo

É bem simples! Como podemos ver, os bancos foram unidos pela posição das variáveis.

bind_rows()

Com bind_rows() a lógica é a mesma. A coluna 1 no banco 1 representa a mesma variável da coluna 1 no banco 2. Quando um data.frame e outro possuem variáveis iguais, os casos são adicionados. Quando as colunas diferem de nome, os casos que não possuem valor nessa coluna recebem o valor de missing NA.

lista <- list(col1 = "a", col2 = 4, col3 = 1028, col4 = 255, col5 = 9902)
dataframe_completo <- bind_rows(dataframe_completo, lista)

dataframe_completo

Pronto! Já que nenhuma linha possui valores na coluna 5, isso é preenchido como NA, assim como explicado acima.

Mutating-Joins

Vamos para funções mais completas e que levam em conta bancos de dados com mais informações. Os mutating joins são funções que unem bancos baseados em condições específicas. Vou explicar.

Considere os bancos a seguir:

dass_dep <- read.csv("https://raw.githubusercontent.com/GabrielReisR/R/master/estrutura%20de%20dados/dados/dass_dep.csv")
dass_ans <- read.csv("https://raw.githubusercontent.com/GabrielReisR/R/master/estrutura%20de%20dados/dados/dass_ans.csv")
dass_est <- read.csv("https://raw.githubusercontent.com/GabrielReisR/R/master/estrutura%20de%20dados/dados/dass_est.csv")

São os mesmos casos que vimos no bloco 2. Abaixo as características dos bancos.

  • O banco dass_dep contém todas as respostas à DASS, contabilizando 1000 casos.
  • Os bancos dass_ans e dass_est possuem 900 casos cada.
  • Cada banco possui uma coluna chamada escore_total e outra coluna chamada nivel:
    • No banco dass_dep, escore_total significa o escore em depressão, e nivel, a classificação do caso.
    • No banco dass_ans, escore_total significa o escore em ansiedade, e nivel, a classificação do caso.
    • No banco dass_est, escore_total significa o escore em estresse, e nivel, a classificação do caso.
  • Todos os bancos possuem uma coluna de identificação, inicializada por “id”.

Como juntar bancos com colunas de nomes iguais? Quais casos aceitar ou não?

Temos algumas opções:

  1. full_join(): todos os casos entram.
  2. inner_join(): todos os casos em comum em todos os bancos entram.
  3. left_join(): todos os casos do primeiro banco ficam, os casos em comum com o segundo banco também entram.
  4. right_join(): todos os casos do segundo banco ficam, os casos em comum com o primeiro banco também entram.

Vamos começar com a opção menos excludente.

Full join()

Um full_join nos permite incluir todos os casos do banco x e do banco y ao mesmo tempo. Assim, todos os casos que existem no banco 2 serão também adicionados ao banco 1.

Para fazer qualquer join, possuímos duas funções bem importantes:

  • by(): escolhe qual argumento ambos bancos tem em comum.
  • suffix(): auxilia colocando um sufixo em colunas de nome idêntico.

Sabendo que nos dois bancos temos todas as respostas da DASS, precisamos fazer duas transformações. Antes de começar, vamos excluir as respostas da DASS do banco dass_ans e dass_est. Aproveitaremos e já excluíremos a coluna “X” de todos os bancos.

dass_dep <- dass_dep %>% 
  select(-X) # excluindo a coluna X

dass_ans <- dass_ans %>% 
  select(id_ans, escore_total, nivel) # selecionando id_ans, escore_total e nivel

dass_est <- dass_est %>% 
  select(id_est, escore_total, nivel) # selecionando id_est, escore_total e nivel

names(dass_dep) # nomes das colunas do banco 'dass_dep'
##  [1] "id"           "dass_1"       "dass_2"       "dass_3"       "dass_4"      
##  [6] "dass_5"       "dass_6"       "dass_7"       "dass_8"       "dass_9"      
## [11] "dass_10"      "dass_11"      "dass_12"      "dass_13"      "dass_14"     
## [16] "dass_15"      "dass_16"      "dass_17"      "dass_18"      "dass_19"     
## [21] "dass_20"      "dass_21"      "dass_22"      "dass_23"      "dass_24"     
## [26] "dass_25"      "dass_26"      "dass_27"      "dass_28"      "dass_29"     
## [31] "dass_30"      "dass_31"      "dass_32"      "dass_33"      "dass_34"     
## [36] "dass_35"      "dass_36"      "dass_37"      "dass_38"      "dass_39"     
## [41] "dass_40"      "dass_41"      "dass_42"      "escore_total" "nivel"
names(dass_ans) # nomes das colunas do banco 'dass_ans'
## [1] "id_ans"       "escore_total" "nivel"
names(dass_est) # nomes das colunas do banco 'dass_est'
## [1] "id_est"       "escore_total" "nivel"

Agora, entendendo os novos nomes, podemos começar.

dass_total <- dass_dep %>% # sempre se começa a partir de um banco
  full_join(dass_ans, by = c("id" = "id_ans"), suffix = c("_dep", "_ans"))

dass_total

O que aconteceu aqui foi o seguinte:

  • Abrimos um pipe em dass_dep e, na função full_join(), chamamos o banco a ser juntado, dass_ans.
  • Especificamos em by() que a coluna “id” do primeiro banco é igual à coluna “id_ans” do segundo banco (by = c("id" = "id_ans")), delimitando que (1) essas colunas deveriam ser iguais e que os valores dos casos deveriam ser posicionados de acordo com essa coluna.
    • Caso “id” nos dois bancos, bastaria especificar by = "id".
  • Em suffix(), para todas as colunas com nome igual, adicionamos um sufixo no primeiro banco chamado "_dep" e um sufixo no segundo banco chamado "_ans" (suffix = c("_dep", "_ans")).

O processo agora pode ser o mesmo (e ele é segue essa linha para todos os mutating-joins).

dass_total <- dass_total %>% 
  full_join(dass_est, by = c("id" = "id_est")) %>% 
  rename(escore_total_est = escore_total, # renomear a coluna escore_total
         nivel_est = nivel) # renomear a coluna nivel

dass_total # full join concluído!

1000 casos! Isso aconteceu porque todos os casos foram adicionados.

Inner join()

Um inner join escolhe apenas os casos que existam no banco x E no banco y. Os casos em comum são juntados. A fórmula é a mesma, então vamos direto à ela.

dass_inner <- dass_dep %>% 
  inner_join(dass_ans, by = c("id" = "id_ans"), suffix = c("_dep", "_ans")) %>% 
  inner_join(dass_est, by = c("id" = "id_est")) %>% 
  rename(escore_total_est = escore_total, # renomear a coluna escore_total
         nivel_est = nivel) # renomear a coluna nivel

dass_inner

Pronto! Podemos ver que o número de casos diminuiu. Isso porque só os que estavam no banco dass_dep, dass_ans e dass_est ao mesmo tempo que entraram no banco.

Left join()

O left join, como o nome diz, preserva o primeiro banco (o banco da esquerda). Todos os casos da esquerda são preservados, enquanto os outros são adicionados.

dass_left <- dass_dep %>% 
  left_join(dass_ans, by = c("id" = "id_ans"), suffix = c("_dep", "_ans")) %>% 
  left_join(dass_est, by = c("id" = "id_est")) %>% 
  rename(escore_total_est = escore_total, # renomear a coluna escore_total
         nivel_est = nivel) # renomear a coluna nivel

dass_left

Temos novamente 1000 casos! Isso porque preservamos o banco dass_dep primeiro, e ele possui 1000 casos.

Right join()

O right join, como o nome diz, preserva o segundo banco (o banco da direita). Todos os casos da direita são preservados, enquanto os outros são adicionados.

dass_right <- dass_dep %>% 
  right_join(dass_ans, by = c("id" = "id_ans"), suffix = c("_dep", "_ans")) %>% 
  right_join(dass_est, by = c("id" = "id_est")) %>% 
  rename(escore_total_est = escore_total, # renomear a coluna escore_total
         nivel_est = nivel) # renomear a coluna nivel

dass_right

Temos 900 casos agora. Isso acontece porque preservamos primeiro o banco da direita (dass_ans), que possuía 900 casos.

Filter-joins

Enquanto os mutating joins se importam em unir bancos baseados em condições, os filter joins são construídos para filtrar casos de um banco a outro, não necessariamente para unir bancos.

Semi join()

O semi join retorna todos os valores do banco x que também estão no banco y. Ao fazer isso, ele não incorpora as colunas e variáveis de y. Ou seja, assim como o próximo verbo que vamos tratar (anti_join()), o semi_join é uma função filtro, pois retorna as colunas de apenas um banco se baseando nos casos de outro banco.

Vamos fazer o semi-join entre dass_dep e os outros bancos. Notemos que, aqui, não é necessário renomear variáveis ou colocar sufixos, pois as colunas de dass_ans e dass_est não serão incorporadas.

dass_semi <- dass_dep %>% 
  semi_join(dass_ans, by = c("id" = "id_ans")) %>% 
  semi_join(dass_est, by = c("id" = "id_est"))

dass_semi

Aqui temos 809 casos. Por quê?

  • Apenas os 900 casos que estavam em dass_ans permaneceram em dass_dep.
  • Após isso, desses 900 casos, 809 estavam em dass_est.

Sendo assim, apenas esses últimos permaneceram. Vale ressaltar que nenhuma nova coluna foi adicionada.

Anti join()

Esse é o último join que veremos aqui e ele é bem interessante. Ele preserva apenas os casos que estão em x e que não estão em y. Assim, só se salva quem está no banco da esquerda e que não está nos outros bancos.

dass_anti <- dass_dep %>% 
  anti_join(dass_ans, by = c("id" = "id_ans")) %>% 
  anti_join(dass_est, by = c("id" = "id_est"))

dass_anti

Apenas 9 casos estão no primeiro banco e não são compartilhados com os demais!

Por enquanto é isso :)


Mais informações

Manipulando com dplyr - Parte 1: select() e mutate(): https://rpubs.com/reisrgabriel/dplyrPt1

Organizando com tidyr - Parte 1: dados wide e long: https://rpubs.com/reisrgabriel/tidyrPt1

Organizando com tidyr - Parte 2: valores missing: https://rpubs.com/reisrgabriel/tidyrPt2

Importação de dados e diagnósticos iniciais: https://rpubs.com/reisrgabriel/importdiagn

Para maiores informações sobre o dplyr: https://dplyr.tidyverse.org/ ou https://github.com/rstudio/cheatsheets/blob/master/data-transformation.pdf