Tetapkan beberapa kolom menggunakan: = dalam data.table, berdasarkan grup

130

Apa cara terbaik untuk menggunakan beberapa kolom data.table? Sebagai contoh:

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

Saya ingin melakukan sesuatu seperti ini (tentu saja sintaks ini salah):

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

Dan untuk memperluas itu, saya mungkin memiliki banyak kolom dengan nama yang disimpan dalam variabel (katakanlah col_names) dan saya ingin melakukan:

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

Apa cara yang benar untuk melakukan sesuatu seperti ini?

Alex
sumber
1
Ini sepertinya sudah dijawab: stackoverflow.com/questions/11308754/…
Alex
Alex, Jawaban itu dekat tetapi tampaknya tidak bekerja bersama- bysama dengan @Christoph_J benar untuk mengatakan. Tautkan ke pertanyaan Anda yang ditambahkan ke FR # 2120 "Drop needing with = FALSE for LHS of: =", sehingga tidak akan lupa untuk mengunjungi kembali.
Matt Dowle
Agar jelas, f()adalah fungsi yang mengembalikan beberapa nilai, satu untuk setiap kolom Anda.
smci

Jawaban:

161

Ini sekarang bekerja di v1.8.3 pada R-Forge. Terima kasih telah menyorotnya!

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

Versi yang lebih lama menggunakan withargumen (kami mencegah argumen ini bila memungkinkan):

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
sumber
Terima kasih atas jawaban dan contohnya. Bagaimana saya harus memodifikasi baris berikut untuk mendapatkan dua kolom untuk setiap objectName dari keluaran redup, daripada satu kolom dengan dua baris? data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](Saya menggunakan data.table1.8.11)
dnlbrky
@ dnlbrky dimmengembalikan vektor sehingga mengonversi itu untuk mengetik listharus memutarnya; mis [,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]. Masalahnya adalah yang as.listmemiliki panggilan overhead dan juga menyalin vektor kecil. Jika efisiensi menjadi masalah karena jumlah grup meningkat maka beri tahu kami.
Matt Dowle
1
Hai Matt. Contoh pertama di blok kode kedua Anda (yaitu x[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][]) sekarang melempar peringatan, jadi mungkin menghapusnya? Pada catatan terkait, adakah yang menyarankan agar, dengan options(datatable.WhenJisSymbolThenCallingScope=TRUE), penugasan seperti x[,mynames:=list(mean(b)*4,sum(b)*3),by=a]apakah sebenarnya berhasil? Sepertinya itu akan konsisten dengan perubahan lain, meskipun saya kira itu mungkin merusak terlalu banyak kode pengguna yang ada (?).
Josh O'Brien
1
@ PanFrancisco Tanpa by=aitu akan berhasil, tetapi kembalikan jawaban yang berbeda. The mean(a)dan sum(a)agregat sedang didaur ulang dalam setiap kelompok saat by=a. Tanpanya by=ahanya akan menempelkan meandan sumuntuk seluruh kolom ke dalam setiap sel (yaitu angka yang berbeda).
Matt Dowle
1
@MattDowle bagaimana jika fungsi saya sudah mengembalikan daftar bernama, adakah di sana saya dapat menambahkan kolom ke dt tanpa harus menamai mereka lagi? misal f <- function (x) {list ("c" = "hi", "d" = "hello")} akan mencetak hasil dengan nama cols dengan x [, f (), dengan = a] []. Saya tidak tahu bagaimana menambahkan hasilnya ke dt.
Jfly
48

Notasi steno berikut mungkin berguna. Semua kredit diberikan kepada Andrew Brooks, khususnya artikel ini .

dt[,`:=`(avg=mean(mpg), med=median(mpg), min=min(mpg)), by=cyl]
Gerry
sumber