2016-07-10 58 views
9

İki ayrı veri tablosunun birden çok sütunundan arama değerlerine bir çözüm geliştirdim ve değerlerinin yeni bir sütun tabanlı hesaplamaları ekleyin (çoklu koşullu karşılaştırmalar). Aşağıdaki kod. Her iki tablodan değerleri hesaplarken bir data.table kullanmayı ve birleştirme işlemini içerir, ancak tablolar karşılaştırdığım sütunlarda birleştirilmez ve bu nedenle data.tables'in doğasındaki hız avantajlarını elde edemeyeceğimi sanıyorum. Hakkında çok şey okudum ve dokunarak heyecanlıyım. Başka bir şekilde, 'kukla' sütununa katılıyorum, bu yüzden "düzgün bir şekilde" katıldığımı sanmıyorum.R data.table Çoklu Koşullar Katıl

Alıştırma X X ızgarası dtGrid ve X^listesiyle verilir Her grid noktasının 1 birim yarıçapında kaç tane Olay olduğunu belirlemek için, ızgara içinde 2 rastgele Olaylar dtEvents. Kod aşağıda. Makinemde birleştirme işlemini gerçekleştirmek için ~ 1.5 saniye süren 100 X 100 boyutunda bir ızgara seçtim. Fakat çok büyük bir performans isabeti yapmadan çok daha fazla gidemem (200 X 200 ~ 22 saniye sürer).

Gerçekten (ı AND bir demet eklemek istedim ve OR kombinasyonları Bunu yapabilirim eğer, örneğin) benim val açıklamaya birden çok koşul ekleyememe esnekliği gibi, bu yüzden bu işlevselliği sürdürmek istiyorsanız .

Çok daha hızlı/verimli bir sonuç elde etmek için data.table birleşimlerini "düzgün" (veya başka bir data.table çözümü) kullanmanın bir yolu var mı?

Çok teşekkürler!

#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: Suçlu olarak suçlanıyor. Kesinlikle haklısın. X + 1 ile X + 1 ızgarası demeliydi ... Tüm Etkinlik noktalarının ızgaraya sığmasını istedim, bu yüzden 0 X ve Y ızgara noktalarını dahil etmeliydim. Bununla birlikte, çözmeye çalıştığım problem bu değişiklikten en az etkilenen ... olayların ve ızgara büyüklüğünün her ikisi de, ikisi de oldukça büyüktür. Düzeltme için teşekkürler. – ColoradoGranite

+0

Değeri ne olursa olsun, bir ölçekten bağımsız olarak her bir boyutta +/- 1'e geçiş yapabiliyorsanız çok daha hızlı olabilir: 'system.time (dtEvents [, { L = lapply (. SD, fonksiyon (x) rep (as.integer (zemin (x)), her biri = 4L)) ( x = L [[1]] + 0: 1, y = L [[2]] + rep (0: 1, her = 2L) ) }, .SDcols = x: y] [, .N, by = x: y]) '. Başlangıçta yaptığınızı sandım. – Frank

cevap

12

Çok ilginç bir soru .. ve by = .EACHI'un harika kullanımı! İşte, NEWnon-equi joins from the current development version, v1.9.7'u kullanan başka bir yaklaşım.

Sayı:by=.EACHI kullanımınız diğer alternatif çapraz katılmak gerçekleştirmek için çünkü tamamen haklı (dtGrid her satırı dtEvents tüm satırlara katıldı) ama bu çok kapsamlı var ve çok hızlı bir şekilde patlayabilir bağlıdır . Ancak by = .EACHI

eş-katılabilir, bir bütün mesafeleri hesaplamak ile sonuçlanan bir kukla kolon kullanılarak birlikte gerçekleştirilir (haricinde, bir seferde bir tane yapar, bu nedenle bellek etkin). Yani, kodunuzda, her bir dtGrid için tüm olası mesafeler hala dtEvents; bu nedenle beklendiği gibi ölçeklendirilmez.

Strateji: Sonra dtEvents için dtGrid her satır katılmasını neden olacaktır satır sayısını sınırlamak için kabul edilebilir bir iyileşme olduğunu kabul ediyorum.

(x_i, y_i)dtGrid den gelsin ve (a_j, b_j)dtEvents, diyelim ki, nerede 1 <= i <= nrow(dtGrid) ve 1 <= j <= nrow(dtEvents) dan gelir. Daha sonra, i = 1, j'un, (x1 - a_j)^2 + (y1 - b_j)^2 < 1 gereksinimlerini karşıladığını ima eder.Bunu sadece olabilir: Bunun nedeni, yerine dtGrid her satır için dtEvents yılında tüm satırları bakmanın ölçüde arama alanını azaltmaya yardımcı olur

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

, sadece, nerede bu satırları ayıklamak zorunda

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

Bu kısıtlama, eşdeğeri olmayan birleştirmeye doğrudan dönüştürülebilir ve daha önce olduğu gibi by = .EACHI ile birleştirilebilir. . Dışı equi ifadeleri, henüz on= formül izin verilmez çünkü (katılır

## (2) gerçekleştirir için gerekli tüm sütun

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) yapıları: Gerekli sadece ek aşama, aşağıdaki gibi a_j-1, a_j+1, b_j-1, b_j+1 sütunları oluşturmak için olmayan bir equi o dtGrid her satır için kombinasyonların kısıtlı sette < 1 tüm mesafelerde mesafeleri ve kontroller hesaplar katılmak - dolayısıyla çok daha hızlı olmalı

.

Deneyler:

# 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 

speedups sırasıyla ~ 10x, 32x ve 53x bulunmaktadır. dtGrid satırlar olan durum bile dtEvents tek satır için yerine

Not sonucu (nedeniyle nomatch=0L) içinde mevcut olmayacaktır. Bu satırları istiyorsanız, xm/xp/ym/yp kodlarından birini de eklemeniz gerekir. Bunları NA (= eşleşme yok) olarak işaretleyin.

Bu nedenle, tüm 0 sayımlarını kaldırmak zorunda kaldık çünkü 0 = TRUE.

HTH

PS: Tüm hayata olan ve daha sonra uzaktan hesaplanır ve sayımlar oluşturulan katılmak başka varyasyonu için geçmişine bakın.

+1

Teşekkürler Arun! Çok akıllı çözümler! Her iki çözümü de sağladığınızı takdir ediyorum çünkü her ikisinden de teknikler öğrendim. Ayrıca, birden fazla koşul eklemeyi (örneğin, başka bir sütun ekleyerek ve yalnızca belirli bir değere eşit olduğunda özetleyerek) araştırmak istiyorum, bu yüzden bu çözümlerde seçeneklerle oynayacağım. – ColoradoGranite