Vai al contenuto principale

Un tutorial su data.table in R: introduzione a DT[i, j, by]

Il tutorial gratuito su data.table per R spiega le basi e la sintassi del pacchetto data.table per R. Padroneggia subito la sintassi di data.table.
· 10 min leggi

Scopri con l'IA

Questo tutorial R su data.table spiega le basi del comando DT[i, j, by], che è al centro del pacchetto data.table. Se vuoi approfondire il pacchetto data.table, DataCamp offre un corso R interattivo sul pacchetto data.table. Il corso include oltre 35 esercizi R interattivi – tutti fruibili comodamente dal tuo browser – e diversi video con Matt Dowle, autore principale del pacchetto data.table, e Arun Srinivasan, tra i maggiori contributor. Provalo gratis.

Un tutorial R su data.table di DataCamp

Se hai già lavorato con grandi dataset in RAM (da 1 a oltre 100 GB), sai che un data.frame può essere limitante: il tempo necessario per fare certe operazioni è semplicemente troppo lungo. Data.table risolve il problema riducendo i tempi di calcolo. Inoltre, rende più facile fare di più scrivendo meno. Una volta che avrai padroneggiato la sintassi di data.table con questo tutorial R su data.table, la semplicità con cui eseguire operazioni complesse ti stupirà. Quindi non solo ridurrai i tempi di calcolo, ma anche quelli di programmazione.

Il comando DT[i,j,by] ha tre parti: i, j e by. Se pensi in termini SQL, i corrisponde a WHERE, j a SELECT e by a GROUP BY. Descriviamo il comando dicendo: “Prendi DT, fai il subset delle righe usando ‘i’, poi calcola ‘j’ raggruppando per ‘by’”. In un esempio semplice e usando il dataset hflights (così puoi riprodurre tutti gli esempi) otteniamo:

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
... 

Qui abbiamo fatto il subset della tabella per mantenere solo le righe del decimo mese dell’anno, calcolato il tempo medio in volo degli aerei che hanno effettivamente volato (ecco perché si usa na.omit(): i voli cancellati non hanno un valore per AirTime) e poi abbiamo raggruppato i risultati per compagnia. Si vede per esempio che AA (American Airlines) ha un AirTime medio molto più breve rispetto ad AS (Alaska Airlines). Hai notato anche che è possibile usare le funzioni di base di R nella parte j? Ci torneremo tra poco.

La parte i

La parte “i” serve per fare subset sulle righe, proprio come in un data frame.

DT[2:5]
 #selects the second to the fifth row of 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

Ma puoi anche usare i nomi delle colonne, perché vengono valutati nello scope di DT.

DT[UniqueCarrier=="AA"]
#Returns all those rows where the Carrier is 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 

Nota che non devi usare la virgola per fare subset di righe in una data table. In un data.frame, fare DF[2:5] restituirebbe tutte le righe dalla seconda alla quinta colonna. Invece (come ovviamente sa chi sta leggendo), dobbiamo specificare DF[2:5,]. Nota anche che DT[,2:5] non significa nulla per le data table, come spiegato nella prima domanda delle FAQ del pacchetto data.table.

Curioso e utile: quando fai subset sulle righe puoi anche usare il simbolo .N nel comando DT[…], che indica il numero di righe o l’ultima riga. Puoi usarlo per selezionare l’ultima riga o un offset rispetto ad essa.

DT[.N-1]
#Returns the penultimate row of 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 

La parte j

La parte “j” serve per selezionare colonne e farci delle cose. E “cose” può davvero significare qualsiasi cosa. Si possono usare tutti i tipi di funzioni, ed è uno dei punti di forza del pacchetto data.table.

DT[, mean(na.omit(ArrDelay))] 
[1] 7.094334

Nota che la parte “i” è vuota e la prima cosa tra parentesi è una virgola. All’inizio può sembrare controintuitivo. Tuttavia, significa semplicemente che non facciamo subset su alcuna riga, quindi vengono selezionate tutte le righe. Nella parte “j” viene calcolato il ritardo medio in arrivo di tutti i voli. Sembra che l’aereo medio del dataset hflights abbia avuto oltre 7 minuti di ritardo. Tienilo presente quando prenderai il prossimo volo!

