Tại sao tham gia X [Y] của data.tables không cho phép tham gia bên ngoài đầy đủ hoặc tham gia bên trái?


123

Đây là một chút câu hỏi triết học về cú pháp tham gia data.table. Tôi đang tìm thấy ngày càng nhiều cách sử dụng cho data.tables, nhưng vẫn đang học ...

Định dạng nối X[Y]cho data.tables rất ngắn gọn, tiện dụng và hiệu quả, nhưng theo như tôi có thể nói, nó chỉ hỗ trợ các phép nối bên trong và các phép nối bên ngoài bên phải. Để có được kết nối bên trái hoặc toàn bộ bên ngoài, tôi cần sử dụng merge:

  • X[Y, nomatch = NA] - tất cả các hàng trong Y - nối ngoài bên phải (mặc định)
  • X[Y, nomatch = 0] - chỉ các hàng có kết quả phù hợp trong cả X và Y - nối bên trong
  • merge(X, Y, all = TRUE) - tất cả các hàng từ cả X và Y - tham gia bên ngoài đầy đủ
  • merge(X, Y, all.x = TRUE) - tất cả các hàng trong X - nối ngoài cùng bên trái

Đối với tôi, có vẻ như sẽ rất hữu ích nếu X[Y]định dạng nối hỗ trợ cả 4 kiểu nối. Có lý do gì mà chỉ có hai loại liên kết được hỗ trợ?

Đối với tôi, các giá trị nomatch = 0nomatch = NAtham số không trực quan cho các hành động đang được thực hiện. Nó là dễ dàng hơn cho tôi để hiểu và nhớ mergecú pháp: all = TRUE, all.x = TRUEall.y = TRUE. Vì X[Y]hoạt động giống với mergenhiều hơn match, tại sao không sử dụng mergecú pháp cho phép nối hơn matchnomatchtham số của hàm ?

Dưới đây là các ví dụ về mã của 4 loại kết hợp:

# sample X and Y data.tables
library(data.table)
X <- data.table(t = 1:4, a = (1:4)^2)
setkey(X, t)
X
#    t  a
# 1: 1  1
# 2: 2  4
# 3: 3  9
# 4: 4 16

Y <- data.table(t = 3:6, b = (3:6)^2)
setkey(Y, t)
Y
#    t  b
# 1: 3  9
# 2: 4 16
# 3: 5 25
# 4: 6 36

# all rows from Y - right outer join
X[Y]  # default
#  t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

X[Y, nomatch = NA]  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

