Assegna più colonne usando: = in data.table, per gruppo


130

Qual è il modo migliore per assegnare a più colonne usando data.table? Per esempio:

f <- function(x) {c("hi", "hello")}
x <- data.table(id = 1:10)

Vorrei fare qualcosa del genere (ovviamente questa sintassi non è corretta):

x[ , (col1, col2) := f(), by = "id"]

E per estenderlo, potrei avere molte colonne con nomi memorizzati in una variabile (diciamo col_names) e vorrei fare:

x[ , col_names := another_f(), by = "id", with = FALSE]

Qual è il modo corretto di fare qualcosa del genere?


1
Sembra che abbia ricevuto risposta: stackoverflow.com/questions/11308754/…
Alex

Alex, quella risposta è vicina ma non sembra funzionare in combinazione con bycome ha ragione @Christoph_J. Collegamento alla tua domanda aggiunta a FR # 2120 "Rilascio necessario con = FALSO per LHS di: =", in modo che non venga dimenticato di rivisitare.
Matt Dowle,

Per essere chiari, f()è una funzione che restituisce più valori, uno per ciascuna delle tue colonne.
smci,

Risposte:


161

Ora funziona in v1.8.3 su R-Forge. Grazie per averlo evidenziato!

x <- data.table(a = 1:3, b = 1:6) 
f <- function(x) {list("hi", "hello")} 
x[ , c("col1", "col2") := f(), by = a][]
#    a b col1  col2
# 1: 1 1   hi hello
# 2: 2 2   hi hello
# 3: 3 3   hi hello
# 4: 1 4   hi hello
# 5: 2 5   hi hello
# 6: 3 6   hi hello

x[ , c("mean", "sum") := list(mean(b), sum(b)), by = a][]
#    a b col1  col2 mean sum
# 1: 1 1   hi hello  2.5   5
# 2: 2 2   hi hello  3.5   7
# 3: 3 3   hi hello  4.5   9
# 4: 1 4   hi hello  2.5   5
# 5: 2 5   hi hello  3.5   7
# 6: 3 6   hi hello  4.5   9 

mynames = c("Name1", "Longer%")
x[ , (mynames) := list(mean(b) * 4, sum(b) * 3), by = a]
#     a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27


x[ , get("mynames") := list(mean(b) * 4, sum(b) * 3), by = a][]  # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

x[ , eval(mynames) := list(mean(b) * 4, sum(b) * 3), by = a][]   # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

Versione precedente usando l' withargomento (scoraggiamo questo argomento quando possibile):

x[ , mynames := list(mean(b) * 4, sum(b) * 3), by = a, with = FALSE][] # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

Grazie per questa risposta e gli esempi. Come devo modificare la seguente riga per ottenere due colonne per ciascun objectName dall'output attenuato, anziché una colonna con due righe? data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](Sto usando l' data.table1.8.11)
dnlbrky

@dnlbrky dimrestituisce un vettore in modo che la conversione da digitare listdebba ruotarlo; es [,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]. Il problema è che as.listha un overhead di chiamata e copia anche il piccolo vettore. Se l'efficienza è un problema all'aumentare del numero di gruppi, fatecelo sapere.
Matt Dowle,

1
Ciao Matt. Il primo esempio nel secondo blocco di codice (ovvero x[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][]) ora genera un avviso, quindi forse rimuoverlo? Su una nota correlata, qualcuno ha suggerito che, con options(datatable.WhenJisSymbolThenCallingScope=TRUE), un compito come x[,mynames:=list(mean(b)*4,sum(b)*3),by=a]dovrebbe effettivamente funzionare? Sembra che sarebbe coerente con le altre modifiche, anche se immagino che potrebbe rompere troppo codice utente esistente (?).
Josh O'Brien,

1
@PanFrancisco Senza by=afunzionerà, ma restituirà una risposta diversa. Gli aggregati mean(a)e sum(a)vengono riciclati all'interno di ciascun gruppo quando by=a. Senza di by=aessa, attacca semplicemente meane sumper l'intera colonna in ogni cella (ovvero numeri diversi).
Matt Dowle,

1
@MattDowle cosa succede se la mia funzione restituisce già un elenco di nomi, è possibile aggiungere le colonne al dt senza doverle rinominare? es. f <- function (x) {list ("c" = "hi", "d" = "hello")} stamperà i risultati con i col nome con x [, f (), by = a] []. Non so come aggiungere il risultato al dt.
Jfly

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.