2013-12-16 13 views
10

पर आधारित बस सोच रहा है कि डेटाटेबल को सब्सक्राइब करने का कोई आसान तरीका है या नहीं। असल में मेरे पास लाखों पंक्तियों और सैकड़ों रंगों के साथ एक बड़ी मेज है। मैं इसे एक पूर्णांक कॉल/एस के आधार पर परिभाषित करना चाहता हूं जो मेरे द्वारा परिभाषित सीमा के बीच एक मूल्य है।आर: डेटाटेबल सबसेटिंग एक पूर्णांक कॉलम

मैं सोच रहा था कि प्रासंगिक कॉलम कुंजी के रूप में सेट है, यह बाइनरी खोज होगी लेकिन फिर सुनिश्चित नहीं है कि मैं पंक्तियों की एक श्रृंखला के बीच पंक्तियां पा सकता हूं।

नीचे उल्लिखित उदाहरण।

> n = 1e7 
> dt <- data.table(a=rnorm(n),b=sample(letters,replace=T,n)) 
> system.time(subset(dt, a > 1 & a < 2)) 
    user system elapsed 
    1.596 0.000 1.596 
> system.time(dt[a %between% c(1,2)]) 
    user system elapsed 
    1.168 0.000 1.168 

ऐसा कुछ ऐसा किया जा सकता है?

setkey(dt,a) 
dt[ ] : get me the rows between 1 and 2 values of the key 

धन्यवाद! -Abhi

+0

'बीच 'किसी भी समय सहेज नहीं पाएगा क्योंकि इसमें कोड' x> = निचला और x <= upper' है। 'dt [a> 1 और a <2]' उतनी तेज होगी जितनी तेजी से –

+0

एक कुंजी सेट करने के बारे में कैसे? मैंने अभी अपना प्रश्न अपडेट नहीं किया है, यह सुनिश्चित नहीं है कि मैं एक कुंजी पर एक विस्तृत खोज कर सकता हूं। – Abhi

उत्तर

1

मैं एक data.table विशेषज्ञ नहीं हूँ, लेकिन से मैं क्या कारण समझने एक key खोज setkey(dt, b) ; dt['a'] इतनी तेजी से किया जाता है, क्योंकि यह वेक्टर स्कैन के बजाय द्विआधारी खोज का उपयोग करता है। यह संख्यात्मक कॉलम के लिए संभव नहीं है, जहां को सब्सक्राइब करने के लिए बाइनरी ऑपरेटरों की आवश्यकता होती है।

dt[,Between:=ifelse(a > 1 & a < 2, 'yes', 'no')] 
setkey(dt, Between) 
> system.time(dt['yes']) 
    user system elapsed 
    0.04 0.00 0.03 

कौन सा, दिलचस्प है, है तेजी से भी की तुलना में:

एकमात्र विकल्प की तरह कुछ करने के लिए किया जाएगा

Index = dt[,a > 1 & a < 2] 
> system.time(dt[Index]) 
    user system elapsed 
    0.23 0.00 0.23 

लेकिन जब से तुम सिर्फ एक अलग डेटा के रूप में एक सबसेट बचा सकता है। वैसे भी, मुझे यह बहुत अधिक आवेदन नहीं दिख रहा है।

7

setkey करना महंगा होगा (भले ही आप 1.8.11 में तेज़ क्रम का उपयोग करना चाहते थे), क्योंकि इसे डेटा (संदर्भ द्वारा) को भी स्थानांतरित करना है।

हालांकि, आप floor फ़ंक्शन का उपयोग करके इस मामले को प्राप्त कर सकते हैं। असल में, यदि आप सभी संख्याओं को [1,2] (नोट: यहां 1 और 2 सहित) चाहते हैं, तो floor इन सभी मानों के लिए "1" का मान प्रदान करेगा। यही कारण है, आप कर सकते हैं:

system.time(t1 <- dt[floor(a) == 1]) 
# user system elapsed 
# 0.234 0.001 0.238 

यह dt[a >= 1 & a <=2] कर के बराबर दोगुनी गति से है और कर रहा है।

system.time(t2 <- dt[a >= 1 & a <= 2]) 
# user system elapsed 
# 0.518 0.081 0.601 

identical(t1,t2) # [1] TRUE 

हालांकि, चूंकि आपने समानता नहीं करना चाहते, तो आप एक हैक स्तंभ a से सहिष्णुता = .Machine$double.eps^0.5 घटाना करने के लिए उपयोग कर सकते हैं। यदि मान [1, 1+tolerance) श्रेणी में है, तो यह अभी भी 1 माना जाता है। और यदि यह और अधिक है, तो यह अब 1 (आंतरिक रूप से) नहीं है। यही है, यह सबसे छोटी संख्या> 1 है जिसे मशीन 1 के रूप में पहचाना जा सकता है। इसलिए, यदि आप सहिष्णुता द्वारा 'ए' घटाते हैं तो आंतरिक रूप से "1" के रूप में प्रतिनिधित्व की जाने वाली सभी संख्याएं < 1 और floor(.) परिणामस्वरूप 0 हो जाएंगी। , आपको इसके बजाय श्रेणी> 1 और < 2 मिल जाएगी। यही कारण है कि

