Fazer um join ou um merge nada mais é do que juntar dois conjuntos de dados por meio de um ou mais campos em comum (chaves/key). Existem algumas vantagens de se utilizar o join com dplyr em relação ao merge do base R e ao SQL:
● Sempre preserva a ordem das linhas originais
● Sintaxe mais intuitiva
● Pode ser aplicado em diferentes tipos de dados, inclusive com spark.
instalação e inicialização do pacote:
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
Podemos dividir as funções do tipo join em 2 tipos:
●Mutating joins:
left_join(), right_join(),inner_join(), full_join()
●Filtering joins:
semi_join(), anti_join()
Temos ainda, no pacote dplyr, as funções union(), intersect(), setdiff(), setequal() que também serão úteis para unir e comparar conjuntos de dados.
O conjunto de dados para análise será nycflights13. Ele é adequado para o estudo que iremos realizar pois se trata de um banco de dados relacional.
inicializar o pacote:
library(nycflights13)
analizar:
flights
## # A tibble: 336,776 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Note que por se tratar de uma tibble, o dataframe é impresso de uma forma um pouco diferente da usual. Temos aqui os dados de 336,776 vôos que partiram de Nova Iorque em 2013. O pacote inclui, além da tabela flights, outras 4 tibbles (airlines, airports, planes, weather) relacionadas à esta:
●airlines: permite que você procure o nome completo do operador através do código abreviado
●airports: fornece informações sobre cada aeroporto, identificadas pelo código do aeroporto faa
●planes: fornece informações sobre cada avião, identificados pelo seu tailnum
●weather: dados climáticos para cada aeroporto de Nova York por hora
O diagrama acima mostra como a tabela flights se relaciona com as outras:
●flights se conecta à planes através de uma variável única, tailnum.
●flights se conecta à airlines através de uma variável única, carrier.
●flights se conecta à airports de duas formas: através das variáveis origin e dest.
●flights se conecta à weather através de origin (location), year, month, day e hour (time).
inner_join(x, y): Retorna todas as linhas de x onde existem valores correspondentes em y e todas as colunas de x e y. Se houver múltiplas combinações entre x e y, todas as combinações das correspondências são retornadas.
É o tipo mais simples de join, que combina pares de observações sempre que suas chaves são iguais.
O resultado do inner join é um novo data frame que contém a chave, os valores de “x” e os valores de “y”. Usamos “by” para informar ao dplyr qual variável é a chave.
Tanto o merge() do R base quanto o inner_join() do dplyr tentam descobrir as chaves em comum buscando campos com o mesmo nome. Mas vamos supor que os campos chave tivessem nomes diferentes (o que é bem comum). No R base usamos by.x e by.y para especificar o nome do campo chave do primeiro e segundo data.frame. No inner_join() do dplyr usamos um vetor no formato c(“chave.x” = “chave.y”).
Veremos como isso se aplica à nossa base de dados: flights e planes estão conectadas através da chave “tailnum”.
inner_join(x, y, by = NULL, copy = FALSE, suffix = c(“.x”, “.y”), …)
inner_join<-inner_join(flights,planes,by="tailnum")
dim(flights)
## [1] 336776 19
dim(planes)
## [1] 3322 9
dim(inner_join)
## [1] 284170 27
Resumindo, o inner join retorna todos os campos de ambos os data.frames, mas somente as linhas em que as chaves são iguais.
Podemos estranhar o fato da tabela “inner_join” conter um menor número de linhas do que a tabela “flights”. Isso significa que existem aviões que não estão identificados na tabela “plane”. Uma boa forma de checar isso é usando o anti_join:
anti_join(x, y): Returna todas as linhas de x para as quais não existem valores correspondente em y, mantendo apenas as colunas de x. É considerado um “filtering join”.
anti_join<-anti_join(flights,planes,by="tailnum")
dim(anti_join)[1]+dim(inner_join)[1]
## [1] 336776
dim(flights)[1]
## [1] 336776
Vemos que a quantidade de linhas cujas chaves estão em flights mas não estão em planes (representadas no anti_join) somadas às linhas que estão em ambas (representadas em inner_join) é exatamente a mesma que na base original flights, conforme esperado.
semi_join(x, y): Returna todas as linhas de x para as quais existem valores correspondente em y, mantendo apenas as x. O semi join difere do inner join porque o inner join irá retornar uma linha de x para cada valor correspondente de y, enquanto um semi join nunca irá duplicar valores x.
Pode-se dizer que é um “filtering join” complementar ao anti join. Na prática, ele vai retornar as mesmas linhas que o inner_join equivalente (sem duplicar), e apenas vai exercer a função filtrar a tabela x. Isto é, diferentemente do inner join, ele não retorna as colunas de y, apenas identifica se existe correpondência.
semi_join<-semi_join(flights,planes,by="tailnum")
dim(semi_join)
## [1] 284170 19
dim(inner_join)
## [1] 284170 27
Neste exemplo, o número de linhas resultante do semi join e do inner join foi o mesmo pois não havia nenhum valor duplicado da variável chave tailnum na tabela planes.
full_join(x, y): Returna todas as linhas e todas as colunas tanto de x quanto de y. Returna NA no caso de não haver valor correspondente.
full_join<-full_join(flights,planes,by="tailnum")
dim(full_join)
## [1] 336776 27
tail(full_join)
## # A tibble: 6 x 27
## year.x month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 9 30 NA 1842 NA NA
## 2 2013 9 30 NA 1455 NA NA
## 3 2013 9 30 NA 2200 NA NA
## 4 2013 9 30 NA 1210 NA NA
## 5 2013 9 30 NA 1159 NA NA
## 6 2013 9 30 NA 840 NA NA
## # ... with 20 more variables: sched_arr_time <int>, arr_delay <dbl>,
## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## # time_hour <dttm>, year.y <int>, type <chr>, manufacturer <chr>,
## # model <chr>, engines <int>, seats <int>, speed <int>, engine <chr>
É interessante notar que como as bases flights e planes possuiam ambas, originalmente, uma coluna “year”, sendo que ela não foi usada como chave para o join (consideramos apenas o “tailnum”), o dplyr renomeou estas colunas como “year.x” e “year.y” para poder distinguir entre aquela que veio da base flights (base x) e aquela que veio da base planes(base y).
colnames(full_join)
## [1] "year.x" "month" "day" "dep_time"
## [5] "sched_dep_time" "dep_delay" "arr_time" "sched_arr_time"
## [9] "arr_delay" "carrier" "flight" "tailnum"
## [13] "origin" "dest" "air_time" "distance"
## [17] "hour" "minute" "time_hour" "year.y"
## [21] "type" "manufacturer" "model" "engines"
## [25] "seats" "speed" "engine"
Note ainda que, caso não especifique a chave, o R automaticamente considera todas as colunas com nome igual como keys e isso não necessariamente produz o resultado desejado:
full_join2<-full_join(flights,planes)
## Joining, by = c("year", "tailnum")
dim(full_join2)
## [1] 340006 26
tail(full_join2)
## # A tibble: 6 x 26
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 1991 NA NA NA NA NA NA
## 2 2002 NA NA NA NA NA NA
## 3 1992 NA NA NA NA NA NA
## 4 2002 NA NA NA NA NA NA
## 5 1992 NA NA NA NA NA NA
## 6 1992 NA NA NA NA NA NA
## # ... with 19 more variables: sched_arr_time <int>, arr_delay <dbl>,
## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## # time_hour <dttm>, type <chr>, manufacturer <chr>, model <chr>,
## # engines <int>, seats <int>, speed <int>, engine <chr>
No nosso exemplo, year da base flights é referente à data de partida do avião, enquanto year da base planes é referente à data de fabricação do avião. Dessa forma, se não especificarmos a chave corretamente através do argumento by, teremos um join sem sentido.
left_join(x, y): Returna todas as linhas de x, e todas as colunas tanto de x quanto de y. Se houver múltiplas correspondências entre x e y, todas as combinações de correspondências serão retornadas.
left_join<-left_join(flights,planes,by="tailnum")
dim(left_join)
## [1] 336776 27
right_join(x, y): Returna todas as linhas de y, e todas as colunas tanto de x quanto de y. Se houver múltiplas correspondências entre x e y, todas as combinações de correspondências serão retornadas.
O princípio é EXATAMENTE o mesmo do left join. A única diferença é a permanência dos registros do conjunto da direita em vez dos da esquerda, como podemos ver na figura acima.
right_join<-right_join(planes,flights,by="tailnum")
dim(left_join)
## [1] 336776 27
Veremos que o conjunto gerado em right_join tem exatamente as mesmas dimensões que o de left_join, pois usamos as mesmas bases de dados, em ordem trocada, para fazer as junções.