2016-07-10 19 views
9

Tôi đã nghĩ ra một giải pháp để tra cứu các giá trị từ nhiều cột của hai bảng dữ liệu riêng biệt và thêm một cột mới dựa trên tính toán giá trị của chúng (nhiều so sánh có điều kiện). Mã dưới đây. Nó liên quan đến việc sử dụng data.table và join trong khi tính toán các giá trị từ cả hai bảng, tuy nhiên, các bảng không được nối với các cột mà tôi so sánh, và do đó tôi nghi ngờ có thể không nhận được các ưu điểm tốc độ vốn có. Tôi đã đọc rất nhiều về và vui mừng về việc khai thác. Nói cách khác, tôi đang tham gia vào một cột 'giả', vì vậy tôi không nghĩ rằng tôi đang tham gia "đúng".R data.table Multiple Conditions Tham gia

Bài tập được cung cấp cho lưới X theo X dtGrid và danh sách X^2 sự kiện ngẫu nhiên dtEvents trong lưới, để xác định số lượng Sự kiện xảy ra trong phạm vi bán kính đơn vị 1 của mỗi điểm lưới. Mã dưới đây. Tôi đã chọn kích thước lưới 100 X 100, mất ~ 1,5 giây để chạy kết nối trên máy của tôi. Nhưng tôi không thể đi lớn hơn nhiều mà không giới thiệu một hit hiệu suất rất lớn (200 X 200 mất ~ 22 giây).

Tôi thực sự thích sự linh hoạt khi có thể thêm nhiều điều kiện vào câu lệnh val của mình (ví dụ: nếu tôi muốn thêm một loạt các kết hợp AND và OR tôi có thể làm điều đó), vì vậy tôi muốn giữ lại chức năng đó .

Có cách nào để sử dụng dữ liệu. Có thể tham gia ‘đúng’ (hoặc bất kỳ giải pháp data.table nào khác) để đạt được kết quả nhanh hơn/hiệu quả hơn không?

Cảm ơn bạn rất nhiều!

#Initialization stuff 
library(data.table) 
set.seed(77L) 

#Set grid size constant 
#Increasing this number to a value much larger than 100 will result in significantly longer run times 
cstGridSize = 100L 

#Create Grid 
vecXYSquare <- seq(0, cstGridSize, 1) 
dtGrid <- data.table(expand.grid(vecXYSquare, vecXYSquare)) 
setnames(dtGrid, 'Var1', 'x') 
setnames(dtGrid, 'Var2', 'y') 
dtGrid[, DummyJoin:='A'] 
setkey(dtGrid, DummyJoin) 

#Create Events 
xrand <- runif(cstGridSize^2, 0, cstGridSize + 1) 
yrand <- runif(cstGridSize^2, 0, cstGridSize + 1) 
dtEvents <- data.table(x=xrand, y=yrand) 
dtEvents[, DummyJoin:='A'] 
dtEvents[, Counter:=1L] 
setkey(dtEvents, DummyJoin) 

#Return # of events within 1 unit radius of each grid point 
system.time(
    dtEventsWithinRadius <- dtEvents[dtGrid, { 
     val = Counter[(x - i.x)^2 + (y - i.y)^2 < 1^2]; #basic circle fomula: x^2 + y^2 = radius^2 
     list(col_i.x=i.x, col_i.y=i.y, EventsWithinRadius=sum(val)) 
    }, by=.EACHI] 
) 
+0

Frank: Guilty as charg. Bạn hoàn toàn đúng. Nó nên nói một X + 1 bởi X + 1 lưới ... Tôi muốn tất cả các điểm sự kiện để phù hợp trong lưới điện, vì vậy tôi đã bao gồm các điểm lưới 0 X và Y. Điều đó nói rằng, vấn đề tôi đang cố gắng giải quyết bị ảnh hưởng tối thiểu bởi sự thay đổi này ... số sự kiện và kích thước lưới là một phần tùy ý, khác hơn là chúng đều lớn. Cảm ơn vì sự đúng đắn của bạn. – ColoradoGranite

+0

Đối với giá trị của nó, nếu bạn có thể chuyển tiêu chí từ vòng tròn đơn vị thành +/- 1 trên mỗi thứ nguyên một cách độc lập, nó có thể nhanh hơn rất nhiều: 'system.time (dtEvents [, { L = lapply (. SD, hàm (x) đại diện (as.integer (tầng (x)), mỗi = 4L)); . ( x = L [[1]] + 0: 1, y = L [[2]] + đại diện (0: 1, mỗi = 2L) ) }, .SDcols = x: y] [, .N, by = x: y]) '. Đó là những gì tôi nghĩ bạn đã làm ban đầu. – Frank

Trả lời

12

Câu hỏi rất thú vị .. và sử dụng tuyệt vời by = .EACHI! Dưới đây là một cách tiếp cận khác bằng cách sử dụng NEWnon-equi joins from the current development version, v1.9.7.

Vấn đề: Việc bạn sử dụng by=.EACHI là hoàn toàn hợp lý vì thay thế khác là để thực hiện một chéo tham gia (mỗi hàng của dtGrid nối với tất cả các hàng của dtEvents) nhưng đó là quá đầy đủ và chắc chắn sẽ bùng nổ rất nhanh chóng .