merge(X, Y, by = "t", all.y = TRUE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

identical(X[Y], merge(X, Y, by = "t", all.y = TRUE))
# [1] TRUE

# only rows in both X and Y - inner join
X[Y, nomatch = 0]  
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t")  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t", all = FALSE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

identical( X[Y, nomatch = 0], merge(X, Y, by = "t", all = FALSE) )
# [1] TRUE

# all rows from X - left outer join
merge(X, Y, by = "t", all.x = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16

# all rows from both X and Y - full outer join
merge(X, Y, by = "t", all = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36

Cập nhật: data.table v1.9.6 đã giới thiệu on=cú pháp, cho phép kết hợp đặc biệt trên các trường không phải khóa chính. câu trả lời của jangorecki cho câu hỏi Làm thế nào để nối (hợp nhất) các khung dữ liệu (trong, ngoài, trái, phải)? cung cấp một số ví dụ về các kiểu nối bổ sung mà data.table có thể xử lý.


4
Bạn đã đọc Câu hỏi thường gặp 1.12 chưa? Bạn luôn có thể gọi Y[X]nếu bạn muốn nối ngoài bên trái của X[Y]rbind(Y[X],X[Y])nếu bạn muốn có một bên ngoài đầy đủ tham gia
mnel

Xem câu trả lời của tôi để biết thêm phương pháp tiếp cận data.table cho toàn bộ kết nối bên ngoài
mnel 8/10/12

@mnel, tôi cho rằng unique()cách tiếp cận của bạn dưới đây cho phép tham gia đầy đủ là thích hợp hơn rbind(Y[X],X[Y]), vì rbind sẽ liên quan đến việc sao chép bảng. Có đúng không?
Douglas Clark

theo hiểu biết tốt nhất của tôi, vâng. Tôi chưa kiểm tra xem ba lệnh gọi duy nhất nhỏ hơn có nhanh hơn một lệnh gọi lớn hay không (ví dụ unique(c(unique(X[,t]), unique(Y[,t]))- điều này sẽ hiệu quả hơn về bộ nhớ vì nó chỉ kết hợp hai danh sách sẽ nhỏ hơn hoặc bằng số hàng trong X và Y .
mnel

2
Câu hỏi của bạn như một mô tả tốt; Tôi đã tìm thấy câu trả lời cho câu hỏi của tôi trong câu hỏi của bạn. Cảm ơn
tưới

Câu trả lời:


71

Để trích dẫn từ data.table Câu hỏi thường gặp 1.11 Sự khác biệt giữa X[Y]merge(X, Y)?

X[Y] là một phép nối, tìm kiếm các hàng của X bằng cách sử dụng Y (hoặc khóa của Y nếu nó có) làm chỉ mục.

Y[X] là một phép nối, tra cứu các hàng của Y bằng cách sử dụng X (hoặc khóa của X nếu nó có)

merge(X,Y)thực hiện cả hai cách cùng một lúc. Số hàng của X[Y]Y[X]thường khác nhau, trong khi số hàng được trả về merge(X,Y)và bằng merge(Y,X)nhau.

NHƯNG mà bỏ sót điểm chính. Hầu hết các tác vụ yêu cầu một cái gì đó được thực hiện trên dữ liệu sau khi nối hoặc hợp nhất. Tại sao phải hợp nhất tất cả các cột dữ liệu, chỉ để sử dụng một tập con nhỏ của chúng sau đó? Bạn có thể đề xuất merge(X[,ColsNeeded1],Y[,ColsNeeded2]), nhưng điều đó đòi hỏi người lập trình phải tìm ra những cột nào cần thiết. X[Y,j] trong data.table thực hiện tất cả điều đó trong một bước cho bạn. Khi bạn viết X[Y,sum(foo*bar)], data.table tự động kiểm tra jbiểu thức để xem nó sử dụng những cột nào. Nó sẽ chỉ đặt các cột đó mà thôi; những người khác bị bỏ qua. Bộ nhớ chỉ được tạo cho các cột được jsử dụng và Ycác cột được hưởng các quy tắc tái chế R tiêu chuẩn trong ngữ cảnh của mỗi nhóm. Giả sử foolà trong Xvà thanh ở trong Y(cùng với 20 cột khác ở trong Y). KhôngX[Y,sum(foo*bar)] nhanh hơn để lập trình và nhanh hơn để chạy hơn là hợp nhất mọi thứ một cách lãng phí theo sau bởi một tập hợp con?


Nếu bạn muốn một kết nối bên ngoài bên trái của X[Y]

le <- Y[X]
mallx <- merge(X, Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le, mallx)
# [1] TRUE

Nếu bạn muốn tham gia bên ngoài đầy đủ

# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
##   t  b  a
## 1: 1 NA  1
## 2: 2 NA  4
## 3: 3  9  9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA

# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]

5
Cảm ơn @mnel. Câu hỏi thường gặp 1.12 không đề cập đến kết nối bên ngoài đầy đủ hoặc bên trái. Đề xuất kết hợp bên ngoài đầy đủ của bạn với unique () là một trợ giúp tuyệt vời. Điều đó phải có trong Câu hỏi thường gặp. Tôi biết Matthew Dowle "đã thiết kế nó để sử dụng cho riêng mình và anh ấy muốn nó theo cách đó." (Câu hỏi thường gặp 1.9), nhưng tôi nghĩ X[Y,all=T]có thể là một cách hay để chỉ định một phép nối bên ngoài đầy đủ trong cú pháp data.table X [Y]. Hoặc X[Y,all.x=T]cho phép nối bên trái. Tôi tự hỏi tại sao nó không được thiết kế theo cách đó. Chỉ là một suy nghĩ.
Douglas Clark

1
@DouglasClark Đã thêm câu trả lời và nộp 2302: Thêm cú pháp tham gia hợp nhất của mnel vào Câu hỏi thường gặp (với thời gian) . Gợi ý tuyệt vời!
Matt Dowle

1
@mnel Cảm ơn vì giải pháp ... đã làm cho ngày của tôi ... :)
Ankit

@mnel Có cách nào chúng ta có thể áp dụng NA bằng 0 khi biểu diễn X[Y[J(unique_keys)]]không?
Ankit

11
điều gây ấn tượng với tôi về dữ liệu. tài liệu bảng là nó có thể rất dài, nhưng vẫn rất khó hiểu ...
NiuBiBang

24

Câu trả lời của @ mnel là đúng, vì vậy hãy chấp nhận câu trả lời đó. Đây chỉ là theo dõi, quá dài để nhận xét.

Như mnel đã nói, liên kết ngoài trái / phải có được bằng cách hoán đổi YX: Y[X]-vs- X[Y]. Vì vậy, 3 trong số 4 kiểu nối được hỗ trợ trong cú pháp đó, không phải 2, iiuc.

Thêm thứ 4 có vẻ là một ý kiến ​​hay. Giả sử chúng tôi thêm full=TRUEhoặc both=TRUEhoặc merge=TRUE(không chắc tên đối số tốt nhất?) Thì điều đó đã không xảy ra với tôi trước đó X[Y,j,merge=TRUE]sẽ hữu ích vì những lý do sau câu NHƯNG trong Câu hỏi thường gặp 1.12. Yêu cầu tính năng mới hiện đã được thêm và liên kết trở lại đây, cảm ơn:

FR # 2301: Thêm đối số merge = TRUE cho cả X [Y] và Y [X] tham gia giống như merge ().

Các phiên bản gần đây đã tăng tốc merge.data.table(ví dụ: bằng cách lấy một bản sao cạn bên trong để đặt các phím hiệu quả hơn). Vì vậy, chúng tôi đang cố gắng mang lại merge()X[Y]gần hơn, đồng thời cung cấp tất cả các tùy chọn cho người dùng để có được sự linh hoạt hoàn toàn. Có những ưu và khuyết điểm của cả hai. Một yêu cầu tính năng nổi bật khác là:

FR # 2033: Thêm by.x và by.y vào merge.data.table

Nếu có bất kỳ người khác, xin vui lòng tiếp tục họ đến.

Bởi phần này trong câu hỏi:

tại sao không sử dụng cú pháp hợp nhất cho các phép nối thay vì tham số nomatch của hàm đối sánh?

Nếu bạn thích merge()cú pháp và 3 đối số của nó all, all.xall.ysau đó chỉ cần sử dụng thay vì X[Y]. Hãy nghĩ rằng nó sẽ bao gồm tất cả các trường hợp. Hay bạn có nghĩa là lý do tại sao là đối số một đĩa đơn nomatchtại [.data.table? Nếu vậy, đó chỉ là cách có vẻ tự nhiên với Câu hỏi thường gặp 2.14: "Bạn có thể giải thích thêm tại sao data.table được lấy cảm hứng từ cú pháp A [B] trong cơ sở không?". Nhưng cũng nomatchchỉ nhận hai giá trị hiện tại 0NA. Điều đó có thể được mở rộng để giá trị âm có nghĩa là một cái gì đó hoặc 12 có nghĩa là sử dụng các giá trị của hàng thứ 12 để điền vào NA, hoặc nomatchtrong tương lai có thể là một vectơ hoặc thậm chí chính nó a data.table.

Hừm. Làm thế nào mà by-without-by sẽ tương tác với merge = TRUE? Có lẽ chúng ta nên chuyển việc này cho datatable-help .


Cảm ơn @Matthew. Câu trả lời của @ mnel rất tuyệt vời, nhưng câu hỏi của tôi không phải là làm thế nào để thực hiện tham gia đầy đủ hoặc kết hợp trái, mà là "Có lý do gì mà chỉ có hai loại kết hợp được hỗ trợ?" Vì vậy, bây giờ nó triết học hơn một chút ;-) Thực ra tôi không thích cú pháp hợp nhất, nhưng dường như có một truyền thống R để xây dựng trên những thứ hiện có mà mọi người đã quen thuộc. Tôi đã viết nguệch ngoạc join="all", join="all.x", join="all.y" and join="x.and.y"trong lề các ghi chú của mình. Không chắc liệu điều đó có tốt hơn không.
Douglas Clark

@DouglasClark Có lẽ joinnhư vậy, ý kiến ​​hay. Tôi đã đăng lên datatable-help để chúng ta cùng xem. Cũng có thể cho data.tablemột thời gian để ổn định. Ví dụ, bạn đã phải từng-mà-khôngtham gia phạm vi kế thừa ?
Matt Dowle

Như đã nêu trong bình luận của tôi ở trên, tôi đề nghị thêm một jointừ khóa để, khi tôi là một DataTable: X[Y,j,join=string]. Các giá trị chuỗi có thể cho tham gia được đề nghị là: 1) "all.y" và "đúng" -
Douglas Clark

1
Xin chào Matt, thư viện data.table thật tuyệt vời; cảm ơn vì điều đó; mặc dù tôi nghĩ rằng hành vi tham gia (là một liên kết bên ngoài bên phải theo mặc định) nên được giải thích nổi bật trong tài liệu chính; tôi đã mất 3 ngày để tìm ra điều này.
Timothée HENRY

1
@tucson Chỉ cần liên kết ở đây, hiện đã được nộp dưới dạng số 709 .
Matt Dowle

17

"Câu trả lời" này là một đề xuất để thảo luận: Như đã nêu trong nhận xét của tôi, tôi khuyên bạn nên thêm một join tham số để [.data.table () để cho phép loại bổ sung lượng tham gia, ví dụ: X[Y,j,join=string]. Ngoài 4 loại phép tham gia thông thường, tôi cũng đề nghị hỗ trợ 3 loại phép nối độc quyền và phép ghép chéo .

Các joingiá trị chuỗi (và bí danh) cho các kiểu nối khác nhau được đề xuất là:

  1. "all.y""right"- tham gia phải, mặc định data.table hiện tại (nomatch = NA) - tất cả các hàng Y có NA không có X khớp;
  2. "both""inner" - liên kết bên trong (nomatch = 0) - chỉ các hàng mà X và Y khớp nhau;

  3. "all.x""left" - nối trái - tất cả các hàng từ X, NA không có Y nào khớp:

  4. "outer""full" - tham gia đầy đủ bên ngoài - tất cả các hàng từ X và Y, NA không khớp

  5. "only.x""not.y" - không tham gia hoặc chống tham gia trả về hàng X trong đó không có Y phù hợp

  6. "only.y""not.x" - không tham gia hoặc chống tham gia trả về các hàng Y mà không có kết quả X nào
  7. "not.both" - phép nối độc quyền trả về các hàng X và Y mà không có kết quả phù hợp với bảng khác, tức là một-hoặc (XOR) độc quyền
  8. "cross"- kết hợp chéo hoặc tích Descartes với mỗi hàng X khớp với mỗi hàng Y

Giá trị mặc định là join="all.y" tương ứng với mặc định hiện tại.

Các giá trị chuỗi "all", "all.x" và "all.y" tương ứng với merge() các tham số. Các chuỗi "phải", "trái", "bên trong" và "bên ngoài" có thể phù hợp hơn với người dùng SQL.

Chuỗi "cả hai" và "not.both" là gợi ý tốt nhất của tôi vào lúc này - nhưng ai đó có thể có đề xuất chuỗi tốt hơn cho phép nối bên trong và phép nối độc quyền. (Tôi không chắc liệu "độc quyền" có phải là thuật ngữ phù hợp hay không, hãy sửa cho tôi nếu có thuật ngữ thích hợp cho tham gia "XOR".)

Sử dụng join="not.y"là một thay thế cho X[-Y,j]hoặcX[!Y,j] không nối và có thể rõ ràng hơn (đối với tôi), mặc dù tôi không chắc liệu chúng có giống nhau hay không (tính năng mới trong data.table phiên bản 1.8.3).

Phép nối chéo đôi khi có thể hữu ích, nhưng nó có thể không phù hợp với mô hình data.table.


1
Vui lòng gửi cái này đến datatable-help để thảo luận.
Matt Dowle

3
+1 Nhưng, vui lòng gửi đến datatable-help hoặc gửi yêu cầu tính năng . Tôi không ngại thêm joinnhưng trừ khi nó vào trình theo dõi, nó sẽ bị lãng quên.
Matt Dowle

1
Tôi thấy bạn đã không đăng nhập vào SO trong một thời gian. Vì vậy, tôi đã đệ trình này trong FR # 2301
Matt Dowle

@MattDowle, +1 cho tính năng này. (Đã cố gắng làm điều đó qua FR # 2301 nhưng tôi nhận được thông báo bị từ chối cấp quyền).
adilapapaya

@adilapapaya Chúng tôi đã chuyển từ RForge sang GitHub. Vui lòng +1 tại đây: github.com/Rdatatable/data.table/issues/614 . Arun chuyển các vấn đề để chúng không bị mất.
Matt Dowle
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.