दिलचस्प पोस्ट
Printf के लिए एच और एचएच संशोधक का क्या उद्देश्य है? एक फार्म के अंदर फार्म, ठीक है? एक स्ट्रिंग को सी ++ ऊपरी केस में कनवर्ट करें सी # दो सामान्य मूल्यों की तुलना करें जेडीबीसी में नाममात्र पैरामीटर SIGPIPEs को कैसे रोकें (या उन्हें ठीक से संभाल) संमिश्र प्राथमिक कुंजी और व्याख्या के साथ अनेक कई मैपिंग मानचित्रण: क्या सी और सी ++ दोनों में वैध कोड हो सकता है, जब प्रत्येक भाषा में संकलित हो? WinForms या WPF के लिए UI को वापस इवेंट भेजने के लिए सिंक्रनाइज़ेशन कॉन्टैक्स का उपयोग करना फ़ंक्शन की तुलना के साथ सॉर्ट फ़ंक्शन जावास्क्रिप्ट में कैसे काम करता है पायथन: दो सीएसवी फाइलों की तुलना करना और समान वस्तुओं की खोज करना स्टोरीबोर्ड को iPhone से लेकर iPad तक परिवर्तित करना साइलेंट आईओएस 11 पर ऐप को डिलीवर नहीं किया गया कुंजीपटल iPhone-Portrait-NumberPad के लिए टाइप 4 का समर्थन करने वाली कुंजीप्लेन नहीं मिल सकता; 3876877096_Portrait_iPhone-Simple-Pad_Default का उपयोग करते हुए चेकबॉक्स के साथ एक WPF कॉम्बो बॉक्स की तलाश में

क्या "* लागू" परिवार वास्तव में वेक्टर नहीं है?

इसलिए हम हर आर नए उपयोगकर्ता से कहा जाता है कि " apply करना वैक्टर नहीं है, पैट्रिक बर्न्स आर इन्फर्नो सर्कल 4 की जांच करें " जो कहता है (I उद्धरण):

एक आम रिफ्लेक्स को लागू परिवार में किसी फ़ंक्शन का उपयोग करना है। यह सदिश नहीं है , यह लूप-छुपा है । लागू फ़ंक्शन में इसकी परिभाषा में लूप के लिए है लापली फ़ंक्शन लूप को छोड़ देता है, लेकिन निष्पादन का समय लूप के लिए स्पष्ट रूप से बराबर होता है।

दरअसल, apply स्रोत कोड पर त्वरित रूप से लूप का पता चलता है:

 grep("for", capture.output(getAnywhere("apply")), value = TRUE) ## [1] " for (i in 1L:d2) {" " else for (i in 1L:d2) {" 

ठीक है अब तक, लेकिन lapply या vapply पर एक नज़र वास्तव में एक पूरी तरह से अलग तस्वीर का पता चलता है:

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

इसलिए जाहिरा तौर पर वहाँ लूप छिपने के for कोई आर नहीं है, बल्कि वे आंतरिक सी लिखित कार्य को बुला रहे हैं।

खरगोश छेद में एक त्वरित देखो बहुत ज्यादा एक ही तस्वीर से पता चलता है

इसके अलावा, हम उदाहरण के लिए colMeans फ़ंक्शन लेते हैं, जो कभी भी वेक्टर वाली नहीं होने का आरोपी था

 colMeans # function (x, na.rm = FALSE, dims = 1L) # { # if (is.data.frame(x)) # x <- as.matrix(x) # if (!is.array(x) || length(dn <- dim(x)) < 2L) # stop("'x' must be an array of at least two dimensions") # if (dims < 1L || dims > length(dn) - 1L) # stop("invalid 'dims'") # n <- prod(dn[1L:dims]) # dn <- dn[-(1L:dims)] # z <- if (is.complex(x)) # .Internal(colMeans(Re(x), n, prod(dn), na.rm)) + (0+1i) * # .Internal(colMeans(Im(x), n, prod(dn), na.rm)) # else .Internal(colMeans(x, n, prod(dn), na.rm)) # if (length(dn) > 1L) { # dim(z) <- dn # dimnames(z) <- dimnames(x)[-(1L:dims)] # } # else names(z) <- dimnames(x)[[dims + 1]] # z # } # <bytecode: 0x0000000008f89d20> # <environment: namespace:base> 

