2013-07-07 5 views
12

मान लीजिए हम गए एक vector (या उस बात के लिए एक data.frame) इस प्रकार है:"संख्यात्मक" प्रकार पर सबसेट करने से "लॉजिकल" प्रकार धीमा क्यों है?

set.seed(1) 
x <- sample(10, 1e6, TRUE) 

और एक x जहां x > 4 के सभी मूल्यों को प्राप्त करना चाहता है, कहते हैं:

a1 <- x[x > 4] # (or) 
a2 <- x[which(x > 4)] 

identical(a1, a2) # TRUE 

मैं सबसे लगता है लोग x[x > 4] पसंद करेंगे। लेकिन आश्चर्य की बात है (कम से कम मेरे लिए), which का उपयोग करके सबसेटिंग तेज है!

require(microbenchmark) 
microbenchmark(x[x > 4], x[which(x > 4)], times = 100) 

Unit: milliseconds 
      expr  min  lq median  uq  max neval 
     x[x > 4] 56.59467 57.70877 58.54111 59.94623 104.51472 100 
x[which(x > 4)] 26.62217 27.64490 28.31413 29.97908 99.68973 100 

यह मेरे बारे में 2.1 गुना तेज है। अंतर के लिए

एक संभावना है, मैंने सोचा, तथ्य यह है कि whichNA पर विचार नहीं करता लेकिन > उन्हें साथ ही रिटर्न की वजह से हो सकता है। लेकिन फिर तार्किक संचालन स्वयं इस अंतर का कारण होना चाहिए, जो मामले (स्पष्ट रूप से) नहीं है। यही कारण है:

microbenchmark(x > 4, which(x > 4), times = 100) 

Unit: milliseconds 
     expr  min  lq median  uq  max neval 
     x > 4 8.182576 10.06163 12.68847 14.64203 60.83536 100 
which(x > 4) 18.579746 19.94923 21.43004 23.75860 64.20152 100 

का उपयोग which 1.7 के बारे में गुना धीमी सिर्फ subsetting से पहले है। लेकिन which सबसेटिंग के दौरान/दौरान भारी पकड़ लेता है।

यह which कॉल .Internal(which(x))== जबकि कॉल .Primitive("==") के रूप में पसंद debugonce (thanks to @GavinSimpson) की मेरी हमेशा की तरह हथियार का उपयोग करना संभव नहीं लगता है।

