2011-06-23 9 views
7

संभव डुप्लिकेट:
Is R's apply family more than syntactic sugar"लागू करें" फ़ंक्शंस के क्या फायदे हैं? लूपों के बजाय "वे" उपयोग करने के लिए बेहतर कब होते हैं, और वे कब नहीं होते?

शीर्षक कहते हैं बस क्या। बेवकूफ सवाल, शायद, लेकिन मेरी समझ यह है कि "लागू" फ़ंक्शन का उपयोग करते समय, पुनरावृत्ति कोड Rarser के बजाय संकलित कोड में किया जाता है। ऐसा लगता है कि उदाहरण के लिए, उदाहरण के लिए, "लूप" की तुलना में केवल तेज है यदि बहुत सारे पुनरावृत्तियों हैं और प्रत्येक ऑपरेशन अपेक्षाकृत सरल है। उदाहरण के लिए, यदि लापरवाही में लिपटे फ़ंक्शन में एक सिंगल कॉल 10 सेकंड लेता है, और केवल, कहें, इसके 12 पुनरावृत्तियों, मैं कल्पना करूंगा कि "for" और "lapply" का उपयोग करने के बीच वास्तव में कोई अंतर नहीं है।

अब जब मैं इसके बारे में सोचता हूं, तो "लापरवाही" के अंदर फ़ंक्शन को किसी भी तरह से पार्स किया जाना चाहिए, तब तक "के लिए" के बजाय "लापरवाही" का उपयोग करने से कोई प्रदर्शन लाभ क्यों नहीं होना चाहिए जब तक कि आप वहां कुछ नहीं कर रहे हों के लिए संकलित संकलित हैं (जैसे संक्षेप या गुणा, इत्यादि)?

अग्रिम धन्यवाद!

जोश

+1

इस सूत्र देखें: http://stackoverflow.com/q/2275896/429846 –

+1

और यह एक: http://stackoverflow.com/q/5533246/210673 – Aaron

+1

वहाँ भी एक महान आर सहायता डेस्क लेख है इस पर मई, 2008 में: http://promberger.info/files/rnews-vectorvsloops2008.pdf – DrewConway

उत्तर

12

कई कारण एक एक for पाश पर एक apply परिवार समारोह पसंद कर सकते हैं, या इसके विपरीत हैं।

सबसे पहले, for() और apply(), sapply() आमतौर पर सही तरीके से निष्पादित होने पर एक दूसरे के जितना जल्दी होगा। lapply() इसमें से अधिकतर दूसरों के मुकाबले आर आंतरिक के भीतर संकलित कोड में काम कर रहा है, इसलिए उन कार्यों की तुलना में तेज़ हो सकता है। ऐसा लगता है कि गति लाभ सबसे बड़ा है जब डेटा पर "लूपिंग" का कार्य गणना समय का एक महत्वपूर्ण हिस्सा है; कई सामान्य दिन-प्रतिदिन उपयोगों में आप स्वाभाविक रूप से तेज़ी से lapply() से अधिक लाभ प्राप्त करने की संभावना नहीं रखते हैं। अंत में, ये सभी आर कार्यों को बुलाएंगे ताकि उन्हें व्याख्या करने और फिर चलाने की आवश्यकता हो।

for() छोरों अक्सर लागू करने के लिए, खासकर यदि आप एक प्रोग्रामिंग पृष्ठभूमि जहां छोरों प्रचलित हैं से आते हैं आसान हो सकता है। एक लूप में काम करना apply पारिवारिक कार्यों में से एक में पुनरावृत्ति गणना को मजबूर करने से अधिक प्राकृतिक हो सकता है। हालांकि, for() लूप का उपयोग करने के लिए, आपको स्टोरेज सेट अप करने के लिए कुछ अतिरिक्त काम करने की आवश्यकता है और लूप के आउटपुट को फिर से एक साथ प्लग करने का प्रबंधन करना है। apply फ़ंक्शंस स्वचालित रूप से आपके लिए यह करते हैं। उदा .:

IN <- runif(10) 
OUT <- logical(length = length(IN)) 
for(i in IN) { 
    OUT[i] <- IN > 0.5 
} 

एक मूर्खतापूर्ण उदाहरण है कि के रूप में > एक vectorised ऑपरेटर है, लेकिन मैं आप उत्पादन प्रबंधन करने के लिए अर्थात् है कि एक बिंदु बनाने के लिए, कुछ करना चाहता था। मुख्य बात यह है कि for() लूप के साथ, आप हमेशा लूप शुरू करने से पहले आउटपुट को पकड़ने के लिए पर्याप्त संग्रहण आवंटित करते हैं। यदि आपको नहीं पता कि आपको कितना भंडारण की आवश्यकता होगी, तो भंडारण का एक उचित हिस्सा आवंटित करें, और फिर लूप जांच में यदि आपने उस भंडारण को समाप्त कर दिया है, और भंडारण के दूसरे बड़े हिस्से पर बोल्ट करें।

