Przypisz wiele kolumn za pomocą: = w data.table, według grupy

130

Jaki jest najlepszy sposób przypisywania do wielu kolumn przy użyciu data.table? Na przykład:

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

Chciałbym zrobić coś takiego (oczywiście ta składnia jest niepoprawna):

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

Aby to rozszerzyć, mogę mieć wiele kolumn z nazwami przechowywanymi w zmiennej (powiedzmy col_names) i chciałbym zrobić:

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

Jaki jest właściwy sposób zrobienia czegoś takiego?

Alex
źródło
1
Wygląda na to, że udzielono odpowiedzi: stackoverflow.com/questions/11308754/…
Alex
Alex, ta odpowiedź jest bliska, ale wydaje się, że nie działa w połączeniu z, byjak słusznie powiedzieć @Christoph_J. Link do twojego pytania dodany do FR # 2120 "Porzuć wymaganie z = FALSE dla LHS z: =", więc nie zapomnisz o ponownej wizycie.
Matt Dowle
Aby było jasne, f()jest to funkcja zwracająca wiele wartości, po jednej dla każdej kolumny.
smci

Odpowiedzi:

161

Działa to teraz w wersji 1.8.3 w R-Forge. Dzięki za podkreślenie tego!

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

Starsza wersja używająca withargumentu (odradzamy ten argument, jeśli to możliwe):

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
Matt Dowle
źródło
Dzięki za tę odpowiedź i przykłady. Jak powinienem zmodyfikować następujący wiersz, aby uzyskać dwie kolumny dla każdego objectName z wyjścia dim, zamiast jednej kolumny z dwoma wierszami? data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](Używam data.table1.8.11)
dnlbrky
@dnlbrky dimzwraca wektor, więc konwertując go, aby wpisać tekst , listnależy go obrócić; np [,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]. Problem polega na tym, że as.listnarzut wywołania, a także kopiuje mały wektor. Jeśli wydajność jest problemem w miarę wzrostu liczby grup, prosimy o poinformowanie nas o tym.
Matt Dowle
1
Cześć Matt. Pierwszy przykład w drugim bloku kodu (tj. x[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][]) Generuje teraz ostrzeżenie, więc może je usunąć? A propos, czy ktoś zasugerował, że options(datatable.WhenJisSymbolThenCallingScope=TRUE)zadanie takie jak x[,mynames:=list(mean(b)*4,sum(b)*3),by=a]powinno w rzeczywistości zadziałać? Wydaje się, że byłoby to spójne z innymi zmianami, chociaż wydaje mi się, że może to spowodować zbyt duże uszkodzenie istniejącego kodu użytkownika (?).
Josh O'Brien
1
@PanFrancisco Bez by=atego zadziała, ale zwróci inną odpowiedź. mean(a)I sum(a)kruszywo są utylizowane w każdej grupie Kiedy by=a. Bez by=atego po prostu wkleja meani sumdla całej kolumny do każdej komórki (tj. Różne liczby).
Matt Dowle,
1
@MattDowle co jeśli moja funkcja zwraca już nazwaną listę, czy mimo wszystko mogę dodać kolumny do dt bez konieczności ponownego nadawania im nazwy? np. f ​​<- function (x) {list ("c" = "hi", "d" = "hello")} wyświetli wyniki z nazwanymi kolumnami z x [, f (), by = a] []. Nie wiem, jak dołączyć wynik do dt.
Jfly