Quando selezioni più colonne e ci fai delle cose nella parte “j”, devi usare la notazione ‘.()’. Questa notazione è in realtà solo un alias di ‘list()’. Restituisce una data table, mentre non usare ‘.()’ restituisce solo un vettore, come visto sopra.

DT[, .(mean(na.omit(DepDelay)), mean(na.omit(ArrDelay)))] 
V1       V2
9.444951 7.094334

Un’altra funzione utile che richiede la notazione ‘.()’ ti permette di rinominare le colonne all’interno del 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

Ovviamente, i nuovi nomi di colonna non sono obbligatori.

Combinando quanto visto su “i” e “j” otteniamo:

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

Grafico DepDelay DepTime

Qui abbiamo preso DT, selezionato nella parte “i” tutte le righe in cui il vettore era AA, calcolato il ritardo medio in partenza e in arrivo, e tracciato nella parte “j” l’orario di partenza rispetto al ritardo in partenza.

Ricapitolando, la parte “j” serve per effettuare calcoli sulle colonne specificate in quella parte. Poiché le colonne di una data table sono viste come variabili e le parti di “j” sono valutate come espressioni, praticamente qualsiasi cosa può essere fatta nella parte “j”. Questo accorcia sensibilmente i tempi di programmazione.

La parte by

L’ultima sezione di questo tutorial R su data.table si concentra sulla parte “by”. La parte “by” si usa quando vogliamo calcolare la parte “j” raggruppando per una specifica variabile (o una sua trasformazione). Vedrai che l’espressione “j” viene ripetuta per ciascun gruppo “by”. È semplice da usare: basta specificare la colonna per cui vuoi raggruppare nell’argomento “by”.

DT[,mean(na.omit(DepDelay)),by=Origin] 
Origin  V1
IAH     8.436951
HOU    12.837873 

Qui abbiamo calcolato il ritardo medio prima della partenza, ma raggruppando in base all’aeroporto di origine. Sembra che i voli in partenza da HOU abbiano un ritardo medio maggiore rispetto a quelli da IAH.

Come per la parte “j”, anche nella parte “by” puoi fare molte cose. Si possono usare funzioni nella parte “by” in modo che i risultati delle operazioni fatte nella parte “j” siano raggruppati per qualcosa che abbiamo specificato dentro il comando DT[…]. Usare funzioni all’interno di DT[…] rende quella singola riga molto potente. Allo stesso modo, è necessario usare la notazione ‘.()’ quando si usano più colonne nella parte “by”.

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

Qui è stato prima calcolato il ritardo medio prima della partenza di tutti gli aerei (nessun subset nella parte “i”, quindi tutte le righe sono selezionate) e poi raggruppato, prima per aeroporto di origine e poi per giorni feriali. Weekdays è False nei weekend. Sembra che il ritardo medio alla partenza fosse maggiore quando l’aereo partiva da HOU rispetto a IAH e, sorprendentemente, i ritardi erano minori nei weekend.

Mettendo tutto insieme, un tipico comando DT[i,j,by] dà:

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<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

Qui il sottoinsieme di aerei operati da Delta Air Lines (selezionato in “i”) è stato raggruppato per aeroporto di origine e per giorni feriali (in “by”). È stato anche calcolato il tempo compensato in volo (in “j”). Sembra che nei weekend, indipendentemente dall’aeroporto di partenza (IAH o HOU), il tempo compensato in aria (cioè volando più velocemente) sia maggiore.

C’è molto altro da scoprire nel pacchetto data.table, ma questo post ha illustrato il comando base DT[i,j,by]. Il corso DataCamp spiega in modo approfondito l’intero pacchetto data.table. Puoi svolgere gli esercizi al tuo ritmo nel browser ricevendo suggerimenti e feedback, e rivedere video e slide quanto vuoi. Questo modo interattivo di apprendere ti permette di acquisire conoscenze approfondite ed esperienza pratica con le data table. Provalo gratis. Dai un’occhiata anche alla Cheat Sheet del pacchetto data.table per R di DataCamp.

Speriamo che grazie a questo tutorial R su data.table tu abbia compreso la sintassi fondamentale di data.table e sia pronto a fare esperimenti in autonomia. Se hai domande sul pacchetto data.table, dai un’occhiata qui. Matt e Arun sono molto attivi. Uno dei prossimi post sul pacchetto data.table sarà più tecnico e approfondirà le ampie possibilità offerte dalle data table. Resta sintonizzato!

Argomenti

Correlato