मुख्य कारण, मेरे मन में, कार्यों का apply परिवार से एक का उपयोग के लिए और अधिक सुंदर, पठनीय कोड के लिए है। आउटपुट स्टोरेज को प्रबंधित करने और लूप को स्थापित करने के बजाय (ऊपर दिखाए गए अनुसार) हम आर को संभाल सकते हैं और संक्षेप में हमारे डेटा के सबसेट पर फ़ंक्शन चलाने के लिए आर से पूछ सकते हैं।स्पीड आमतौर पर कम से कम मेरे लिए निर्णय में प्रवेश नहीं करता है। मैं उस फ़ंक्शन का उपयोग करता हूं जो स्थिति को सर्वोत्तम रूप से उपयुक्त बनाता है और परिणामस्वरूप कोड को समझने में आसान, आसान होगा, क्योंकि मुझे याद नहीं है कि मैं सबसे तेज़ फ़ंक्शन चुनकर अधिक समय बर्बाद कर सकता हूं अगर मुझे याद नहीं है कि कोड क्या है एक दिन या एक सप्ताह या अधिक बाद में कर रहे हैं!

apply परिवार स्केलर या वेक्टर संचालन के लिए खुद को उधार देता है। for() लूप अक्सर एक ही इंडेक्स i का उपयोग करके एकाधिक पुनरावृत्त संचालन करने के लिए खुद को उधार देगा। उदाहरण के लिए, मैंने कोड लिखा है जो for() लूप के-ऑब्जेक्ट्स पर फोल्ड या बूटस्ट्रैप क्रॉस-सत्यापन करने के लिए उपयोग करता है। मैं शायद apply परिवार में से किसी एक के साथ ऐसा करने का मनोरंजन नहीं करूंगा क्योंकि प्रत्येक सीवी पुनरावृत्ति को कई संचालन की आवश्यकता होती है, वर्तमान फ्रेम में बहुत सारी ऑब्जेक्ट्स तक पहुंच होती है, और पुनरावृत्तियों के आउटपुट वाले कई आउटपुट ऑब्जेक्ट्स में भर जाती है।

अंतिम बिंदु, के बारे में क्यों lapply() संभवतः तेजी से कि for() या apply(), आप को एहसास है कि "लूप" व्याख्या आर कोड में या संकलित कोड में किया जा सकता की जरूरत है हो सकता है के रूप में। हां, दोनों अभी भी आर फ़ंक्शंस को कॉल करेंगे जिन्हें व्याख्या करने की आवश्यकता है, लेकिन यदि आप लूपिंग कर रहे हैं और सीधे संकलित सी कोड (जैसे lapply()) से कॉल कर रहे हैं, तो वह जगह है जहां प्रदर्शन लाभ apply() से आता है जो कहता है कि वास्तविक आर कोड में for() लूप। देखने के लिए कि यह एक for() पाश के आसपास एक आवरण है apply() का स्रोत देखें, और फिर lapply() के लिए कोड है, जो देखो:

> lapply 
function (X, FUN, ...) 
{ 
    FUN <- match.fun(FUN) 
    if (!is.vector(X) || is.object(X)) 
     X <- as.list(X) 
    .Internal(lapply(X, FUN)) 
} 
<environment: namespace:base> 

और आप देखना चाहिए क्यों lapply() के बीच गति में अंतर हो सकता है और for() और अन्य apply पारिवारिक कार्य। .Internal() आर द्वारा उपयोग किए गए संकलित सी कोड को कॉल करने के आर के तरीकों में से एक है। एक हेरफेर के अलावा, और FUN पर एक सैनिटी चेक के साथ, संपूर्ण गणना सी में किया जाता है, आर फ़ंक्शन FUN पर कॉल किया जाता है। apply() के स्रोत के साथ इसकी तुलना करें।

+0

नाइस ! आप संबंधित प्रश्नों में उससे परे नई जानकारी भी जोड़ पाए। धन्यवाद, मैंने कुछ नई चीजें सीखी हैं। – Aaron

+0

ग्रेट, धन्यवाद। मैं संभावित प्रदर्शन लाभ के बारे में एक बात स्पष्ट करना चाहता हूं। कोड को लापरवाही() में कॉल के अंदर निष्पादित किया जा रहा है, फिर भी प्रति पुनरावृत्ति जितना लंबा होगा, है ना?तो क्या मैं यह कहने में सही होगा कि ऐसी परिस्थिति में जहां प्रत्येक पुनरावृत्ति स्वयं कई सेकंड या उससे अधिक के आदेश पर जा रही है कि लागू होने पर प्रदर्शन लाभ() या() के लिए नगण्य होगा? – Josh

3

बर्न्स 'R Inferno (पीडीएफ), P25 से:

उपयोग एक स्पष्ट for पाश जब प्रत्येक यात्रा एक गैर तुच्छ काम है। लेकिन सरल पाश अधिक स्पष्ट हो सकता है और apply फ़ंक्शन का उपयोग करके कॉम्पैक्टली रूप से व्यक्त किया जा सकता है। इस नियम के लिए कम से कम एक अपवाद नहीं है ... यदि परिणाम एक सूची हो जाएगा और घटकों के कुछ NULL हो सकता है, तो पाश के लिए एक मुसीबत (बहुत बड़ी मुसीबत) और lapply देता है उम्मीद जवाब है।

+0

पूरी तरह से असहमत। यदि पुनरावृत्ति एक गैर-तुच्छ कार्य है, तो आप कोड के उस ब्लॉक को उचित कार्य में ले जाने से बेहतर होते हैं और फिर इसे * लागू() के साथ बुलाते हैं। – geoffjentry