dt[floor(a-.Machine$double.eps^0.5)==1] 

dt[a>1 & a<2] के रूप में बराबर परिणाम दे देंगे, है।


आप मदद कर सकता है तो शायद उस integer स्तंभ पर इस floor समारोह के साथ एक नया स्तंभ और सेटिंग कुंजी बनाने बार-बार ऐसा करने के लिए, कर दिया है तो:

dt[, fa := as.integer(floor(a-.Machine$double.eps^0.5))] 
system.time(setkey(dt, fa)) # v1.8.11 
# user system elapsed 
# 0.852 0.158 1.043 

अब, आप जो कुछ भी सीमा क्वेरी कर सकता है आप द्विआधारी खोज का उपयोग करना चाहते हैं:

> system.time(dt[J(1L)]) # equivalent to > 1 & < 2 
# user system elapsed 
# 0.071 0.002 0.076 
> system.time(dt[J(1:4)]) # equivalent to > 1 & < 5 
# user system elapsed 
# 0.082 0.002 0.085 
8

आपपर कुंजी सेट कर हैं(जिसमें कुछ समय लगेगा (n=1e7 के लिए मेरी मशीन पर 14.7 सेकेंड), तो आप रुचि के क्षेत्र के आरंभ और अंत की पहचान करने के लिए रोलिंग जॉइन का उपयोग कर सकते हैं।

# thus the following will work. 
dt[seq.int(dt[.(1),.I,roll=-1]$.I, dt[.(2), .I, roll=1]$.I)] 


n = 1e7 
dt <- data.table(a=rnorm(n),b=sample(letters,replace=T,n)) 
system.time(setkey(dt,a)) 
# This does take some time 
# user system elapsed 
# 14.72 0.00 14.73 
library(microbenchmark) 
f1 <- function() t1 <- dt[floor(a) == 1] 
f2 <- function() t2 <- dt[a >= 1 & a <= 2] 
f3 <- function() {t3 <- dt[seq.int(dt[.(1),.I,roll=-1]$.I, dt[.(2), .I, roll=1]$.I)] } 
microbenchmark(f1(),f2(),f3(), times=10) 
# Unit: milliseconds 
# expr  min  lq median  uq  max neval 
# f1() 371.62161 387.81815 394.92153 403.52299 489.61508 10 
# f2() 529.62952 536.23727 544.74470 631.55594 634.92275 10 
# f3() 65.58094 66.34703 67.04747 75.89296 89.10182 10 

अब यह "तेज़" है, लेकिन क्योंकि हमने पहले कुंजी को सेट करने में समय बिताया था।

f4 <- function(tolerance = 1e-7){ # adjust according to your needs 
    start = dt[J(1 + tolerance), .I[1], roll = -Inf]$V1 
    end = dt[J(2 - tolerance), .I[.N], roll = Inf]$V1 
if (start <= end) dt[start:end]} 
microbenchmark(f1(),f2(),f3(),f4(), times=10) 
# Unit: milliseconds 
# expr  min  lq median  uq  max neval 
# f1() 373.3313 391.07479 440.07025 488.54020 491.48141 10 
# f2() 523.2319 530.11218 533.57844 536.67767 629.53779 10 
# f3() 65.6238 65.71617 66.09967 66.56768 83.27646 10 
# f4() 65.8511 66.26432 66.62096 83.86476 87.01092 10 

Eddi का दृष्टिकोण बेंचमार्क करने के लिए @ Eddi के दृष्टिकोण को जोड़ने से थोड़ा अधिक सुरक्षित रूप में यह चल बिन्दु सहिष्णुता का ध्यान रखता है।

5

आप एक कुंजी सेट है, तो आपके डेटा सॉर्ट हो जाता है, तो बस अंतिमबिंदुओं पाते हैं और बीच में अंक लेने:

setkey(dt, a) 
tolerance = 1e-7 # adjust according to your needs 
start = dt[J(1 + tolerance), .I[1], roll = -Inf]$V1 
end = dt[J(2 - tolerance), .I[.N], roll = Inf]$V1 
if (start <= end) dt[start:end] 

यह एक छोटा सा अरुण के floor दृष्टिकोण की तुलना में धीमी हो जाएगा, यह बाद से क्या 2 जुड़ता है, लेकिन प्लस तरफ आप अपनी पसंद की किसी भी संख्या में प्लग कर सकते हैं।

+0

ने कहा कि रोलिंग जॉइन केवल एक ही वापसी मूल्य उत्पन्न करेगा, आप '[1]' '[.N]' सबसेटिंग से बच सकते हैं। – mnel

+1

मुझे यकीन था कि 'रोल' जल्द या बाद में रोल हो जाएगा :) – Arun

+1

@mnel जो वास्तव में मामला नहीं है,' data.table (a = c (1,1,1), key = 'a') [J (1), रोल = इंफ] ' – eddi

संबंधित मुद्दे