है ना? यह भी सिर्फ कॉल। .Internal(colMeans(... जो हम भी खरगोश छेद में पा सकते हैं। तो यह कैसे से अलग है। .Internal(lapply(.. ?

दरअसल, एक त्वरित बेंचमार्क से पता चलता है कि sapply डेटा सेट के for लूप के मुकाबले colMeans से ज्यादा बदतर नहीं होता है

 m <- as.data.frame(matrix(1:1e7, ncol = 1e5)) system.time(colMeans(m)) # user system elapsed # 1.69 0.03 1.73 system.time(sapply(m, mean)) # user system elapsed # 1.50 0.03 1.60 system.time(apply(m, 2, mean)) # user system elapsed # 3.84 0.03 3.90 system.time(for(i in 1:ncol(m)) mean(m[, i])) # user system elapsed # 13.78 0.01 13.93 

दूसरे शब्दों में, क्या यह कहना सही है कि lapply और vapply वास्तव में vapply हैं (तुलना करने के apply जो एक लूप के for है जो कि lapply भी है) और पैट्रिक बर्न्स को वास्तव में क्या कहना है?

वेब के समाधान से एकत्रित समाधान "क्या "* लागू" परिवार वास्तव में वेक्टर नहीं है?"

सबसे पहले, आपके उदाहरण में आप "data.frame" पर परीक्षण करते हैं जो colMeans लिए उचित नहीं है, apply और "[.data.frame" बाद से उनके पास ओवरहेड है:

 system.time(as.matrix(m)) #called by `colMeans` and `apply` # user system elapsed # 1.03 0.00 1.05 system.time(for(i in 1:ncol(m)) m[, i]) #in the `for` loop # user system elapsed # 12.93 0.01 13.07 

मैट्रिक्स पर, तस्वीर थोड़ा अलग है:

 mm = as.matrix(m) system.time(colMeans(mm)) # user system elapsed # 0.01 0.00 0.01 system.time(apply(mm, 2, mean)) # user system elapsed # 1.48 0.03 1.53 system.time(for(i in 1:ncol(mm)) mean(mm[, i])) # user system elapsed # 1.22 0.00 1.21 

प्रश्न के मुख्य भाग को रेगुडींग करना, lapply / mapply / आदि के बीच मुख्य अंतर और सीधा-सी आर-लोप्स है, जहां लूपिंग किया जाता है। रॉलेंड के रूप में, सी और आर दोनों loops को प्रत्येक आवृत्ति में एक आर समारोह का मूल्यांकन करने की आवश्यकता है जो कि सबसे अधिक महंगा है। वास्तव में तेजी से सी फ़ंक्शंस उन सभी होते हैं जो सी में सब कुछ करते हैं, तो मुझे लगता है, यह क्या होना चाहिए "vectorised" क्या है?

एक उदाहरण जहां हम "सूची" तत्वों में से प्रत्येक में माध्य खोजते हैं:

( 11 मई को संपादित करें : मेरा मानना ​​है कि "मतलब" खोजने के साथ उदाहरण "आरएफ" का मूल्यांकन करने के लिए एक आर फ़ंक्शन का मूल्यांकन और कम्पाइल किए गए कोड के बीच अंतर के लिए एक अच्छा सेटअप नहीं है, (1) "संख्यात्मक" पर आर के मतलब एल्गोरिथ्म की वजह से एक सरल sum(x) / length(x) और (2) से अधिक है, इसे "सूची" की length(x) >> lengths(x) पर परीक्षण करने के लिए और अधिक समझ लेनी चाहिए। इसलिए, "मतलब" उदाहरण ले जाया गया है अंत करने के लिए और दूसरे के साथ बदल दिया।)

एक सरल उदाहरण के रूप में हम प्रत्येक length == 1 के विपरीत की खोज को "सूची" के length == 1 तत्व पर विचार कर सकते हैं:

एक tmp.c फ़ाइल में:

 #include <Rh> #define USE_RINTERNALS #include <Rinternals.h> #include <Rdefines.h> /* call a C function inside another */ double oppC(double x) { return(ISNAN(x) ? NA_REAL : -x); } SEXP sapply_oppC(SEXP x) { SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x))); for(int i = 0; i < LENGTH(x); i++) REAL(ans)[i] = oppC(REAL(VECTOR_ELT(x, i))[0]); UNPROTECT(1); return(ans); } /* call an R function inside a C function; * will be used with 'f' as a closure and as a builtin */ SEXP sapply_oppR(SEXP x, SEXP f) { SEXP call = PROTECT(allocVector(LANGSXP, 2)); SETCAR(call, install(CHAR(STRING_ELT(f, 0)))); SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x))); for(int i = 0; i < LENGTH(x); i++) { SETCADR(call, VECTOR_ELT(x, i)); REAL(ans)[i] = REAL(eval(call, R_GlobalEnv))[0]; } UNPROTECT(2); return(ans); } 

और आर ओर में:

 system("R CMD SHLIB /home/~/tmp.c") dyn.load("/home/~/tmp.so") 

डेटा के साथ:

 set.seed(007) myls = rep_len(as.list(c(NA, runif(3))), 1e7) #a closure wrapper of `-` oppR = function(x) -x for_oppR = compiler::cmpfun(function(x, f) { f = match.fun(f) ans = numeric(length(x)) for(i in seq_along(x)) ans[[i]] = f(x[[i]]) return(ans) }) 

बेंचमार्किंग:

 #call a C function iteratively system.time({ sapplyC = .Call("sapply_oppC", myls) }) # user system elapsed # 0.048 0.000 0.047 #evaluate an R closure iteratively system.time({ sapplyRC = .Call("sapply_oppR", myls, "oppR") }) # user system elapsed # 3.348 0.000 3.358 #evaluate an R builtin iteratively system.time({ sapplyRCprim = .Call("sapply_oppR", myls, "-") }) # user system elapsed # 0.652 0.000 0.653 #loop with a R closure system.time({ forR = for_oppR(myls, "oppR") }) # user system elapsed # 4.396 0.000 4.409 #loop with an R builtin system.time({ forRprim = for_oppR(myls, "-") }) # user system elapsed # 1.908 0.000 1.913 #for reference and testing system.time({ sapplyR = unlist(lapply(myls, oppR)) }) # user system elapsed # 7.080 0.068 7.170 system.time({ sapplyRprim = unlist(lapply(myls, `-`)) }) # user system elapsed # 3.524 0.064 3.598 all.equal(sapplyR, sapplyRprim) #[1] TRUE all.equal(sapplyR, sapplyC) #[1] TRUE all.equal(sapplyR, sapplyRC) #[1] TRUE all.equal(sapplyR, sapplyRCprim) #[1] TRUE all.equal(sapplyR, forR) #[1] TRUE all.equal(sapplyR, forRprim) #[1] TRUE 

(मतलब खोजने का मूल उदाहरण निम्नानुसार है):

 #all computations in C all_C = inline::cfunction(sig = c(R_ls = "list"), body = ' SEXP tmp, ans; PROTECT(ans = allocVector(REALSXP, LENGTH(R_ls))); double *ptmp, *pans = REAL(ans); for(int i = 0; i < LENGTH(R_ls); i++) { pans[i] = 0.0; PROTECT(tmp = coerceVector(VECTOR_ELT(R_ls, i), REALSXP)); ptmp = REAL(tmp); for(int j = 0; j < LENGTH(tmp); j++) pans[i] += ptmp[j]; pans[i] /= LENGTH(tmp); UNPROTECT(1); } UNPROTECT(1); return(ans); ') #a very simple `lapply(x, mean)` C_and_R = inline::cfunction(sig = c(R_ls = "list"), body = ' SEXP call, ans, ret; PROTECT(call = allocList(2)); SET_TYPEOF(call, LANGSXP); SETCAR(call, install("mean")); PROTECT(ans = allocVector(VECSXP, LENGTH(R_ls))); PROTECT(ret = allocVector(REALSXP, LENGTH(ans))); for(int i = 0; i < LENGTH(R_ls); i++) { SETCADR(call, VECTOR_ELT(R_ls, i)); SET_VECTOR_ELT(ans, i, eval(call, R_GlobalEnv)); } double *pret = REAL(ret); for(int i = 0; i < LENGTH(ans); i++) pret[i] = REAL(VECTOR_ELT(ans, i))[0]; UNPROTECT(3); return(ret); ') R_lapply = function(x) unlist(lapply(x, mean)) R_loop = function(x) { ans = numeric(length(x)) for(i in seq_along(x)) ans[i] = mean(x[[i]]) return(ans) } R_loopcmp = compiler::cmpfun(R_loop) set.seed(007); myls = replicate(1e4, runif(1e3), simplify = FALSE) all.equal(all_C(myls), C_and_R(myls)) #[1] TRUE all.equal(all_C(myls), R_lapply(myls)) #[1] TRUE all.equal(all_C(myls), R_loop(myls)) #[1] TRUE all.equal(all_C(myls), R_loopcmp(myls)) #[1] TRUE microbenchmark::microbenchmark(all_C(myls), C_and_R(myls), R_lapply(myls), R_loop(myls), R_loopcmp(myls), times = 15) #Unit: milliseconds # expr min lq median uq max neval # all_C(myls) 37.29183 38.19107 38.69359 39.58083 41.3861 15 # C_and_R(myls) 117.21457 123.22044 124.58148 130.85513 169.6822 15 # R_lapply(myls) 98.48009 103.80717 106.55519 109.54890 116.3150 15 # R_loop(myls) 122.40367 130.85061 132.61378 138.53664 178.5128 15 # R_loopcmp(myls) 105.63228 111.38340 112.16781 115.68909 128.1976 15 

मेरे लिए, वेक्टरविसेशन मुख्य रूप से अपना कोड लिखना आसान है और समझने में आसान है।

एक वेक्टरयुक्त फ़ंक्शन का लक्ष्य, लूप के साथ जुड़े पुस्तक-पालन को समाप्त करना है। उदाहरण के लिए, के बजाय:

 means <- numeric(length(mtcars)) for (i in seq_along(mtcars)) { means[i] <- mean(mtcars[[i]]) } sds <- numeric(length(mtcars)) for (i in seq_along(mtcars)) { sds[i] <- sd(mtcars[[i]]) } 

तुम लिख सकते हो:

 means <- vapply(mtcars, mean, numeric(1)) sds <- vapply(mtcars, sd, numeric(1)) 

इससे यह देखने में आसान हो जाता है कि समान क्या है (इनपुट डेटा) और क्या अलग है (आप जिस फ़ंक्शन को लागू कर रहे हैं)।

वेक्टरविसेशन का एक माध्यमिक लाभ यह है कि आर-आर की तुलना में अक्सर सी-इन में लूप को लिखा जाता है। इसमें पर्याप्त प्रदर्शन लाभ हैं, लेकिन मुझे नहीं लगता कि यह वैक्टरविसेशन की महत्वपूर्ण संपत्ति है। वेक्टरेशन आपके मस्तिष्क को सहेजने के लिए मौलिक है, कम्प्यूटर का काम नहीं सहेज रहा है

मैं पैट्रिक बर्न्स से सहमत हूं कि यह बल्कि लूप छुपा रहा है और कोड वेक्टरविसेशन नहीं है। यहाँ पर क्यों:

इस C कोड स्निपेट पर विचार करें:

 for (int i=0; i<n; i++) c[i] = a[i] + b[i] 

हम क्या करना चाहते हैं, काफी स्पष्ट है लेकिन कार्य कैसे किया जाता है या यह कैसे किया जा सकता है वास्तव में नहीं है डिफ़ॉल्ट रूप से लूप के लिए एक सीरियल का निर्माण होता है। यह सूचित नहीं करता है कि कैसे या कैसे समानांतर में किया जा सकता है।

सबसे स्पष्ट तरीका यह है कि कोड अनुक्रमिक तरीके से चलाया जाता हैa[i] और b[i] को रजिस्टरों के पास लोड करें, उन्हें जोड़ें, परिणाम को c[i] , और प्रत्येक के लिए ऐसा करें i

हालांकि, आधुनिक प्रोसेसर के पास वेक्टर या सिम निर्देश सेट है, जो एक ही ऑपरेशन के दौरान उसी निर्देश के दौरान डेटा के सदिश पर काम करने में सक्षम है (जैसे, ऊपर दिखाए गए दो वैक्टर जोड़ना)। प्रोसेसर / आर्किटेक्चर के आधार पर, a ही समय के बजाय, एक ही अनुदेश के तहत a और b के चार नंबरों को जोड़ना संभव है, कहते हैं।

हम एकल निर्देश मल्टीपल डेटा का फायदा उठाना चाहते हैं और डेटा स्तर समानांतरता करते हैं , यानी, एक समय में 4 चीजें लोड करें, समय पर 4 चीजें जोड़ें, उदाहरण के लिए एक समय में 4 चीजों को स्टोर करें। और यह कोड vectorisation है

ध्यान दें कि यह कोड समानांतर से भिन्न है – जहां एकाधिक कंप्यूटेशन समवर्ती रूप से किए जाते हैं।

यह बहुत अच्छा होगा यदि संकलक कोड के ऐसे ब्लॉक को पहचानता है और स्वचालित रूप से वेक्टर बना देता है, जो एक कठिन काम है। कंप्यूटर विज्ञान में स्वत: कोड वेक्टरविजेशन एक चुनौतीपूर्ण शोध विषय है लेकिन समय के साथ, कंपाइलरों ने इसे बेहतर बना दिया है। आप यहां GNU-gcc की ऑटो वैक्टरिज़ेशन क्षमताओं की जांच कर सकते हैं । इसी तरह LLVM-clang लिए यहाँ । और आप gcc और ICC (इंटेल सी ++ कंपाइलर) की तुलना में अंतिम लिंक में कुछ बेंचमार्क भी पा सकते हैं।

उदाहरण के लिए gcc (मैं v4.9 पर हूं) स्वचालित रूप से- -O2 लेवल ऑप्टिमाइज़ेशन पर कोड नहीं करता। इसलिए यदि हम ऊपर दिखाए गए कोड को निष्पादित करने के लिए थे, तो इसे क्रमिक रूप से चलाया जाएगा यहां दो पूर्णांक वैक्टर लंबाई 500 मिलियन जोड़ने के लिए समय है।

हमें या तो फ्लैग -ftree-vectorize को जोड़ना होगा या स्तर -ftree-vectorize ऑप्टिमाइज़ेशन बदलना होगा। (ध्यान दें कि -O3 अन्य अतिरिक्त अनुकूलन भी करता है)। फ्लैग -fopt-info-vec उपयोगी है क्योंकि यह सूचित करता है कि जब कोई लूप सफलतापूर्वक vectorised था)।

 # compiling with -O2, -ftree-vectorize and -fopt-info-vec # test.c:32:5: note: loop vectorized # test.c:32:5: note: loop versioned for vectorization because of possible aliasing # test.c:32:5: note: loop peeled for vectorization to enhance alignment 

यह हमें बताता है कि समारोह vectorised है यहां 500 मिलियन लम्बाई के पूर्णांक वैक्टर पर गैर-वैक्टर वाले और वेक्टर वाले दोनों संस्करणों की तुलना की गई समय हैं:

 x = sample(100L, 500e6L, TRUE) y = sample(100L, 500e6L, TRUE) z = vector("integer", 500e6L) # result vector # non-vectorised, -O2 system.time(.Call("Csum", x, y, z)) # user system elapsed # 1.830 0.009 1.852 # vectorised using flags shown above at -O2 system.time(.Call("Csum", x, y, z)) # user system elapsed # 0.361 0.001 0.362 # both results are checked for identicalness, returns TRUE 

निरंतरता खोए बिना इस भाग को सुरक्षित रूप से छोड़ दिया जा सकता है

कम्पाइलर्स को हमेशा वेक्टरिस के लिए पर्याप्त जानकारी नहीं होगी। हम समानांतर प्रोग्रामिंग के लिए ओपनएमपी विनिर्देशन का उपयोग कर सकते हैं, जो कम्पाइलर को कोड को सदिश करने के लिए निर्देश देने के लिए एक सिम कंपाइलर निर्देश भी प्रदान करता है। यह सुनिश्चित करने के लिए जरूरी है कि कोई मेमोरी ओवरलैप, रेस परिस्थितियों आदि न हो। जब वैक्टरिंग कोड मैन्युअल रूप से किया जाता है, तो इसके परिणामस्वरूप गलत परिणाम होंगे।

 #pragma omp simd for (i=0; i<n; i++) c[i] = a[i] + b[i] 

ऐसा करने से, हम कम्पाइलर से विशेष रूप से पूछते हैं कि कोई भी बात न करें। संकलित समय -fopenmp का उपयोग करके -fopenmp एक्सटेंशन को सक्रिय करना होगा। ऐसा करके:

 # timing with -O2 + OpenMP with simd x = sample(100L, 500e6L, TRUE) y = sample(100L, 500e6L, TRUE) z = vector("integer", 500e6L) # result vector system.time(.Call("Cvecsum", x, y, z)) # user system elapsed # 0.360 0.001 0.360 

जो माहान है! यह जीसीसी v6.2.0 और एलएलवीएम क्लैंग v3.9.0 के साथ परीक्षण किया गया था (दोनों होमब्रे, मैकोड 10.12.3 के माध्यम से स्थापित किया गया), जो दोनों OpenMP 4.0 का समर्थन करते हैं।


इस मायने में, हालांकि, अर्रे प्रोग्रामिंग पर विकिपीडिया पेज का उल्लेख है कि पूरे एरे पर काम करने वाली भाषाएं आम तौर पर वेक्टर वाले परिचालन के रूप में कहती हैं , यह वास्तव में आईओएम छिपाने वाला लूप है (जब तक कि वास्तव में वेक्टर वाला नहीं)।

आर के मामले में, सी में colSums() rowSums() या colSums() कोड कोड colSums() दोहन ​​नहीं करते; यह सी में सिर्फ एक पाश है। apply() मामले में apply() , यह आर में है। इसलिए ये सब लूप छुपा रहे हैं

संक्षेप में, एक आर फ़ंक्शन लपेटकर:

बस C में एक for-loop लिख रहा C ! = आपके कोड को सदिश करना
बस में R लिखें! = आपके कोड को सदिश करना

उदाहरण के लिए इंटेल मैथ कर्नल लाइब्रेरी (एमकेएल) कार्यों के वेक्टर वाले रूपों को लागू करता है।

HTH


संदर्भ:

  1. जेम्स रेइंडर्स, इंटेल द्वारा बात करें (यह जवाब ज्यादातर इस उत्कृष्ट बात को संक्षेप करने का प्रयास है)

तो कुछ सामान्य उत्तरों में महान उत्तर / टिप्पणियों को जोड़ना और कुछ पृष्ठभूमि प्रदान करने के लिए: आर में चार प्रकार की छोरें हैं ( गैर-वेक्टर से सदिश किए गए क्रम में )

  1. आर के for जो बार-बार हर पुनरावृत्तियों में आर फ़ंक्शन कॉल करता है ( वेक्टर वाला नहीं )
  2. सी लूप, जो प्रत्येक पुनरावृत्तियों में आर फ़ंक्शन को बार-बार कॉल करता है ( वेक्टर वाला नहीं )
  3. सी पाश जो केवल आर फ़ंक्शन को एक बार कॉल करता है ( कुछ वैक्टरयुक्त )
  4. एक सादे सी लूप जो किसी भी आर फ़ंक्शन पर कॉल नहीं करता है और इसे स्वयं संकलित फ़ंक्शंस ( वेक्टरोइज्ड ) का उपयोग करता है

तो *apply परिवार दूसरा प्रकार है सिवाय apply जो पहले प्रकार से अधिक है

आप इसे इसके स्रोत कोड में टिप्पणी से समझ सकते हैं

/ *। आंतरिक (लापली (एक्स, फ़न)) * /

/ * यह एक विशेष है। अनंत, तो इसमें असामान्य तर्क है। यह है
एक बंद आवरण से कहा जाता है, इसलिए X और FUN वादे हैं। उदा बक्से में उपयोग के लिए अनिवार्य होना चाहिए। * /

इसका अर्थ है कि lapply सी कोड को आर से एक असमाप्त समारोह को स्वीकार करता है और बाद में इसे सी कोड के भीतर ही मूल्यांकन करता है। यह मूल रूप से lapply एस के बीच का अंतर है

 .Internal(lapply(X, FUN)) 

जिसमें एक आर तर्क है जिसमें आर फ़ंक्शन है

और colMeans .Internal कॉल में colMeans .Internal नहीं होता है

 .Internal(colMeans(Re(x), n, prod(dn), na.rm)) 

colMeans , colMeans , जानता है कि इसका उपयोग करने के लिए क्या आवश्यक है, इस प्रकार यह सी कोड के भीतर आंतरिक रूप से मतलब की गणना करता है।

आप स्पष्ट रूप से आर सी फ़ंक्शन के मूल्यांकन प्रक्रिया को प्रत्येक lapply कोड के भीतर चलना देख सकते हैं

  for(R_xlen_t i = 0; i < n; i++) { if (realIndx) REAL(ind)[0] = (double)(i + 1); else INTEGER(ind)[0] = (int)(i + 1); tmp = eval(R_fcall, rho); // <----------------------------- here it is if (MAYBE_REFERENCED(tmp)) tmp = lazy_duplicate(tmp); SET_VECTOR_ELT(ans, i, tmp); } 

चीजों को संक्षेप करने के लिए, lapply को lapply नहीं किया गया है , हालांकि इसके पास सादे आर for लूप के दो संभावित फायदे हैं

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

     ffR = function(x) { ans = vector("list", length(x)) for(i in seq_along(x)) ans[[i]] = x[[i]] ans } ffC = inline::cfunction(sig = c(R_x = "data.frame"), body = ' SEXP ans; PROTECT(ans = allocVector(VECSXP, LENGTH(R_x))); for(int i = 0; i < LENGTH(R_x); i++) SET_VECTOR_ELT(ans, i, VECTOR_ELT(R_x, i)); UNPROTECT(1); return(ans); ') set.seed(007) myls = replicate(1e3, runif(1e3), simplify = FALSE) mydf = as.data.frame(myls) all.equal(ffR(myls), ffC(myls)) #[1] TRUE all.equal(ffR(mydf), ffC(mydf)) #[1] TRUE microbenchmark::microbenchmark(ffR(myls), ffC(myls), ffR(mydf), ffC(mydf), times = 30) #Unit: microseconds # expr min lq median uq max neval # ffR(myls) 3933.764 3975.076 4073.540 5121.045 32956.580 30 # ffC(myls) 12.553 12.934 16.695 18.210 19.481 30 # ffR(mydf) 14799.340 15095.677 15661.889 16129.689 18439.908 30 # ffC(mydf) 12.599 13.068 15.835 18.402 20.509 30 
  2. जैसा कि @ रोलाण्ड द्वारा वर्णित है, यह एक संकलित सी लूप बजाता है, बल्कि एक व्याख्याकृत आर लूप


हालांकि जब आपके कोड को सदिश करना है, तो कुछ चीजें हैं जिन्हें आपको ध्यान में रखना चाहिए।

  1. यदि आपका डेटा सेट (चलो फोन करते हैं) कक्षा data.frame , कुछ data.frame फ़ंक्शंस (जैसे colMeans , colSums , rowSums , आदि) को पहले इसे मैट्रिक्स में बदलना होगा, केवल इसलिए कि यह कैसे डिजाइन किया गया है । इसका मतलब यह है कि एक बड़े df यह एक विशाल ऊपरी भाग बना सकता है। हालांकि lapply को ऐसा करने की आवश्यकता नहीं है क्योंकि यह वास्तविक वैक्टर को df बाहर निकाल देता है (जैसा कि data.frame केवल वैक्टर की सूची है) और इस प्रकार, यदि आपके पास इतने सारे कॉलम नहीं हैं, लेकिन कई पंक्तियां हैं, lapply(df, mean) कभी कभी colMeans(df) तुलना में बेहतर विकल्प हो सकता है
  2. याद करने के लिए एक और चीज यह है कि आर में कई तरह के फ़ंक्शन प्रकार हैं, जैसे कि .Primitive , और जेनेरिक ( S3 , S4 ) यहां कुछ अतिरिक्त जानकारी के लिए देखें। जेनेरिक फ़ंक्शन को एक विधि प्रेषण करना पड़ता है जो कभी-कभी एक महंगा ऑपरेशन होता है। उदाहरण के लिए, सामान्य S3 फ़ंक्शन होता है, जबकि sum Primitive । इस प्रकार, कुछ बार lapply(df, sum) ऊपर सूचीबद्ध सूचीबद्ध कारणों से तुलना में बहुत कुशल हो सकते हैं