2013-09-23 29 views
26

Làm cách nào để thực hiện một semi-join với dữ liệu.table? Một phép nối bán giống như một kết nối bên trong ngoại trừ nó chỉ trả về các cột của X (không phải của các cột Y), và không lặp lại các hàng của X để phù hợp với các hàng của Y. Ví dụ, đoạn mã sau thực hiện một bên trong tham gia:Thực hiện việc bán tham gia với dữ liệu.table

x <- data.table(x = 1:2, y = c("a", "b")) 
setkey(x, x) 
y <- data.table(x = c(1, 1), z = 10:11) 

x[y] 
# x y z 
# 1: 1 a 10 
# 2: 1 a 11 

một nửa tham gia sẽ trở lại chỉ x[1]

Trả lời

12

Nhiều khả năng:

Nếu có giá trị khóa trùng lặp trong x, sau đó cần:

w = unique(x[y,which=TRUE,allow.cartesian=TRUE]) 
x[w] 

Hoặc cách khác xung quanh:

setkey(y,x) 
w = !is.na(y[x,which=TRUE,mult="first"]) 
x[w] 

Nếu nrow (x) < < nrow (y) thì phương pháp y [x] sẽ nhanh hơn.
Nếu nrow (x) >> nrow (y) thì phương pháp x [y] sẽ nhanh hơn.

Nhưng chống chống tham gia kháng cáo quá :-)

+1

Tuyệt! Bây giờ tôi hiểu những gì 'allow.cartesian = TRUE' là cho. –

+0

Tôi biết rằng guru sẽ dừng lại! Kể từ khi bạn chính thức thích cách tiếp cận chống chống tham gia, làm thế nào về việc thêm 'x [!! y]' vào cú pháp? :) –

+0

@VictorK. :) 'x [!! y]' là một lỗi hiện tại không phải là nó, vì vậy ok. Vui lòng gửi dưới dạng yêu cầu tính năng. –

10

một giải pháp tôi có thể nghĩ đến là:

tmp <- x[!y] 
x[!tmp] 

