กำหนดหลายคอลัมน์โดยใช้: = ใน data.table ตามกลุ่ม


130

วิธีที่ดีที่สุดในการกำหนดให้หลายคอลัมน์โดยใช้data.tableคืออะไร? ตัวอย่างเช่น:

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

ฉันต้องการทำสิ่งนี้ (แน่นอนว่าไวยากรณ์นี้ไม่ถูกต้อง):

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

และเพื่อขยายนั้นฉันอาจมีหลายคอลัมน์ที่มีชื่อเก็บไว้ในตัวแปร (พูดcol_names) และฉันต้องการทำ:

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

อะไรคือวิธีที่ถูกต้องในการทำสิ่งนี้?


1
ดูเหมือนจะได้รับคำตอบแล้ว: stackoverflow.com/questions/11308754/…
Alex

อเล็กซ์คำตอบนั้นใกล้เคียง แต่ดูเหมือนจะใช้ไม่ได้เมื่อใช้ร่วมกับby@Christoph_J จะพูดได้ถูกต้อง ลิงก์ไปยังคำถามของคุณที่เพิ่มไปยังFR # 2120 "Drop needs with = FALSE for LHS of: =" ดังนั้นจะไม่ลืมที่จะกลับมาอีก
Matt Dowle

เพื่อความชัดเจนf()คือฟังก์ชันที่ส่งคืนค่าหลายค่าสำหรับแต่ละคอลัมน์ของคุณ
smci

คำตอบ:


161

ตอนนี้ใช้งานได้ใน v1.8.3 บน R-Forge ขอบคุณสำหรับการเน้นย้ำ!

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

เวอร์ชันเก่าที่ใช้withอาร์กิวเมนต์ (เราไม่สนับสนุนข้อโต้แย้งนี้เมื่อเป็นไปได้):

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

ขอบคุณสำหรับคำตอบและตัวอย่างนี้ ฉันควรแก้ไขบรรทัดต่อไปนี้อย่างไรเพื่อให้ได้สองคอลัมน์สำหรับแต่ละ objectName จากเอาต์พุตสลัวแทนที่จะเป็นคอลัมน์เดียวที่มีสองแถว data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](ฉันใช้data.table1.8.11)
dnlbrky

@dnlbrky dimส่งคืนเวกเตอร์ดังนั้นการแปลงเป็นประเภทlistควรหมุน เช่น[,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me] . ปัญหาคือas.listมีค่าใช้จ่ายในการโทรและคัดลอกเวกเตอร์ขนาดเล็ก หากประสิทธิภาพเป็นปัญหาเนื่องจากจำนวนกลุ่มเพิ่มขึ้นโปรดแจ้งให้เราทราบ
Matt Dowle

1
สวัสดี Matt. ตัวอย่างแรกในบล็อกรหัสที่สองของคุณ (เช่นx[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][]) ตอนนี้แสดงคำเตือนดังนั้นอาจลบออก? ในบันทึกที่เกี่ยวข้องมีใครแนะนำว่าoptions(datatable.WhenJisSymbolThenCallingScope=TRUE)งานที่ได้รับมอบหมายx[,mynames:=list(mean(b)*4,sum(b)*3),by=a]ควรจะได้ผลหรือไม่? ดูเหมือนว่าจะสอดคล้องกับการเปลี่ยนแปลงอื่น ๆ แต่ฉันเดาว่ามันอาจทำลายรหัสผู้ใช้ที่มีอยู่มากเกินไป (?)
Josh O'Brien

1
@ PanFrancisco หากไม่มีby=aมันจะทำงาน แต่กลับคำตอบที่ต่างออกไป mean(a)และมวลจะถูกนำกลับมาใช้ในแต่ละกลุ่มเมื่อsum(a) by=aโดยไม่by=aเพียงแค่ยึดคอลัมน์meanและsumสำหรับคอลัมน์ทั้งหมดในแต่ละเซลล์ (เช่นตัวเลขต่างกัน)
Matt Dowle

1
@MattDowle จะเกิดอะไรขึ้นถ้าฟังก์ชันของฉันส่งคืนรายการที่มีชื่อแล้วฉันสามารถเพิ่มคอลัมน์ลงใน dt ได้โดยไม่ต้องตั้งชื่ออีกหรือไม่ เช่น f <- function (x) {list ("c" = "hi", "d" = "hello")} จะพิมพ์ผลลัพธ์ที่มีชื่อ cols ด้วย x [, f (), by = a] [] ฉันไม่รู้ว่าจะผนวกผลลัพธ์กับ dt อย่างไร
Jfly

48

สัญกรณ์ชวเลขต่อไปนี้อาจเป็นประโยชน์ เครดิตทั้งหมดไปที่แอนดรูบรูคส์โดยเฉพาะบทความนี้

dt[,`:=`(avg=mean(mpg), med=median(mpg), min=min(mpg)), by=cyl]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.