data.table
Rimodellamento, accatastamento e divisione
Ricerca…
Osservazioni
La bozza ufficiale, "Rimodellamento efficiente con data.tables" , è la migliore introduzione a questo argomento.
Molti compiti di rimodellamento richiedono lo spostamento tra formati lunghi e ampi:
- I dati ampi sono dati con ogni colonna che rappresenta una variabile separata e le righe che rappresentano le osservazioni separate
- I dati lunghi sono dati con l'ID del modulo | variabile | valore, dove ogni riga rappresenta una coppia di osservazioni-variabili
fondere e lanciare con data.table
data.table
offre una vasta gamma di possibilità per rimodellare i tuoi dati in modo efficiente e semplice
Ad esempio, mentre si rimodella da lungo a largo è possibile passare diverse variabili nel value.var
e nei parametri fun.aggregate
allo stesso tempo
library(data.table) #v>=1.9.6
DT <- data.table(mtcars)
Da lungo a largo
dcast(DT, gear ~ cyl, value.var = c("disp", "hp"), fun = list(mean, sum))
gear disp_mean_4 disp_mean_6 disp_mean_8 hp_mean_4 hp_mean_6 hp_mean_8 disp_sum_4 disp_sum_6 disp_sum_8 hp_sum_4 hp_sum_6 hp_sum_8
1: 3 120.100 241.5 357.6167 97 107.5 194.1667 120.1 483.0 4291.4 97 215 2330
2: 4 102.625 163.8 NaN 76 116.5 NaN 821.0 655.2 0.0 608 466 0
3: 5 107.700 145.0 326.0000 102 175.0 299.5000 215.4 145.0 652.0 204 175 599
Questo imposterà la gear
come colonna dell'indice, mentre la mean
e la sum
saranno calcolate per disp
e hp
per ogni combinazione di gear
e cyl
. Nel caso in cui alcune combinazioni non esistano, è possibile specificare parametri aggiuntivi come na.rm = TRUE
(che verrà passato per mean
e sum
funzioni) o specificare l'argomento di fill
incorporato. È anche possibile aggiungere margini, eliminare combinazioni mancanti e sottoporre a subset i dati. Vedi di più in ?data.table::dcast
Largo a lungo
Per quanto riguarda il rimodellamento da ampio a lungo, è possibile passare colonne al parametro measure.vars
utilizzando le espressioni regolari, ad esempio
print(melt(DT, c("cyl", "gear"), measure = patterns("^d", "e")), n = 10)
cyl gear variable value1 value2
1: 6 4 1 160.00 16.46
2: 6 4 1 160.00 17.02
3: 4 4 1 108.00 18.61
4: 6 3 1 258.00 19.44
5: 8 3 1 360.00 17.02
---
60: 4 5 2 3.77 5.00
61: 8 5 2 4.22 5.00
62: 6 5 2 3.62 5.00
63: 8 5 2 3.54 5.00
64: 4 4 2 4.11 4.00
Questo melt
i dati per cyl
e gear
come le colonne indice, mentre tutti i valori per le variabili che iniziano con d
( disp
& drat
) saranno presenti in value1
e i valori per le variabili che contengono la lettera e
in essi ( qsec
e gear
) saranno presenti nella colonna value2
.
È inoltre possibile rinominare tutti i nomi di colonna nel risultato specificando value.name
argomenti variable.name
e value.name
o decidere se si desidera che le colonne di character
vengano automaticamente convertite in factor
o senza specificare value.factor
argomenti variable.factor
e value.factor
. Vedi di più in ?data.table::melt
Risagoma usando `data.table`
data.table
estende le reshape2
di melt
e dcast
( Riferimento: rimodellamento efficiente con data.tables )
library(data.table)
## generate some data
dt <- data.table(
name = rep(c("firstName", "secondName"), each=4),
numbers = rep(1:4, 2),
value = rnorm(8)
)
dt
# name numbers value
# 1: firstName 1 -0.8551881
# 2: firstName 2 -1.0561946
# 3: firstName 3 0.2671833
# 4: firstName 4 1.0662379
# 5: secondName 1 -0.4771341
# 6: secondName 2 1.2830651
# 7: secondName 3 -0.6989682
# 8: secondName 4 -0.6592184
Da lungo a largo
dcast(data = dt,
formula = name ~ numbers,
value.var = "value")
# name 1 2 3 4
# 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814
Su più colonne (come data.table
1.9.6)
## add an extra column
dt[, value2 := value * 2]
## cast multiple value columns
dcast(data = dt,
formula = name ~ numbers,
value.var = c("value", "value2"))
# name value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4
# 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078 0.3672866 -1.6712572 3.190562 0.6590155
# 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814 -1.6409368 0.9748581 1.476649 1.1515627
Da largo a lungo
## use a wide data.table
dt <- fread("name 1 2 3 4
firstName 0.1836433 -0.8356286 1.5952808 0.3295078
secondName -0.8204684 0.4874291 0.7383247 0.5757814", header = T)
dt
# name 1 2 3 4
# 1: firstName 0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684 0.4874291 0.7383247 0.5757814
## melt to long, specifying the id column, and the name of the columns
## in the resulting long data.table
melt(dt,
id.vars = "name",
variable.name = "numbers",
value.name = "myValue")
# name numbers myValue
# 1: firstName 1 0.1836433
# 2: secondName 1 -0.8204684
# 3: firstName 2 -0.8356286
# 4: secondName 2 0.4874291
# 5: firstName 3 1.5952808
# 6: secondName 3 0.7383247
# 7: firstName 4 0.3295078
# 8: secondName 4 0.5757814
Passando dal formato wide a long usando il fuso
Fusione: le basi
La fusione viene utilizzata per trasformare i dati da un formato ampio a uno lungo.
A partire da un ampio set di dati:
DT = data.table(ID = letters[1:3], Age = 20:22, OB_A = 1:3, OB_B = 4:6, OB_C = 7:9)
Possiamo fondere i nostri dati usando la funzione melt
in data.table. Questo restituisce un altro data.table in formato lungo:
melt(DT, id.vars = c("ID","Age"))
1: a 20 OB_A 1
2: b 21 OB_A 2
3: c 22 OB_A 3
4: a 20 OB_B 4
5: b 21 OB_B 5
6: c 22 OB_B 6
7: a 20 OB_C 7
8: b 21 OB_C 8
9: c 22 OB_C 9
class(melt(DT, id.vars = c("ID","Age")))
# "data.table" "data.frame"
Si presume che le colonne non impostate nel parametro id.vars
siano variabili. In alternativa, possiamo impostarli esplicitamente usando l'argomento measure.vars
:
melt(DT, measure.vars = c("OB_A","OB_B","OB_C"))
ID Age variable value
1: a 20 OB_A 1
2: b 21 OB_A 2
3: c 22 OB_A 3
4: a 20 OB_B 4
5: b 21 OB_B 5
6: c 22 OB_B 6
7: a 20 OB_C 7
8: b 21 OB_C 8
9: c 22 OB_C 9
In questo caso, si presume che tutte le colonne non impostate in measure.vars
siano ID.
Se impostiamo entrambi in modo esplicito, restituirà solo le colonne selezionate:
melt(DT, id.vars = "ID", measure.vars = c("OB_C"))
ID variable value
1: a OB_C 7
2: b OB_C 8
3: c OB_C 9
Denominazione di variabili e valori nel risultato
Possiamo manipolare i nomi delle colonne della tabella restituita usando variable.name
e value.name
melt(DT,
id.vars = c("ID"),
measure.vars = c("OB_C"),
variable.name = "Test",
value.name = "Result"
)
ID Test Result
1: a OB_C 7
2: b OB_C 8
3: c OB_C 9
Impostazione dei tipi per misurare le variabili nel risultato
Per impostazione predefinita, la fusione di un data.table converte tutti i valori da measure.vars
a fattori:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
class(M_DT[, variable])
# "factor"
Per impostare come carattere, utilizzare l'argomento variable.factor
:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), variable.factor = FALSE)
class(M_DT[, variable])
# "character"
I valori generalmente ereditano dal tipo di dati della colonna di origine:
class(DT[, value])
# "integer"
class(M_DT[, value])
# "integer"
Se c'è un conflitto, i tipi di dati saranno forzati. Per esempio:
M_DT <- melt(DT,id.vars = c("Age"), measure.vars = c("ID","OB_C"))
class(M_DT[, value])
# "character"
Quando si scioglie, qualsiasi variabile fattore sarà forzata al tipo di carattere:
DT[, OB_C := factor(OB_C)]
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
class(M_DT)
# "character"
Per evitare ciò e preservare la digitazione iniziale, utilizzare l'argomento value.factor
:
M_DT <- melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), value.factor = TRUE)
class(M_DT)
# "factor"
Gestione dei valori mancanti
Per impostazione predefinita, tutti i valori NA
vengono conservati nei dati fusi
DT = data.table(ID = letters[1:3], Age = 20:22, OB_A = 1:3, OB_B = 4:6, OB_C = c(7:8,NA))
melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"))
ID variable value
1: a OB_C 7
2: b OB_C 8
3: c OB_C NA
Se questi devono essere rimossi dai tuoi dati, imposta na.rm = TRUE
melt(DT,id.vars = c("ID"), measure.vars = c("OB_C"), na.rm = TRUE)
ID variable value
1: a OB_C 7
2: b OB_C 8
Passando dal formato lungo al formato wide usando dcast
Casting: The Basics
La trasmissione viene utilizzata per trasformare i dati dal formato lungo al formato wide.
A partire da un lungo set di dati:
DT = data.table(ID = rep(letters[1:3],3), Age = rep(20:22,3), Test = rep(c("OB_A","OB_B","OB_C"), each = 3), Result = 1:9)
Possiamo dcast
i nostri dati utilizzando la funzione dcast in data.table. Questo restituisce un altro data.table in formato ampio:
dcast(DT, formula = ID ~ Test, value.var = "Result")
ID OB_A OB_B OB_C
1: a 1 4 7
2: b 2 5 8
3: c 3 6 9
class(dcast(DT, formula = ID ~ Test, value.var = "Result"))
[1] "data.table" "data.frame"
Casting un valore
Un argomento value.var
è necessario per un cast corretto - se non viene fornito dcast farà un'ipotesi basata sui tuoi dati.
dcast(DT, formula = ID ~ Test, value.var = "Result")
ID OB_A OB_B OB_C
1: a 1 4 7
2: b 2 5 8
3: c 3 6 9
ID OB_A OB_B OB_C
1: a 20 20 20
2: b 21 21 21
3: c 22 22 22
Più value.var
possono essere forniti in un elenco
dcast(DT, formula = ID ~ Test, value.var = list("Result","Age"))
ID Result_OB_A Result_OB_B Result_OB_C Age_OB_A Age_OB_B Age_OB_C
1: a 1 4 7 20 20 20
2: b 2 5 8 21 21 21
3: c 3 6 9 22 22 22
Formula
La trasmissione è controllata utilizzando l'argomento formula in dcast
. Questo è nella forma ROWS ~ COLUMNS
dcast(DT, formula = ID ~ Test, value.var = "Result")
ID OB_A OB_B OB_C
1: a 1 4 7
2: b 2 5 8
3: c 3 6 9
dcast(DT, formula = Test ~ ID, value.var = "Result")
Test a b c
1: OB_A 1 2 3
2: OB_B 4 5 6
3: OB_C 7 8 9
Sia le righe che le colonne possono essere espanse con ulteriori variabili usando +
dcast(DT, formula = ID + Age ~ Test, value.var = "Result")
ID Age OB_A OB_B OB_C
1: a 20 1 4 7
2: b 21 2 5 8
3: c 22 3 6 9
dcast(DT, formula = ID ~ Age + Test, value.var = "Result")
ID 20_OB_A 20_OB_B 20_OB_C 21_OB_A 21_OB_B 21_OB_C 22_OB_A 22_OB_B 22_OB_C
1: a 1 4 7 NA NA NA NA NA NA
2: b NA NA NA 2 5 8 NA NA NA
3: c NA NA NA NA NA NA 3 6 9
#order is important
dcast(DT, formula = ID ~ Test + Age, value.var = "Result")
ID OB_A_20 OB_A_21 OB_A_22 OB_B_20 OB_B_21 OB_B_22 OB_C_20 OB_C_21 OB_C_22
1: a 1 NA NA 4 NA NA 7 NA NA
2: b NA 2 NA NA 5 NA NA 8 NA
3: c NA NA 3 NA NA 6 NA NA 9
La fusione può spesso creare celle in cui non esiste alcuna osservazione nei dati. Di default questo è denotato da NA
, come sopra. Possiamo sovrascriverlo con l'argomento fill=
.
dcast(DT, formula = ID ~ Test + Age, value.var = "Result", fill = 0)
ID OB_A_20 OB_A_21 OB_A_22 OB_B_20 OB_B_21 OB_B_22 OB_C_20 OB_C_21 OB_C_22
1: a 1 0 0 4 0 0 7 0 0
2: b 0 2 0 0 5 0 0 8 0
3: c 0 0 3 0 0 6 0 0 9
È inoltre possibile utilizzare due variabili speciali nell'oggetto formula
-
.
non rappresenta altre variabili -
...
rappresenta tutte le altre variabili
dcast(DT, formula = Age ~ ., value.var = "Result")
Age .
1: 20 3
2: 21 3
3: 22 3
dcast(DT, formula = ID + Age ~ ..., value.var = "Result")
ID Age OB_A OB_B OB_C
1: a 20 1 4 7
2: b 21 2 5 8
3: c 22 3 6 9
Aggregazione del nostro valore.var
Possiamo anche eseguire il cast e aggregare i valori in un unico passaggio. In questo caso, abbiamo tre osservazioni in ciascuna delle intersezioni di Età e ID. Per impostare quale aggregazione vogliamo, usiamo l'argomento fun.aggregate
:
#length
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = length)
ID 20 21 22
1: a 3 0 0
2: b 0 3 0
3: c 0 0 3
#sum
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = sum)
ID 20 21 22
1: a 12 0 0
2: b 0 15 0
3: c 0 0 18
#concatenate
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = function(x){paste(x,collapse = "_")})
ID 20 21 22
1: a 1_4_7
2: b 2_5_8
3: c 3_6_9
Possiamo anche passare un elenco a fun.aggregate
per utilizzare più funzioni
dcast(DT, formula = ID ~ Age, value.var = "Result", fun.aggregate = list(sum,length))
ID Result_sum_20 Result_sum_21 Result_sum_22 Result_length_20 Result_length_21 Result_length_22
1: a 12 0 0 3 0 0
2: b 0 15 0 0 3 0
3: c 0 0 18 0 0 3
Se passiamo più di una funzione e più di un valore, possiamo calcolare tutte le combinazioni passando un vettore di value.vars
dcast(DT, formula = ID ~ Age, value.var = c("Result","Test"), fun.aggregate = list(function(x){paste0(x,collapse = "_")},length))
ID Result_function_20 Result_function_21 Result_function_22 Test_function_20 Test_function_21 Test_function_22 Result_length_20 Result_length_21
1: a 1_4_7 OB_A_OB_B_OB_C 3 0
2: b 2_5_8 OB_A_OB_B_OB_C 0 3
3: c 3_6_9 OB_A_OB_B_OB_C 0 0
Result_length_22 Test_length_20 Test_length_21 Test_length_22
1: 0 3 0 0
2: 0 0 3 0
3: 3 0 0 3
dove ogni coppia è calcolata nell'ordine value1_formula1, value1_formula2, ... , valueN_formula(N-1), valueN_formulaN
.
In alternativa, possiamo valutare i nostri valori e le funzioni uno a uno passando 'value.var' come lista:
dcast(DT, formula = ID ~ Age, value.var = list("Result","Test"), fun.aggregate = list(function(x){paste0(x,collapse = "_")},length))
ID Result_function_20 Result_function_21 Result_function_22 Test_length_20 Test_length_21 Test_length_22
1: a 1_4_7 3 0 0
2: b 2_5_8 0 3 0
3: c 3_6_9 0 0 3
Denominazione delle colonne nel risultato
Per impostazione predefinita, i componenti del nome della colonna sono separati da un carattere di sottolineatura _
. Questo può essere sovrascritto manualmente usando l'argomento sep=
:
dcast(DT, formula = Test ~ ID + Age, value.var = "Result")
Test a_20 b_21 c_22
1: OB_A 1 2 3
2: OB_B 4 5 6
3: OB_C 7 8 9
dcast(DT, formula = Test ~ ID + Age, value.var = "Result", sep = ",")
Test a,20 b,21 c,22
1: OB_A 1 2 3
2: OB_B 4 5 6
3: OB_C 7 8 9
Questo fun.aggregate
qualsiasi fun.aggregate
o value.var
che usiamo:
dcast(DT, formula = Test ~ ID + Age, value.var = "Result", fun.aggregate = c(sum,length), sep = ",")
Test Result,sum,a,20 Result,sum,b,21 Result,sum,c,22 Result,length,a,20 Result,length,b,21 Result,length,c,22
1: OB_A 1 2 3 1 1 1
2: OB_B 4 5 6 1 1 1
3: OB_C 7 8 9 1 1 1
Impilare più tabelle usando rbindlist
Un ritornello comune in R segue queste linee:
Non dovresti avere un gruppo di tabelle correlate con nomi come
DT1
,DT2
, ...,DT11
. Leggere e assegnare in modo iterativo agli oggetti per nome è disordinato. La soluzione è un elenco di tabelle di dati!
Un simile elenco sembra
set.seed(1)
DT_list = lapply(setNames(1:3, paste0("D", 1:3)), function(i)
data.table(id = 1:2, v = sample(letters, 2)))
$D1
id v
1: 1 g
2: 2 j
$D2
id v
1: 1 o
2: 2 w
$D3
id v
1: 1 f
2: 2 w
Un'altra prospettiva è che dovresti memorizzare queste tabelle insieme come una tabella , impilandole. Questo è semplice da fare usando rbindlist
:
DT = rbindlist(DT_list, id="src")
src id v
1: D1 1 g
2: D1 2 j
3: D2 1 o
4: D2 2 w
5: D3 1 f
6: D3 2 w
Questo formato ha molto più senso con la sintassi data.table, dove le operazioni "per gruppo" sono comuni e semplici.
Per uno sguardo più approfondito, la risposta di Gregor potrebbe essere un buon punto di partenza. Controlla anche ?rbindlist
, ovviamente. C'è un esempio separato che riguarda la lettura in un gruppo di tabelle da CSV e quindi impilarle .