A manipulação de dados, ou data wrangling, consome a maior parte do tempo de quem trabalha com Ciência de Dados. Provavelmente por isso existem vários frameworks diferentes para executar comandos dessa categoria. Em R, as duas principais bibliotecas para isso são o dplyr
e o data.table
. Existem muitas diferenças entre esses dois pacotes e hoje vamos explorar o pacote data.table
e também a ponte entre os dois, chamado dtplyr
.
Por que existem dois pacotes?
A diferença entre os dois pacotes pode ser resumida em dois tópicos:
Velocidade e uso de memória do código: o
data.table
em geral é mais veloz. De tempos em tempos são feitas comparações de performance entredplyr
edata.table
e o pacotedata.table
roda em menos tempo em vários cenários, como pode ser visto aqui e aqui.Sintaxe: o
dplyr
é um dos pacotes principais dotidyverse
,data.table
é mais fiel à sintaxe do R puro.dplyr
foi feito para ser usado com pipes, imita a sintaxe dos verbos de SQL, já odata.table
traz novas features à sintaxe comum do R.
Antes de explorar o data.table
e o dtplyr
, nenhum dos dois pacotes é absolutamente melhor do que o outro. Embora o data.table
seja mais veloz e consuma menos memória que o dplyr
, os códigos escritos nesse framework exigem uma sintaxe específica. Pelo tempo, o data.table
tem uma vantagem, mas uma sintaxe dplyr
pode ser compreendida por interpretadores de SQL
ou até mesmo spark
. Além disso, códigos dplyr
são compatíveis com outros frameworks tidy
, tal como o pacote tidyr
, o pacote purrr
etc. Essas diferenças podem diminuir o valor da eficiência do data.table
: se o computador gasta menos tempo executando um comando, mas você gasta mais o seu tempo (ou o de outras pessoas), a eficiência valeu a pena? É importante considerar o contexto.
O pacote data.table
e o pacote dtplyr
A ideia básica do data.table
é incluir muitas funcionalidades ao operador [
. Um data.frame
transformado em um data.table
passa a funcionar da seguinte maneira:
library(data.table)
DTvoos <- data.table(dados::voos)
DTvoos[
mes == "9",
# antes da primeira vírgula podemos fazer filtros
.(valor = mean(atraso_saida, na.rm = TRUE)),
# depois da segunda vírgula podemos manipular colunas:
# aqui podemos fazer seleções e também podemos criar
# novas colunas. a seleção funciona que nem o "["
# normal. para criar colunas nomeadas, precisamos
# usar a notação ".()"
#
# na verdade, no segundo elemento podemos colocar também uma função que retorne uma tibble
# inclusive existem vários helpers para usar aqui, como o .N, que conta o numero de linhas
# da tibble agrupada, .SD, que permite fazermos manipulações mais complexas
# nos pedacos da base etc
by = .(origem)
# na terceira vírgula, podemos fazer contas agrupadas usando o parâmetro "by"
# que fica disponível uma vez que fazemos "library(data.table)". Para escolher
# as funções que serão usadas no agrupamento sem aspas, precisamos usar a
# notação
]
## origem valor
## 1: JFK 6.635776
## 2: EWR 7.290954
## 3: LGA 6.207439
Embora a notação do .()
seja bem diferente do que normalmente fazemos em dplyr
, a lógica do pacote é similar:
as operacoes são separadas em filtros, manipulações e agrupamentos. As manipulações nas colunas podem ser bastante variadas e isso é um ponto de divergência importante entre o dplyr
e o data.table
, mas também é verdade que existem várias similaridades. Por isso existe o pacote dtplyr:
Traduzindo dplyr
para data.table
: o pacote dtplyr
A lógica aqui é simples: como o data.table
e o dplyr
são minimamente parecidos, muitos códigos dplyr
podem ser traduzidor para data.table
e as computações podem ser feitas só apos a tradução. Dê uma olhada no código abaixo,
que os dados que vieram de base e a chamada que foi feita usando o data.table
:
library(dtplyr)
library(magrittr)
library(dplyr)
library(tidyr)
voos2 <- lazy_dt(dados::voos)
# esse aqui é o pulo do gato. um `lazy_dt` vai sempre traduzir
# os verbos do dplyr para comandos `DT`
voos2 %>%
group_by(origem) %>%
summarise(
valor = mean(atraso_saida, na.rm = TRUE)
)
## Source: local data table [3 x 2]
## Call: `_DT1`[, .(valor = mean(atraso_saida, na.rm = TRUE)), keyby = .(origem)]
##
## origem valor
## <chr> <dbl>
## 1 EWR 15.1
## 2 JFK 12.1
## 3 LGA 10.3
##
## # Use as.data.table()/as.data.frame()/as_tibble() to access results
Claro que existem situações em que a tradução não será possível, principalmente quando tentarmos usar funções do pacote
tidyr
que não tem suporte no dtplyr
. Por outro lado, para grande parte das manipulações simples que fazemos com dplyr
, o pacote dtplyr
vai dar conta.
Comparações e considerações finais
Então, o que a gente deve usar corriqueiramente? Essa pergunta evidentemente não tem uma resposta única, pois como já observamos aqui a ferramenta melhor para resolver um problema depende do contexto em que você está. Entretanto, temos algumas vantagens em usar o dtplyr
quando é possível, pois ele é agnóstico. Além disso, ele tem vantagens como deixar explícito na sintaxe a ordem em que os comandos de um data.table
são executados, o que não acontece no próprio data.table
, que depende de conhecimento sobre o funcionamento interno do código.
De toda forma, existe espaço pra todo mundo e certamente quem programa em R se beneficia de conhecer os dois pacotes!
Gostou? Quer saber mais?
Se você quiser aprender um pouco mais sobre manipulação de dados com R, dê uma olhada no nosso curso R para Ciência de Dados I e aproveite!
Caso você tenha dúvidas, entre em contato com a gente pelos comentários aqui embaixo, pelo nosso Discourse ou pelo e-mail contato@curso-r.com.