trong data.table, bạn có thể có một bảng dữ liệu như là một biểu i (tức là, biểu thức đầu tiên trong data.table.[ cuộc gọi), và điều đó sẽ thực hiện một tham gia, ví dụ .:

x <- data.table(x = 1:10, y = letters[1:10]) 
setkey(x, x) 
y <- data.table(x = c(1,3,5,1), z = 1:4) 

> x[y] 
    x y z 
1: 1 a 1 
2: 3 c 2 
3: 5 e 3 
4: 1 a 4 

Các ! trước khi biểu i là một phần mở rộng của cú pháp trên mà thực hiện một 'không-tham gia', như mô tả trên p. 11 của dữ liệu.table documentation. Vì vậy, nhiệm vụ đầu tiên để đánh giá một tập hợp con của x mà không có bất kỳ hàng nơi chìa khóa (cột x) hiện diện trong y:

> x[!y] 
    x y 
1: 2 b 
2: 4 d 
3: 6 f 
4: 7 g 
5: 8 h 
6: 9 i 
7: 10 j 

Nó tương tự như setdiff trong vấn đề này. Và do đó, câu lệnh thứ hai trả về tất cả các hàng trong x trong đó khóa có trong y.

Tính năng ! đã được bổ sung trong data.table 1.8.4 với các lưu ý sau đây trong NEWS:

o A new "!" prefix on i signals 'not-join' (a.k.a. 'not-where'), #1384i. 
     DT[-DT["a", which=TRUE, nomatch=0]] # old not-join idiom, still works 
     DT[!"a"]        # same result, now preferred. 
     DT[!J(6),...]       # !J == not-join 
     DT[!2:3,...]       # ! on all types of i 
     DT[colA!=6L | colB!=23L,...]   # multiple vector scanning approach (slow) 
     DT[!J(6L,23L)]      # same result, faster binary search 
    '!' has been used rather than '-' : 
     * to match the 'not-join'/'not-where' nomenclature 
     * with '-', DT[-0] would return DT rather than DT[0] and not be backwards 
      compatible. With '!', DT[!0] returns DT both before (since !0 is TRUE in 
      base R) and after this new feature. 
     * to leave DT[+J...] and DT[-J...] available for future use 

Đối với một số lý do, sau đây không hoạt động x[!(x[!y])] - có lẽ data.table là quá thông minh về phân tích các tham số .

P.S. Như Josh O'Brien đã chỉ ra một câu trả lời khác, một dòng sẽ là x[!eval(x[!y])].

+0

Bạn có quan tâm giải thích làm thế nào mà làm việc? Phủ định của bảng dữ liệu là gì? – hadley

+2

Trên p.11 [ở đây] (http://cran.r-project.org/web/packages/data.table/data.table.pdf), nó nói 'Tất cả các loại' tôi 'có thể được đặt trước với! . Điều này báo hiệu việc không tham gia hoặc không chọn nên được thực hiện.' –

+0

Vì vậy, bạn cần biết rằng cộng với hai ghi chú nâng cao tiếp theo. Tôi nghĩ rằng một lời giải thích đầy đủ sẽ là một bổ sung hữu ích cho câu trả lời của bạn. – hadley

9

Tôi đang bối rối với tất cả các không-tham gia ở trên, không phải là những gì bạn muốn chỉ đơn giản là:

unique(x[y, names(x), with = F]) 
# x y 
#1: 1 a 

Nếu x có thể có chìa khóa trùng lặp, sau đó bạn có thể độc đáo y thay vì:

## Creating an example data.table 'a' three-times-repeated first row 
x <- data.table(x = c(1,1,1,2), y = c("a", "a", "a", "b")) 
setkey(x, x) 
y <- data.table(x = c(1, 1), z = 10:11) 
setkey(y, x) 

x[eval(unique(y, by = key(y))), names(x), with = F] # data.table >= 1.9.8 requires by=key(y) 
# x y 
# 1: 1 a 
# 2: 1 a 
# 3: 1 a 
+1

Điều này sẽ không hoạt động chính xác nếu x có hai hàng giống nhau. –

+0

Bạn đã đánh bại tôi sau 3 phút - Tôi vừa thêm giải pháp với so sánh thời gian ở trên. Có, nó chạy nhanh hơn nhiều, nhưng điểm của Josh là hợp lệ. Tôi cũng sẽ thay thế 'tên (x)' bằng 'khóa (x)' để khớp với trường hợp sử dụng dự định ban đầu của @ hadley. –

+0

@ JoshO'Brien đã thêm phiên bản đề cập đến các khóa giống nhau trong 'x' – eddi

2

Cập nhật.Dựa trên tất cả các cuộc thảo luận ở đây, tôi sẽ làm một cái gì đó như thế này, mà nên được nhanh chóng và làm việc trong trường hợp tổng quát nhất:

x[eval(unique(y[, key(x), with = FALSE]))] 

Dưới đây là một giải pháp trực tiếp hơn:

unique(x[eval(y$x)]) 

Trực tiếp và chạy nhanh hơn - đây là so sánh trong thời gian chạy với giải pháp trước của tôi:

# Generate some large data 
N <- 1000000 * 26 
x <- data.table(x = 1:N, y = letters, z = rnorm(N)) 
setkey(x, x) 
y <- data.table(x = sample(N, N/10, replace = TRUE), z = sample(letters, N/10, replace = TRUE)) 
setkey(y, x) 

system.time(r1 <- x[!eval(x[!y])]) 
    user system elapsed 
    7.772 1.217 11.998 

system.time(r2 <- unique(x[eval(y$x)])) 
    user system elapsed 
    0.540 0.142 0.723 

Trong một trường hợp chung, bạn có thể làm điều gì đó như

x[eval(y[, key(x), with = FALSE])] 
+0

Có, tôi đã thêm nó ở trên –

+0

Nếu bạn độc đáo trước khi tham gia, nó nhanh hơn (được đánh giá tương tự như câu trả lời của tôi). – Frank

2

Tôi đã cố gắng viết một phương pháp không sử dụng bất kỳ tên nào, điều này hoàn toàn gây nhầm lẫn trong ví dụ của OP.

sJ <- function(x,y){ 
    ycols <- 1:min(ncol(y),length(key(x))) 
    yjoin <- unique(y[,ycols,with=FALSE,drop=FALSE]) 
    yjoin 
} 

x[eval(sJ(x,y))] 

Ví dụ đơn giản của Victor, điều này mang lại kết quả mong muốn:

x y 
1: 1 a 
2: 3 c 
3: 5 e 

Đây là chậm hơn so với cách Victor của một ~ 30%.

EDIT: Và cách tiếp cận của Victor, lấy độc đáo trước khi gia nhập, là nhanh hơn khá nhiều:

N <- 1e5*26 
x <- data.table(x = 1:N, y = letters, z = rnorm(N)) 
setkey(x, x) 
y <- data.table(x = sample(N, N/10, replace = TRUE), z = sample(letters, N/10, replace = TRUE)) 
setkey(y, x) 
require(microbenchmark) 
microbenchmark(
    sJ=x[eval(sJ(x,y))], 
    dolla=unique(x[eval(y$x)]), 
    brack=x[eval(unique(y[['x']]))] 
) 
Unit: milliseconds 
    expr  min  lq median  uq  max neval 
# sJ 120.22700 125.04900 126.50704 132.35326 217.6566 100 
# dolla 105.05373 108.33804 109.16249 118.17613 285.9814 100 
# brack 53.95656 61.32669 61.88227 65.21571 235.8048 100 

Tôi đoán các [[ vs $ không giúp tốc độ, nhưng không kiểm tra .

0

Gói dplyr hỗ trợ bốn sau loại tham gia:

inner_join, left_join, semi_join, anti_join

Vì vậy, đối với bán tham gia thử mã sau đây

library("dplyr") 

table1 <- data.table(x = 1:2, y = c("a", "b")) 
table2 <- data.table(x = c(1, 1), z = 10:11) 

semi_join(table1, table2) 

Kết quả như mong đợi:

# Joining by: "x" 
# Source: local data table [1 x 2] 
# 
#  x  y 
# (int) (chr) 
# 1  1  a 
+21

Tôi có cảm giác Hadley đã biết điều này :-) –

+2

Có, tôi đã hỏi câu hỏi để tôi có thể tìm ra cách làm cho công việc 'semi_join()' cho các bảng dữ liệu;) – hadley

1

Chủ đề này quá cũ.Nhưng tôi nhận thấy rằng các giải pháp có thể dễ dàng bắt nguồn từ định nghĩa về bán join được đưa ra trong các bài bản gốc:

"Một nửa tham gia cũng giống như một bên tham gia ngoại trừ việc nó chỉ trả về cột của X (không còn những người Y), và không lặp lại các hàng của X để phù hợp với các hàng của Y"

library(data.table) 
dt1 <- data.table(ProdId = 1:4, 
        Product = c("Bread", "Cheese", "Pizza", "Butter")) 
dt2 <- data.table(ProdId = c(1, 1, 3, 4, 5), 
        Company = c("A", "B", "C", "D", "E")) 

# semi-join 
unique(merge(dt1, dt2, on="ProdId")[, names(dt1), with=F]) 
    ProdId Product 
1:  1 Bread 
2:  3 Pizza 
3:  4 Butter 

tôi chỉ đơn giản là áp dụng cú pháp của nội tham gia, tiếp theo bằng cách lọc các cột từ đầu tiên chỉ bảng, với unique() để xóa các hàng của bảng đầu tiên được lặp lại để khớp với các hàng của giây ond bảng.

Chỉnh sửa: Cách tiếp cận trên sẽ chỉ khớp với đầu ra dplyr::semi_join() chỉ khi chúng tôi có các hàng duy nhất trong bảng đầu tiên. Nếu chúng ta cần xuất tất cả các hàng bao gồm các bản sao từ bảng đầu tiên, thì chúng ta có thể sử dụng phương thức fsetdiff() được hiển thị bên dưới.

Một một dòng data.table giải pháp:

fsetdiff(dt1, dt1[!dt2, on="ProdId"]) 
    ProdId Product 
1:  1 Bread 
2:  3 Pizza 
3:  4 Butter 

Tôi vừa mới lấy ra từ bảng đầu tiên chống tham gia của đầu tiên và thứ hai. Có vẻ đơn giản hơn với tôi. Nếu bảng đầu tiên có hàng trùng lặp, chúng tôi sẽ cần:

fsetdiff(dt1, dt1[!dt2, on="ProdId"], all=T) 

Các fsetdiff() kết quả với ,all=T phù hợp với sản lượng từ dplyr:

dplyr::semi_join(dt1, dt2, by="ProdId") 
    ProdId Product 
1  1 Bread 
2  3 Pizza 
3  4 Butter 

Sử dụng một tập hợp các dữ liệu được lấy từ một trong những bài viết theo thời gian:

x <- data.table(x = c(1,1,1,2), y = c("a", "a", "a", "b")) 
y <- data.table(x = c(1, 1), z = 10:11) 

Với dplyr:

dplyr::semi_join(x, y, by="x") 
    x y 
1 1 a 
2 1 a 
3 1 a 

Với data.table:

fsetdiff(x, x[!y, on="x"], all=T) 
    x y 
1: 1 a 
2: 1 a 
3: 1 a 

Without ,all=T, các hàng trùng lặp được loại bỏ:

fsetdiff(x, x[!y, on="x"]) 
    x y 
1: 1 a 
+0

Cách tiếp cận đầu tiên của bạn gặp phải các vấn đề tương tự như lần đầu tiên của eddi trả lời khi 'x' có các hàng giống nhau (chính). – BenBarnes

+0

Tôi đã nhận thấy rằng nó có hiệu quả cùng một biểu thức. Bạn có thể chỉ ra các trường hợp mà phương thức 'fsetdiff()' sẽ thất bại? – San

+0

Có vẻ như cách tiếp cận 'fsetdiff' cũng loại bỏ các hàng trùng lặp trong x giống như cách tiếp cận đầu tiên. Hãy thử sử dụng dữ liệu trong phương pháp thứ hai của eddi. Kết quả phải là 3 hàng, nhưng 'fsetdiff' chỉ trả về 1. – BenBarnes

Các vấn đề liên quan