R data.table simboli e operatori che dovresti conoscere

Il codice R data.table diventa più efficiente ed elegante quando si sfruttano i suoi simboli e funzioni speciali. Con questo in mente, esamineremo alcuni modi speciali per creare sottoinsiemi, contare e creare nuove colonne.

Per questa demo, userò i dati del sondaggio degli sviluppatori Stack Overflow del 2019, con circa 90.000 risposte. Se vuoi seguire, puoi scaricare i dati da Stack Overflow.

Se il pacchetto data.table non è installato sul tuo sistema, installalo da CRAN e poi caricalo come al solito con library(data.table). Per iniziare, potresti voler leggere solo le prime righe del set di dati per facilitare l'esame della struttura dei dati. Puoi farlo con la fread()funzione di data.table e l' nrowsargomento. Leggerò in 10 righe:

data_sample <- fread ("data / survey_results_public.csv", nrows = 10)

Come vedrai, ci sono 85 colonne da esaminare. (Se vuoi sapere cosa significano tutte le colonne, ci sono file nel download con lo schema dei dati e un PDF del sondaggio originale.) 

Per leggere tutti i dati, userò:

mydt <- fread ("data / survey_results_public.csv")

Successivamente, creerò un nuovo data.table con poche colonne per semplificare il lavoro e visualizzare i risultati. Un promemoria che data.table utilizza questa sintassi di base: 

mydt [i, j, by]

L'introduzione al pacchetto data.table dice di leggerlo come "prendi dt, subset o riordina le righe usando i, calcola j, raggruppate per". Tieni presente che i e j sono simili all'ordine delle parentesi di base R: prima le righe, poi le colonne. Quindi i è per le operazioni che faresti sulle righe (scegliendo le righe in base ai numeri di riga o alle condizioni); j è ciò che faresti con le colonne (seleziona le colonne o crea nuove colonne dai calcoli). Tuttavia, nota anche che puoi fare molto di più all'interno delle parentesi data.table rispetto a un frame di dati R di base. E la sezione "by" è nuova per data.table.

Dal momento che sto selezionando le colonne, quel codice va nel punto "j", il che significa che le parentesi devono prima una virgola per lasciare vuoto il punto "i":

mydt [, j]

Seleziona le colonne data.table

Una delle cose che mi piace di data.table è che è facile selezionare le colonne tra virgolette o non quotate . Non quotato è spesso più conveniente (di solito è il modo più ordinato). Ma citato è utile se stai usando data.table all'interno delle tue funzioni o se vuoi passare un vettore che hai creato da qualche altra parte nel tuo codice.

È possibile selezionare le colonne data.table nel tipico modo di base R, con un vettore convenzionale di nomi di colonna tra virgolette. Per esempio: 

dt1 <- mydt [, c ("LanguageWorkedWith", "LanguageDesireNextYear",

"OpenSourcer", "CurrencySymbol", "ConvertedComp",

"Hobbista")]

Se vuoi usarli senza virgolette, crea una lista invece di un vettore e puoi passare i nomi non quotati . 

dt1 <- mydt [, list (LanguageWorkedWith, LanguageDesireNextYear,

OpenSourcer, CurrencySymbol, ConvertedComp,

Hobbista)]

E ora arriviamo al nostro primo simbolo speciale. Invece di digitare list(), puoi semplicemente usare un punto:

dt1 <- mydt [,. (LanguageWorkedWith, LanguageDesireNextYear,

OpenSourcer, CurrencySymbol, ConvertedComp,

Hobbista)]

Questa .()è una scorciatoia per le list()parentesi di data.table all'interno.

Cosa succede se si desidera utilizzare un vettore già esistente di nomi di colonne? Mettere il nome dell'oggetto vettoriale all'interno delle parentesi di data.table non funzionerà. Se creo un vettore con nomi di colonne tra virgolette, in questo modo: 

mycols <- c ("LanguageWorkedWith", "LanguageDesireNextYear",

"OpenSourcer", "CurrencySymbol", "ConvertedComp", "Hobbyist")

Quindi questo codice  non funzionerà: 

dt1 <- mydt [, mycols]

Invece, devi mettere .. (sono due punti) davanti al nome dell'oggetto vettoriale:

dt1 <- mydt [, ..mycols]