मेरा प्रश्न इसलिए क्यों [numericwhich तार्किक वेक्टर > से उत्पन्न की तुलना में तेजी से उत्पन्न प्रकार पर है? कोई विचार?

उत्तर

3

ऐसा लगता है क्योंकि लॉजिकल वेक्टर द्वारा सबसेटिंग संख्यात्मक सूचकांक द्वारा सबसेटिंग से धीमी है।

> ii <- x > 4 
> ij <- which(x > 4) 
> 
> head(ii) 
[1] FALSE FALSE TRUE TRUE FALSE TRUE 
> head(ij) 
[1] 3 4 6 7 8 9 
> 
> microbenchmark(x[ii], x[ij], times = 100) 
Unit: milliseconds 
    expr  min  lq median  uq  max neval 
x[ii] 25.574977 26.15414 28.299858 31.080903 82.04686 100 
x[ij] 3.037134 3.31821 3.670096 7.516761 12.39738 100 

अपडेट किया गया:

शायद एक कारण यह है कि, सूचकांक सांख्यिक की छोटी लंबाई धीमी मूल्यांकन में subsetting और परिणामों के लिए (आंतरिक) पाश को कम कर सकते है। आप ik < ij < il

पा सकते हैं लेकिन वहाँ एक और अंतर हो सकता है, क्योंकि वहाँ ii और il के बीच एक बड़ा अंतर है।

> ii <- x > 4 
> 
> ij <- which(x > 4) 
> ik <- which(x > 9) 
> il <- which(x > -1) 
> 
> microbenchmark(x[ii], x[ij], x[ik], x[il], times = 100) 
Unit: microseconds 
    expr  min   lq median  uq  max neval 
x[ii] 25645.621 25986.2720 28466.412 30693.158 79582.484 100 
x[ij] 3111.974 3281.8280 3477.627 6142.216 55076.121 100 
x[ik] 585.723 628.2125 650.184 682.888 7551.084 100 
x[il] 5266.032 5773.9015 9073.614 10583.312 15113.791 100 
+0

धन्यवाद Kohske। मेरा सवाल मूल रूप से * क्यों * यह मामला है। मैंने यह स्पष्ट करने के लिए एक संपादन किया है। – Arun

+1

अपडेट किया गया है लेकिन संभवतः आप सबमिट करने के स्रोत कोड (सी कार्यान्वयन) में बेहतर खुदाई करेंगे यदि आप वास्तव में जानना चाहते हैं कि ओवरहेड कहां है। – kohske

+0

आप इस पोस्ट को थोड़ा सा अपडेट करना चाहते हैं, क्योंकि आपका पहला वाक्य प्रश्न के नए शीर्षक के प्रकाश में उल्लसित रूप से tautological लगता है। – Thomas

3

यहां मेरा लेना है। एक संख्यात्मक पर सब्सक्रिप्शन उन आवश्यक तत्वों को खींचने की अनुमति देता है जो आवश्यक हैं। लॉजिकल पर सब्सक्रिप्शन को इंडेक्स वेक्टर के प्रत्येक तत्व की जांच करने के लिए यह देखने के लिए कि यह TRUE है, और उसके बाद लक्ष्य वेक्टर के आवश्यक तत्वों की एक आंतरिक सूची बनाना आवश्यक है। इसमें दो कदम शामिल हैं, इसलिए अधिक समय लगेगा।

अंतर सबसे बड़ा है कि निकाले गए तत्वों की संख्या मूल वेक्टर के आकार के सापेक्ष छोटी है।उदाहरण के लिए:

> z <- rnorm(1e8) 
> system.time(z[which(z < -5)]) 
    user system elapsed 
    0.58 0.03 0.60 
> system.time(z[z < -5]) 
    user system elapsed 
    2.56 0.14 2.70 
> system.time(z[which(z < 5)]) 
    user system elapsed 
    1.39 0.30 1.68 
> system.time(z[z < 5]) 
    user system elapsed 
    2.82 0.44 3.26 

यहाँ, अगर आप केवल तत्वों के एक छोटे से अनुपात बाहर खींच रहे, का उपयोग कर which तार्किक अनुक्रमण की तुलना में एक बहुत छोटे से अनुपात लेता है (वहाँ अपने परीक्षण में जेड < -5 के 23 तत्वों थे) । हालांकि, यदि आप तत्वों का एक बड़ा हिस्सा निकाल रहे हैं, तो समय करीब हैं।

+2

लेकिन क्या यह 'जो भी' द्वारा नहीं किया जा रहा है, यानी 'इंडेक्स वेक्टर के प्रत्येक तत्व की जांच'? फिर प्रश्न बन जाता है कि 'जो भी विधि तार्किक सबसेटिंग' लक्ष्य वेक्टर के आवश्यक तत्वों की आंतरिक सूची बनाने 'के लिए उपयोग कर रहा है, उससे तेज है। – asb

+0

@ अरुण: मेरा बिंदु इस तथ्य पर था कि कोई व्यक्ति 'कौन सा' या तार्किक उप-सेटिंग करता है, कोई तार्किक वेक्टर में प्रत्येक तत्व की जांच करने जा रहा है। केवल, 'कौन सा' स्पष्ट रूप से ऐसा कर रहा है जबकि तार्किक उप-सेटिंग उस निहित तरीके से संबंधित है। यह एनएएस के प्रसार के प्रति उदासीनता होना चाहिए जो पहले तेज़ी से बनाता है क्योंकि यह बाद में एकमात्र अतिरिक्त काम है जो बाद में कर रहा है। शायद, हम इस अतिरिक्त काम को मापने के लिए एक तरीके से आ सकते हैं? – asb

+0

@ अरुण: आपका उदाहरण, अच्छी तरह से तैयार किए जाने पर, मेरे बिंदु पर स्पर्शपूर्ण है। शायद, मैं पर्याप्त स्पष्ट नहीं हो रहा हूँ। 'कौन सा लालची है और केवल TRUEs की तलाश में है। तार्किक उप-सेटिंग, ओटीओएच, को बुलियन राज्य की जांच करने से पहले लापता-नस्ल के बारे में चिंता करने की आवश्यकता है। मेरे दिमाग में, और मैं गलत हो सकता हूं, यह बाद में अतिरिक्त काम है। – asb

4

मुझे लगता है कि मुझे टिप्पणियों से बाहर निकलना चाहिए और एक उत्तर जोड़ना चाहिए। दूसरों ने जो जवाब दिया है और चर्चा की है, यह मेरा निर्माण है। एक बार मैं एक वेक्टर x और एक तार्किक वेक्टर x > 0, मैं दो तरीकों से x > 0 पर x सबसेट कर सकते हैं (मुझे यकीन है कि असली जवाब subset_dflt के लिए सी स्रोत में मौजूद हूँ।)

। मैं which का उपयोग कर सकता हूं या मैं इंडेक्सिंग के रूप में सीधे वेक्टर x > 0 का उपयोग कर सकता हूं। हालांकि, हमें ध्यान रखना चाहिए कि दोनों x[x > 0] के बाद समान नहीं हैं NA एस को संरक्षित करेंगे जबकि x[which(x > 0)] नहीं होगा।

हालांकि, किसी भी विधि में, मुझे वेक्टर x > 0 के प्रत्येक तत्व की जांच करने की आवश्यकता होगी। एक स्पष्ट which कॉल में मुझे प्रत्यक्ष उप-सेटिंग ऑपरेशन में केवल तत्व की बुलियन स्थिति की जांच करनी होगी, मुझे प्रत्येक तत्व की लापता-नस्ल और बूलियन स्थिति दोनों की जांच करनी होगी।

@flodel एक दिलचस्प अवलोकन लाता है। [, is.na, which, और | के बाद से सभी पुरातन या आंतरिक दिनचर्या कर रहे हैं, के कोई असाधारण भूमि के ऊपर मान लेते हैं और इस प्रयोग करते हैं:

microbenchmark(which(x > 0), x[which(x > 0)], x > 0 | is.na(x), x[x > 0], 
       unit="us", times=1000) 

Unit: microseconds 
      expr  min  lq median  uq  max neval 
    which(x > 0) 1219.274 1238.693 1261.439 1900.871 23085.57 1000 
    x[which(x > 0)] 1554.857 1592.543 1974.370 2339.238 23816.99 1000 
x > 0 | is.na(x) 3439.191 3459.296 3770.260 4194.474 25234.70 1000 
     x[x > 0] 3838.455 3876.816 4267.261 4621.544 25734.53 1000 

मंझला मूल्यों को देखते हुए, हम देख सकते हैं, x > 0 | is.na(x) संभालने के एक कच्चे मॉडल है क्या मैं कह रहा हूं कि तार्किक उप-सेटिंग में होता है, फिर 'सबसेट' में लिया गया वास्तविक समय ~ 500 है। और 'सबसेट' में लिया गया समय जिसके साथ ~ 700 हम हैं। दोनों संख्या तुलनीय हैं और संकेत देते हैं कि यह स्वयं को 'सबसेट' नहीं है जो एक विधि या किसी अन्य में महंगा है। इसके अलावा, which विधि में सस्ता है कि सबसेट की गणना करने के लिए किया जा रहा है।

+1

हां, बिल्कुल, हम दोनों अनुमान लगा रहे हैं। मैं इसे अद्यतन करने का प्रयास करूंगा जब मैं सी कोड पढ़ने के लिए समय निकाल सकता हूं। लेकिन वास्तव में एक बहुत ही दिलचस्प अवलोकन। मैंने व्यक्तिगत रूप से हमेशा ऐसी परिस्थितियों में उपयोग नहीं करना पसंद किया है। लेकिन मैं इसके बाद स्विच कर सकता हूं। – asb

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