Tuy nhiên by = .EACHI được thực hiện cùng với một equi-join sử dụng một giả cột, mà kết quả trong tính toán tất cả các khoảng cách (ngoại trừ việc nó cùng một lúc, do đó bộ nhớ hiệu quả). Tức là, trong mã của bạn, cho mỗi dtGrid, tất cả các khoảng cách có thể vẫn được tính với dtEvents; do đó nó không quy mô cũng như mong đợi.

Chiến lược: Sau đó, bạn sẽ đồng ý rằng một sự cải tiến có thể chấp nhận là hạn chế số lượng hàng đó sẽ là kết quả của việc gia nhập mỗi hàng của dtGrid-dtEvents.

Hãy (x_i, y_i) đến từ dtGrid(a_j, b_j) này đến từ các dtEvents, nói, nơi 1 <= i <= nrow(dtGrid)1 <= j <= nrow(dtEvents). Sau đó, i = 1 ngụ ý, tất cả j thỏa mãn (x1 - a_j)^2 + (y1 - b_j)^2 < 1 cần được trích xuất.Đó chỉ có thể xảy ra khi:

(x1 - a_j)^2 < 1 AND (y1 - b_j)^2 < 1 

Điều này giúp làm giảm không gian tìm kiếm mạnh mẽ bởi vì, thay vì nhìn vào tất cả hàng trong dtEvents cho mỗi hàng trong dtGrid, chúng tôi chỉ phải trích xuất những hàng ở đâu,

a_j - 1 <= x1 <= a_j + 1 AND b_j - 1 <= y1 <= b_j + 1 
# where '1' is the radius 

Ràng buộc này có thể được dịch trực tiếp sang một số không phải là equi tham gia và kết hợp với by = .EACHI như trước đây. Các bước bổ sung chỉ được yêu cầu là xây dựng các cột a_j-1, a_j+1, b_j-1, b_j+1 như sau:

foo1 <- function(dt1, dt2) { 
    dt2[, `:=`(xm=x-1, xp=x+1, ym=y-1, yp=y+1)]     ## (1) 
    tmp = dt2[dt1, on=.(xm<=x, xp>=x, ym<=y, yp>=y), 
       .(sum((i.x-x)^2+(i.y-y)^2<1)), by=.EACHI, 
       allow=TRUE, nomatch=0L 
      ][, c("xp", "yp") := NULL]        ## (2) 
    tmp[] 
} 

## (1) cấu trúc tất cả các cột cần thiết cho phi đẳng tham gia (kể từ biểu thức không được phép trong công thức cho on= chưa

## (2) thực hiện. một tham gia không equi tính toán khoảng cách và kiểm tra cho tất cả các khoảng cách là < 1 trên tập hợp kết hợp bị hạn chế cho mỗi hàng trong dtGrid - do đó phải là nhiều nhanh hơn

Benchmarks:

# Here's your code (modified to ensure identical column names etc..): 
foo2 <- function(dt1, dt2) { 
    ans = dt2[dt1, 
       { 
       val = Counter[(x - i.x)^2 + (y - i.y)^2 < 1^2]; 
       .(xm=i.x, ym=i.y, V1=sum(val)) 
       }, 
      by=.EACHI][, "DummyJoin" := NULL] 
    ans[] 
} 

# on grid size of 100: 
system.time(ans1 <- foo1(dtGrid, dtEvents)) # 0.166s 
system.time(ans2 <- foo2(dtGrid, dtEvents)) # 1.626s 

# on grid size of 200: 
system.time(ans1 <- foo1(dtGrid, dtEvents)) # 0.983s 
system.time(ans2 <- foo2(dtGrid, dtEvents)) # 31.038s 

# on grid size of 300: 
system.time(ans1 <- foo1(dtGrid, dtEvents)) # 2.847s 
system.time(ans2 <- foo2(dtGrid, dtEvents)) # 151.32s 

identical(ans1[V1 != 0]L, ans2[V1 != 0L]) # TRUE for all of them 

Các speedups là ~ 10x, 32x và 53x tương ứng.

Lưu ý rằng các hàng trong dtGrid mà điều kiện không hài lòng ngay cả đối với một hàng trong dtEvents sẽ không có trong kết quả (do nomatch=0L). Nếu bạn muốn các hàng đó, bạn cũng sẽ phải thêm một trong các số xm/xp/ym/yp cols .. và kiểm tra chúng cho NA (= không có kết quả phù hợp).

Đây là lý do chúng tôi phải xóa tất cả 0 đếm để nhận được giống hệt nhau = TRUE.

HTH

PS: Xem lịch sử cho một biến thể khác mà toàn bộ tham gia được thực hiện và sau đó tính toán và tính được tạo.

+1

Cảm ơn Arun! Giải pháp rất thông minh! Tôi đánh giá cao rằng bạn thực sự cung cấp cả hai giải pháp, bởi vì tôi đã học được các kỹ thuật từ cả hai. Tôi cũng muốn khám phá thêm nhiều điều kiện (ví dụ: thêm một cột khác và chỉ tổng hợp khi nó bằng một giá trị nhất định), vì vậy tôi sẽ phát xung quanh với các tùy chọn trong các giải pháp này. – ColoradoGranite

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