Perché due punti? Mi è sembrato un po 'casuale finché non ho letto la spiegazione. Pensalo come i due punti in un terminale della riga di comando Unix che ti fanno salire di una directory. Qui, stai salendo di uno spazio dei nomi , dall'ambiente all'interno delle parentesi data.table fino all'ambiente globale. (Questo mi aiuta davvero a ricordarlo!)

Contare le righe di data.table

Al prossimo simbolo. Per contare per gruppo, puoi utilizzare il .Nsimbolo di data.table , dove  .Nsta per "numero di righe". Può essere il numero totale di righe o il numero di righe per gruppo se stai aggregando nella sezione "per". 

Questa espressione restituisce il numero totale di righe in data.table: 

mydt [, .N]

L'esempio seguente calcola il numero di righe raggruppate per una variabile: se le persone nel sondaggio codificano anche come hobby (la Hobbyistvariabile).

mydt [, .N, hobbista]

# ritorna:

Hobbista N 1: Sì 71257 2: No 17626

È possibile utilizzare il semplice nome della colonna all'interno delle parentesi data.table se è presente una sola variabile. Se vuoi raggruppare per due o più variabili, usa il .simbolo. Per esempio:

mydt [, .N,. (Hobbyist, OpenSourcer)]

Per ordinare i risultati dal più alto al più basso, puoi aggiungere un secondo gruppo di parentesi dopo il primo. Il .Nsimbolo genera automaticamente una colonna denominata N (ovviamente puoi rinominarla se vuoi), quindi l'ordinamento in base al numero di righe può assomigliare a questo:

mydt [, .N,. (Hobbyist, OpenSourcer)] [order (Hobbyist, -N)]

Man mano che imparo il codice data.table, trovo utile leggerlo passo dopo passo. Quindi leggo questo come "Per tutte le righe in mydt (poiché non c'è nulla nella posizione" I "), conta il numero di righe, raggruppando per Hobbyist e OpenSourcer. Quindi ordina prima per Hobbyist e poi il numero di righe decrescente. " 

È equivalente a questo codice dplyr:

mydf%>%

count (Hobbyist, OpenSourcer)%>%

ordine (Hobbista, -n)

Se trovi più leggibile l'approccio multi-linea convenzionale tidyverse, questo codice data.table funziona anche:

mydt [, .N,

. (Hobbyist, OpenSourcer)] [

ordine (Hobbista, -N)

]

Aggiungi colonne a un data.table

Successivamente, vorrei aggiungere colonne per vedere se ogni intervistato usa R, se usa Python, se usa entrambi o se non usa nessuno dei due. La LanguageWorkedWithcolonna contiene informazioni sulle lingue utilizzate e alcune righe di tali dati hanno questo aspetto:

Sharon Machlis

Ogni risposta è una singola stringa di caratteri. La maggior parte ha più lingue separate da un punto e virgola.

Come spesso accade, è più facile cercare Python che R, poiché non puoi cercare semplicemente "R" nella stringa (Ruby e Rust contengono anche una R maiuscola) nel modo in cui puoi cercare "Python". Questo è il codice più semplice per creare un vettore VERO / FALSO che controlla se ogni stringa in LanguageWorkedWithcontiene Python:

ifelse (LanguageWorkedWith% like% "Python", TRUE, FALSE)

Se conosci SQL, riconoscerai quella sintassi "mi piace". Io, beh, come %like%. se fosse un bel modo semplificato per verificare la corrispondenza dei pattern. La documentazione della funzione dice che è pensata per essere utilizzata all'interno delle parentesi data.table, ma in realtà puoi usarla in qualsiasi codice, non solo con data.tables. Ho controllato con il creatore di data.table Matt Dowle, che ha detto che il consiglio di usarlo tra parentesi è perché lì avviene un'ottimizzazione delle prestazioni extra.

Quindi, ecco il codice per aggiungere una colonna chiamata PythonUser a data.table:

dt1 [, PythonUser: = ifelse (LanguageWorkedWith% like% "Python", TRUE, FALSE)]

Notare l' :=operatore. Python ha anche un operatore del genere, e da quando l'ho sentito chiamare "operatore tricheco", è così che lo chiamo. Penso che sia ufficialmente "assegnazione per riferimento". Questo perché il codice sopra ha modificato l'oggetto esistente dt1 data.table aggiungendo la nuova colonna, senza bisogno di salvarlo in una nuova variabile .

