Gán nhiều cột bằng: = in data.table, theo nhóm


130

Cách tốt nhất để gán cho nhiều cột bằng cách sử dụng là data.tablegì? Ví dụ:

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

Tôi muốn làm một cái gì đó như thế này (tất nhiên cú pháp này không chính xác):

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

Và để mở rộng điều đó, tôi có thể có nhiều cột với tên được lưu trữ trong một biến (giả sử col_names) và tôi muốn làm:

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

Cách chính xác để làm một cái gì đó như thế này là gì?


1
Điều này có vẻ như đã được trả lời: stackoverflow.com/questions/11308754/ Kẻ
Alex

Alex, câu trả lời đó rất gần nhưng dường như nó không hoạt động kết hợp với byvì @Christoph_J là chính xác để nói. Liên kết đến câu hỏi của bạn được thêm vào FR # 2120 "Thả cần với = FALSE cho LHS của: =", vì vậy nó sẽ không bị quên để xem lại.
Matt Dowle

Để rõ ràng, f()là một hàm trả về nhiều giá trị, một giá trị cho mỗi cột của bạn.
smci

Câu trả lời:


161

Điều này hiện hoạt động trong v1.8.3 trên R-Forge. Cảm ơn đã làm nổi bật nó!

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

Phiên bản cũ hơn sử dụng withđối số (chúng tôi không khuyến khích đối số này khi có thể):

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

Cảm ơn câu trả lời này và các ví dụ. Tôi nên sửa đổi dòng sau như thế nào để có được hai cột cho mỗi objectName từ đầu ra mờ, thay vì một cột có hai hàng? data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](Tôi đang sử dụng data.table1.8.11)
dnlbrky

@dnlbrky dimtrả về một vectơ để chuyển đổi thành kiểu listnên xoay nó; ví dụ [,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]. Rắc rối là as.listcó cuộc gọi trên đầu và cũng sao chép các vectơ nhỏ. Nếu hiệu quả là một vấn đề khi số lượng nhóm tăng lên, xin vui lòng cho chúng tôi biết.
Matt Dowle

1
Chào Matt. Ví dụ đầu tiên trong khối mã thứ hai của bạn (tức là x[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][]) hiện đưa ra cảnh báo, vì vậy có thể loại bỏ nó? Trên một lưu ý liên quan, có ai đề nghị rằng, với options(datatable.WhenJisSymbolThenCallingScope=TRUE), một nhiệm vụ như x[,mynames:=list(mean(b)*4,sum(b)*3),by=a]trong thực tế nên làm việc? Có vẻ như điều đó sẽ phù hợp với các thay đổi khác, mặc dù tôi đoán nó có thể phá vỡ quá nhiều mã người dùng hiện có (?).
Josh O'Brien

1
@PanFrancisco Nếu không có by=anó sẽ hoạt động, nhưng trả lại một câu trả lời khác. Các mean(a)sum(a)tổng hợp đang được tái chế trong mỗi nhóm khi by=a. Không có by=anó, chỉ cần gắn meansumcho toàn bộ cột vào mỗi ô (tức là các số khác nhau).
Matt Dowle

1
@MattDowle nếu chức năng của tôi đã trả về danh sách được đặt tên, thì dù sao tôi cũng có thể thêm các cột vào dt mà không phải đặt lại tên? vd Tôi không biết làm thế nào để nối kết quả vào dt.
Jfly

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.