Curso
Este tutorial de data.table em R explica o básico do comando DT[i, j, by], que é o coração do pacote data.table. Se você quiser aprender mais sobre o pacote, a DataCamp oferece um curso interativo de R sobre o pacote data.table. O curso tem mais de 35 exercícios interativos em R — todos no conforto do seu navegador — e vários vídeos com Matt Dowle, autor principal do data.table, e Arun Srinivasan, grande colaborador. Experimente grátis.
Se você já trabalhou com conjuntos de dados grandes em RAM (de 1 a mais de 100 GB), sabe que um data.frame pode ser limitante: algumas operações demoram demais. O data.table resolve isso reduzindo o tempo de computação. E mais: ele também facilita fazer mais com menos digitação. Depois que você dominar a sintaxe do data.table neste tutorial, a simplicidade de realizar operações complexas vai te surpreender. Assim, você reduz não só o tempo de processamento, mas também o tempo de programação.
O comando DT[i,j,by] tem três partes: i, j e by. Pensando em termos de SQL, i corresponde ao WHERE, j ao SELECT e by ao GROUP BY. Costumamos descrever o comando assim: “Pegue DT, faça o subconjunto das linhas usando ‘i’, depois calcule ‘j’ agrupado por ‘by’”. Num exemplo simples, usando o conjunto de dados hflights (assim você consegue reproduzir tudo), temos:
library(hflights)
library(data.table)
DT <- as.data.table(hflights)
DT[Month==10,mean(na.omit(AirTime)), by=UniqueCarrier]
UniqueCarrier V1
AA 68.76471
AS 255.29032
B6 176.93548
CO 141.52861
...
Aqui, filtramos a data table para manter só as linhas do 10º mês do ano, calculamos o tempo médio em voo (AirTime) dos aviões que realmente voaram (por isso usamos na.omit() — voos cancelados não têm valor de AirTime) e agrupamos os resultados por companhia aérea. Dá para ver, por exemplo, que a AA (American Airlines) tem um AirTime médio bem menor que a AS (Alaska Airlines). Reparou também que funções da base do R podem ser usadas na parte j? Já chegamos lá.
A parte i
A parte ‘i’ é usada para filtrar linhas, do mesmo jeito que em um data frame.
DT[2:5]
#seleciona da segunda à quinta linha de DT
Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime
2011 1 2 7 1401 1501 AA 428 N557AA 60 45
2011 1 3 1 1352 1502 AA 428 N541AA 70 48
2011 1 4 2 1403 1513 AA 428 N403AA 70 39
2011 1 5 3 1405 1507 AA 428 N492AA 62 44
ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
-9 1 IAH DFW 224 6 9 0 0
-8 -8 IAH DFW 224 5 17 0 0
3 3 IAH DFW 224 9 22 0 0
-3 5 IAH DFW 224 9 9 0 0
Você também pode usar nomes de colunas, pois eles são avaliados no escopo de DT.
DT[UniqueCarrier=="AA"]
#Retorna todas as linhas em que a companhia é American Airlines
Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime
2011 1 1 6 1400 1500 AA 428 N576AA 60
2011 1 2 7 1401 1501 AA 428 N557AA 60
2011 1 3 1 1352 1502 AA 428 N541AA 70
2011 1 4 2 1403 1513 AA 428 N403AA 70
2011 1 5 3 1405 1507 AA 428 N492AA 62
---
2011 12 27 2 1021 1333 AA 2234 N3ETAA 132
2011 12 28 3 1015 1329 AA 2234 N3FJAA 134
2011 12 29 4 1023 1335 AA 2234 N3GSAA 132
2011 12 30 5 1024 1334 AA 2234 N3BAAA 130
2011 12 31 6 1024 1343 AA 2234 N3HNAA 139
AirTime ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
40 -10 0 IAH DFW 224 7 13 0 0
45 -9 1 IAH DFW 224 6 9 0 0
48 -8 -8 IAH DFW 224 5 17 0 0
39 3 3 IAH DFW 224 9 22 0 0
44 -3 5 IAH DFW 224 9 9 0 0
---
112 -12 1 IAH MIA 964 8 12 0 0
112 -16 -5 IAH MIA 964 9 13 0 0
110 -10 3 IAH MIA 964 12 10 0 0
110 -11 4 IAH MIA 964 9 11 0 0
119 -2 4 IAH MIA 964 8 12 0 0
Perceba que você não precisa usar vírgula para filtrar linhas em uma data table. Em um data.frame, fazer DF[2:5] retornaria todas as linhas das colunas 2 a 5. Em vez disso (como todo mundo aqui sabe), precisamos especificar DF[2:5,]. Note também que DT[,2:5] não significa nada para data tables, como explicado na primeira pergunta do FAQ do pacote data.table.
Curioso e útil: ao filtrar linhas, você pode usar o símbolo .N no comando DT[…], que representa o número de linhas ou a última linha. Dá para usá-lo para pegar a última linha ou um deslocamento a partir dela.
DT[.N-1]
#Retorna a penúltima linha de DT
Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime
2011 12 6 2 656 812 WN 621 N727SW 76 64
ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
-13 -4 HOU TUL 453 3 9 0 0
A parte j
A parte ‘j’ é usada para selecionar colunas e fazer coisas com elas. E coisas aqui pode ser praticamente qualquer operação. Todo tipo de função pode ser usada — esse é um dos grandes pontos fortes do data.table.
DT[, mean(na.omit(ArrDelay))]
[1] 7.094334
Note que a parte ‘i’ ficou em branco e a primeira coisa entre colchetes é uma vírgula. Parece contraintuitivo no início, mas isso só quer dizer que não estamos filtrando nenhuma linha — ou seja, todas as linhas estão selecionadas. Na parte ‘j’, calculamos o atraso médio na chegada de todos os voos. Parece que, em média, os voos do conjunto hflights atrasaram mais de 7 minutos. Se prepare para o seu próximo embarque!
Ao selecionar várias colunas e fazer coisas com elas na parte ‘j’, você precisa usar a notação ‘.()’. Essa notação é, na verdade, um atalho para ‘list()’. Ela retorna uma data table; já sem usar ‘.()’ o retorno é apenas um vetor, como vimos acima.
DT[, .(mean(na.omit(DepDelay)), mean(na.omit(ArrDelay)))]
V1 V2
9.444951 7.094334
Outro recurso útil que exige a notação ‘.()’ permite renomear colunas dentro do comando DT[…].
DT[, .(Avg_ArrDelay =
mean(na.omit(ArrDelay)))]
Avg_ArrDelay
7.094334
DT[, .(Avg_DepDelay = mean(na.omit(DepDelay)),
avg_ArrDelay = mean(na.omit(ArrDelay)))]
Avg_DepDelay Avg_ArrDelay
9.444951 7.094334
Claro, dar novos nomes às colunas é opcional.
Combinando o que vimos sobre ‘i’ e ‘j’, temos:
DT[UniqueCarrier=="AA", .(Avg_DepDelay =
mean(na.omit(DepDelay)),
Avg_ArrDelay = mean(na.omit(ArrDelay)),
plot(DepTime,DepDelay,ylim=c(-15,200)),
abline(h=0))]
Avg_DepDelay Avg_ArrDelay V3 V4
6.390144 0.8917558 NULL NULL
Aqui, pegamos DT, selecionamos todas as linhas em que a companhia era AA na parte ‘i’, calculamos o atraso médio na saída e na chegada, e plotamos o horário de decolagem versus o atraso na decolagem na parte ‘j’.
Resumindo: a parte ‘j’ serve para fazer cálculos sobre as colunas especificadas ali. Como as colunas de uma data table são vistas como variáveis e as partes de ‘j’ são avaliadas como expressões, praticamente qualquer coisa pode ser feita na parte ‘j’. Isso encurta bastante seu tempo de programação.
A parte by
A seção final deste tutorial de data.table em R foca na parte ‘by’. A parte ‘by’ é usada quando queremos calcular a expressão de ‘j’ agrupada por uma variável específica (ou por uma transformação dessa variável). Você vai ver que a expressão ‘j’ é repetida para cada grupo de ‘by’. Usar é simples: basta indicar a coluna pela qual você quer agrupar no argumento ‘by’.
DT[,mean(na.omit(DepDelay)),by=Origin]
Origin V1
IAH 8.436951
HOU 12.837873
Aqui, calculamos o atraso médio antes da decolagem, mas agrupado pela origem do voo. Parece que os voos que partem de HOU têm um atraso médio maior do que os que saem de IAH.
Assim como na parte ‘j’, dá para fazer muita coisa em ‘by’. Funções podem ser usadas em ‘by’ para que os resultados das operações feitas em ‘j’ sejam agrupados por algo que especificamos dentro do comando DT[…]. Usar funções dentro de DT[…] deixa essa única linha muito poderosa. Da mesma forma, a notação ‘.()’ precisa ser usada quando você agrupa por várias colunas.
DT[,.(Avg_DepDelay_byWeekdays = mean(na.omit(DepDelay))), by=.(Origin,Weekdays = DayOfWeek<6)]
Origin Weekdays Avg_DepDelay_byWeekdays
IAH FALSE 8.286543
IAH TRUE 8.492484
HOU FALSE 10.965384
HOU TRUE 13.433994
Aqui, primeiro calculamos o atraso médio antes da decolagem de todos os voos (sem filtro em ‘i’, então todas as linhas foram selecionadas) e, em seguida, agrupamos: primeiro pela origem e depois por dia da semana. Weekdays é False nos fins de semana. Parece que o atraso médio antes da decolagem foi maior quando o voo saiu de HOU do que de IAH e, surpreendentemente, os atrasos foram menores nos fins de semana.
Juntando tudo, um comando típico DT[i,j,by] fica assim:
DT[UniqueCarrier=="DL", .(Avg_DepDelay =
mean(na.omit(DepDelay)),
Avg_ArrDelay = mean(na.omit(ArrDelay)),
Compensation = mean(na.omit(ArrDelay - DepDelay))), by = .(Origin, Weekdays = DayOfWeek&lt;6)]
Origin Weekdays Avg_DepDelay Avg_ArrDelay Compensation
IAH FALSE 8.979730 4.116751 -4.825719
HOU FALSE 7.120000 2.656566 -4.555556
IAH TRUE 9.270948 6.281941 -2.836609
HOU TRUE 11.631387 10.406593 -1.278388
Aqui, o subconjunto de voos operados pela Delta Air Lines (selecionado em ‘i’) foi agrupado pela origem e por Weekdays (em ‘by’). Também calculamos o tempo “compensado” em voo (em ‘j’). Parece que, nos fins de semana — independentemente de sair de IAH ou HOU — o tempo compensado enquanto no ar (voando mais rápido) é maior.
Há muito mais para explorar no pacote data.table, mas este post mostrou o básico do comando DT[i,j,by]. O curso da DataCamp cobre o pacote inteiro em detalhes. Você pratica no seu ritmo, direto no navegador, recebe dicas e feedback, e pode rever os vídeos e slides quando quiser. Esse formato interativo acelera seu aprendizado e dá experiência prática com data tables. Experimente grátis. Confira também o Cheat Sheet do pacote data.table em R.
Esperamos que, com este tutorial de data.table em R, você tenha entendido a sintaxe fundamental do data.table e esteja pronto para praticar. Se tiver dúvidas sobre o pacote, dê uma olhada aqui. Matt e Arun são bem ativos por lá. Um dos próximos posts sobre data.table será mais técnico, explorando a fundo as possibilidades com data tables. Fique de olho!