Per cercare R, userò l'espressione regolare "\\bR\\b"che dice: "Trova uno schema che inizi con un confine di parola - il \\b, poi un R, e poi finisca con un altro confine di parola. (Non posso semplicemente cercare "R;" perché l'ultimo elemento di ogni stringa non ha un punto e virgola.) 

Questo aggiunge una colonna RUser a dt1:

dt1 [, RUser: = ifelse (LanguageWorkedWith% like% "\\ bR \\ b", TRUE, FALSE)]

Se si desidera aggiungere entrambe le colonne contemporaneamente, :=è necessario trasformare l'operatore di tricheco in una funzione citando all'indietro, in questo modo:

dt1 [, `: =` (

PythonUser = ifelse (LanguageWorkedWith% like% "Python", TRUE, FALSE),

RUser = ifelse (LanguageWorkedWith% like% "\\ bR \\ b", TRUE, FALSE)

)]

Operatori data.table più utili

Ci sono molti altri operatori data.table che vale la pena conoscere. L'  %between% operatore ha questa sintassi:

myvector% tra% c (lower_value, upper_value)

Quindi, se voglio filtrare tutte le risposte in cui il compenso era compreso tra 50.000 e 100.000 pagati in dollari USA, questo codice funziona:

comp_50_100k <- dt1 [CurrencySymbol == "USD" &

ConvertedComp% tra% c (50000, 100000)]

La seconda riga sopra è la condizione tra. Notare che l' %between%operatore include sia il valore inferiore che quello superiore durante il controllo.

Un altro utile operatore è %chin%. Funziona come le R di base %in%ma è ottimizzato per la velocità ed è solo per i vettori di caratteri . Quindi, se voglio filtrare tutte le righe in cui la colonna OpenSourcer era "Mai" o "Meno di una volta all'anno", questo codice funziona:

rareos <- dt1 [OpenSourcer% chin% c ("Mai", "Meno di una volta all'anno")]

Questo è abbastanza simile alla base R, tranne per il fatto che la base R deve specificare il nome del frame di dati all'interno della parentesi e richiede anche una virgola dopo l'espressione del filtro:

rareos_df <- df1 [df1 $ OpenSourcer% in% c ("Mai", "Meno di una volta all'anno"),]

La nuova funzione fcase ()

Per questa demo finale, inizierò creando un nuovo data.table con solo persone che hanno segnalato un compenso in dollari USA:

usd <- dt1 [CurrencySymbol == "USD" &! is.na (ConvertedComp)]

Successivamente, creerò una nuova colonna Languageper indicare se qualcuno usa solo R, solo Python, entrambi o nessuno dei due. E userò la nuova fcase()funzione. Al momento in cui questo articolo è stato pubblicato, fcase()era disponibile solo nella versione di sviluppo di data.table. Se hai già installato data.table, puoi aggiornare all'ultima versione dev con questo comando: 

data.table :: update.dev.pkg ()

La funzione fcase () è simile CASE WHENall'istruzione SQL e alla case_when()funzione dplyr . La sintassi di base è  fcase(condition1, "value1", condition2, "value2")e così via. È possibile aggiungere un valore predefinito per "tutto il resto" default = value.

Ecco il codice per creare la nuova colonna Lingua:

usd [, Lingua: = fcase (

RUser &! PythonUser, "R",

PythonUser &! RUser, "Python",

PythonUser e RUser, "Both",

! PythonUser &! RUser, "Nessuno dei due"

)]

Metto ogni condizione su una riga separata perché trovo che sia più facile da leggere, ma non è necessario.

Attenzione: se stai usando RStudio, la struttura data.table non si aggiorna automaticamente nel riquadro RStudio in alto a destra dopo aver creato una nuova colonna con l'operatore walrus. È necessario fare clic manualmente sull'icona di aggiornamento per visualizzare le modifiche nel numero di colonne.

Ci sono alcuni altri simboli che non tratterò in questo articolo. È possibile trovarne un elenco nel file della guida data.table "simboli speciali" eseguendo help("special-symbols"). Uno dei più utili, .SD, ha già il suo articolo e il video "Fai di più con R", "Come utilizzare .SD nel pacchetto R data.table".

Per ulteriori suggerimenti R, vai alla pagina "Fai di più con R" o controlla la playlist di YouTube "Fai di più con R".