وظائف التجميع (tapply ، بواسطة ، excregate) و *تطبيق الأسرة

StackOverflow https://stackoverflow.com/questions/3505701

سؤال

كلما أردت أن أفعل شيئًا "خريطة" PY في R ، عادة ما أحاول استخدام وظيفة في apply الأسرة.

ومع ذلك ، لم أفهم أبدًا الاختلافات بينهما - كيف {sapply, lapply, ، إلخ.

هل يمكن لأحد أن يشرح كيفية استخدام أي واحد متى؟

فهمي الحالي (ربما غير صحيح/غير مكتمل) هو ...

  1. sapply(vec, f): الإدخال هو متجه. الإخراج هو متجه/مصفوفة ، حيث العنصر i هو f(vec[i]), ، يعطيك مصفوفة إذا f لديه إخراج متعدد العناصر

  2. lapply(vec, f): مثل sapply, ، لكن الإخراج هو قائمة؟

  3. apply(matrix, 1/2, f): الإدخال مصفوفة. الإخراج هو ناقل ، حيث العنصر i IS F (الصف/العقيد الأول من المصفوفة)
  4. tapply(vector, grouping, f): الإخراج مصفوفة/صفيف ، حيث يكون عنصر في المصفوفة/الصفيف هو قيمة f في مجموعة g من المتجه ، و g يتم دفعها إلى أسماء الصف/العقيد
  5. by(dataframe, grouping, f): يترك g كن مجموعة. يتقدم f إلى كل عمود من مجموعة/dataframe. طباعة التجميع وقيمة f في كل عمود.
  6. aggregate(matrix, grouping, f): مشابه ل by, ، ولكن بدلاً من طباعة الإخراج الجميل ، يلتصق إجمالي كل شيء في إطار بيانات.

سؤال جانبي: ما زلت لم أتعلم plyr أو إعادة تشكيل - plyr أو reshape استبدل كل هذه بالكامل؟

هل كانت مفيدة؟

المحلول

R لديه العديد من *تطبيقات التطبيق الموصوفة باقتدار في ملفات المساعدة (على سبيل المثال ?apply). على الرغم من ذلك ، هناك ما يكفي منهم ، قد يواجه المستخدمون في البداية صعوبة في تحديد أي واحد مناسب لوضعهم أو حتى تذكرهم جميعًا. قد يكون لديهم شعور عام بأنه "يجب أن أستخدم وظيفة *تطبيق هنا" ، ولكن قد يكون من الصعب إبقائهم جميعًا مستقيمين في البداية.

على الرغم من الحقيقة (التي لوحظت في إجابات أخرى) أن الكثير من وظائف *تطبيق الأسرة مغطاة بالشهرة الشائعة للغاية plyr الحزمة ، تظل وظائف الأساس مفيدة وتستحق المعرفة.

تهدف هذه الإجابة إلى العمل كنوع من علامة لمستخدمي الجدد للمساعدة في توجيههم إلى وظيفة *تطبيق مشكلتهم الخاصة. ملاحظة ، هذا هو ليس تهدف ببساطة إلى تجديد أو استبدال وثائق R! الأمل هو أن تساعدك هذه الإجابة على تحديد *تطبيق الوظيفة التي تناسب موقفك ومن ثم الأمر متروك لك للبحث عنه أكثر. باستثناء واحد ، لن تتم معالجة اختلافات الأداء.

  • يتقدم - عندما تريد تطبيق وظيفة على صفوف أو أعمدة المصفوفة (ونظريات الأبعاد العليا) ؛ غير مستحسن بشكل عام لإطارات البيانات لأنه سيشجع على المصفوفة أولاً.

    # Two dimensional matrix
    M <- matrix(seq(1,16), 4, 4)
    
    # apply min to rows
    apply(M, 1, min)
    [1] 1 2 3 4
    
    # apply max to columns
    apply(M, 2, max)
    [1]  4  8 12 16
    
    # 3 dimensional array
    M <- array( seq(32), dim = c(4,4,2))
    
    # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension
    apply(M, 1, sum)
    # Result is one-dimensional
    [1] 120 128 136 144
    
    # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension
    apply(M, c(1,2), sum)
    # Result is two-dimensional
         [,1] [,2] [,3] [,4]
    [1,]   18   26   34   42
    [2,]   20   28   36   44
    [3,]   22   30   38   46
    [4,]   24   32   40   48
    

    إذا كنت تريد أن تعني الصف/العمود أو ملخصًا لمصفوفة ثنائية الأبعاد ، فتأكد من التحقيق في التحسين المُحسّن للغاية والبرق السريع colMeans, rowMeans, colSums, rowSums.

  • لابلي - عندما تريد تطبيق وظيفة على كل عنصر من عناصر القائمة بدوره واستعادة قائمة.

    هذا هو العمود الفقري للعديد من الوظائف الأخرى *تطبيق. قشر رمزهم وستجد في كثير من الأحيان lapply تحته.

    x <- list(a = 1, b = 1:3, c = 10:100) 
    lapply(x, FUN = length) 
    $a 
    [1] 1
    $b 
    [1] 3
    $c 
    [1] 91
    lapply(x, FUN = sum) 
    $a 
    [1] 1
    $b 
    [1] 6
    $c 
    [1] 5005
    
  • سابلي - عندما تريد تطبيق وظيفة على كل عنصر من عناصر القائمة بدوره ، لكنك تريد أ المتجه العودة ، بدلا من قائمة.

    إذا وجدت نفسك تكتب unlist(lapply(...)), وتوقف ونظرsapply.

    x <- list(a = 1, b = 1:3, c = 10:100)
    # Compare with above; a named vector, not a list 
    sapply(x, FUN = length)  
    a  b  c   
    1  3 91
    
    sapply(x, FUN = sum)   
    a    b    c    
    1    6 5005 
    

    في الاستخدامات الأكثر تقدما من sapply سيحاول إجبار النتيجة على صفيف متعدد الأبعاد ، إذا كان ذلك مناسبًا. على سبيل المثال ، إذا كانت وظيفتنا تُرجع متجهات من نفس الطول ، sapply سوف تستخدمها كأعمدة من المصفوفة:

    sapply(1:5,function(x) rnorm(3,x))
    

    إذا كانت وظيفتنا تُرجع مصفوفة ثنائية الأبعاد ، sapply سيفعل نفس الشيء بشكل أساسي ، معاملة كل مصفوفة يتم إرجاعها كمتجه طويل واحد:

    sapply(1:5,function(x) matrix(x,2,2))
    

    ما لم نحدد simplify = "array", ، في هذه الحالة ، ستستخدم المصفوفات الفردية لبناء صفيف متعدد الأبعاد:

    sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
    

    كل من هذه السلوكيات تتوقف بالطبع على وظائفنا العائدة أو المصفوفات من نفس الطول أو البعد.

  • فابلي - عندما تريد الاستخدام sapply ولكن ربما تحتاج إلى الضغط على بعض السرعة من الكود الخاص بك.

    إلى عن على vapply, ، تعطي بشكل أساسي مثالًا على نوع الشيء الذي ستعوده وظيفتك ، مما قد يوفر بعض الوقت لإكراه القيم التي تم إرجاعها لتناسبها في متجه ذري واحد.

    x <- list(a = 1, b = 1:3, c = 10:100)
    #Note that since the advantage here is mainly speed, this
    # example is only for illustration. We're telling R that
    # everything returned by length() should be an integer of 
    # length 1. 
    vapply(x, FUN = length, FUN.VALUE = 0L) 
    a  b  c  
    1  3 91
    
  • مابلي - لأنه عندما يكون لديك العديد من هياكل البيانات (مثل المتجهات والقوائم) وتريد تطبيق وظيفة على العناصر الأولى من كل منها ، ثم العناصر الثانية لكل منها ، وما إلى ذلك ، إكراه النتيجة على المتجه/الصفيف كما في sapply.

    هذا متعدد المتغيرات بمعنى أن وظيفتك يجب أن تقبل وسيطات متعددة.

    #Sums the 1st elements, the 2nd elements, etc. 
    mapply(sum, 1:5, 1:5, 1:5) 
    [1]  3  6  9 12 15
    #To do rep(1,4), rep(2,3), etc.
    mapply(rep, 1:4, 4:1)   
    [[1]]
    [1] 1 1 1 1
    
    [[2]]
    [1] 2 2 2
    
    [[3]]
    [1] 3 3
    
    [[4]]
    [1] 4
    
  • خريطة - غلاف ل mapply مع SIMPLIFY = FALSE, ، لذلك من الضمان إعادة قائمة.

    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3
    
    [[2]]
    [1] 6
    
    [[3]]
    [1] 9
    
    [[4]]
    [1] 12
    
    [[5]]
    [1] 15
    
  • rapply - عندما تريد تطبيق وظيفة على كل عنصر من عناصر أ قائمة متداخلة هيكل ، متكرر.

    لإعطائك فكرة عن مدى شهرة rapply هو ، لقد نسيت ذلك عند نشر هذه الإجابة لأول مرة! من الواضح ، أنا متأكد من أن الكثير من الناس يستخدمونه ، لكن YMMV. rapply من الأفضل توضيحها مع وظيفة محددة من قبل المستخدم للتطبيق:

    # Append ! to string, otherwise increment
    myFun <- function(x){
        if(is.character(x)){
          return(paste(x,"!",sep=""))
        }
        else{
          return(x + 1)
        }
    }
    
    #A nested list structure
    l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), 
              b = 3, c = "Yikes", 
              d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5)))
    
    
    # Result is named vector, coerced to character          
    rapply(l, myFun)
    
    # Result is a nested list like l, with values altered
    rapply(l, myFun, how="replace")
    
  • tapply - عندما تريد تطبيق وظيفة على مجموعات فرعية يتم تعريف المتجه والمجموعات الفرعية بواسطة ناقلات أخرى ، وعادة ما يكون عاملًا.

    الأغنام السوداء من *تطبيق الأسرة ، من نوع ما. يمكن أن يكون استخدام ملف المساعدة لعبارة "صفيف خشن" قليلاً مربك, ، لكنها في الواقع بسيطة للغاية.

    ناقل:

    x <- 1:20
    

    عامل (من نفس الطول!) تحديد المجموعات:

    y <- factor(rep(letters[1:5], each = 4))
    

    إضافة القيم في x داخل كل مجموعة فرعية محددة بواسطة y:

    tapply(x, y, sum)  
     a  b  c  d  e  
    10 26 42 58 74 
    

    يمكن التعامل مع أمثلة أكثر تعقيدًا حيث يتم تعريف المجموعات الفرعية من خلال المجموعات الفريدة لقائمة من عدة عوامل. tapply يشبه الروح مع الوظائف المسببة للمشتركة التي تكون شائعة في R (aggregate, by, ave, ddply, ، وما إلى ذلك) وبالتالي وضعها الغنم الأسود.

نصائح أخرى

في الملاحظة الجانبية ، إليكم كيف تختلف plyr تتوافق الوظائف مع القاعدة *apply وظائف (من وثيقة المقدمة إلى PLYR من صفحة الويب PLYR http://had.co.nz/plyr/)

Base function   Input   Output   plyr function 
---------------------------------------
aggregate        d       d       ddply + colwise 
apply            a       a/l     aaply / alply 
by               d       l       dlply 
lapply           l       l       llply  
mapply           a       a/l     maply / mlply 
replicate        r       a/l     raply / rlply 
sapply           l       a       laply 

أحد أهداف plyr هو توفير اتفاقيات تسمية متسقة لكل وظيفة ، وترميز أنواع بيانات الإدخال والإخراج في اسم الوظيفة. كما يوفر الاتساق في الإخراج ، في هذا الإخراج من dlply() يمكن اجتيازه بسهولة ldply() لإنتاج إخراج مفيد ، إلخ.

من الناحية النظرية ، التعلم plyr ليس أكثر صعوبة من فهم القاعدة *apply المهام.

plyr و reshape لقد استبدلت الوظائف كل هذه الوظائف تقريبًا في استخدام كل يوم. ولكن ، أيضًا من مقدمة إلى وثيقة Plyr:

الوظائف ذات الصلة tapply و sweep ليس لها وظيفة مقابلة في plyr, ، وتبقى مفيدة. merge مفيد للجمع بين الملخصات والبيانات الأصلية.

من الشريحة 21 من http://www.slideshare.net/hadley/plyr-one-data-anyalticty-strategy:

apply, sapply, lapply, by, aggregate

(نأمل أن يكون من الواضح ذلك apply يتوافق مع @هادلي aaply و aggregate يتوافق مع @هادلي ddply إلخ. ستوضح الشريحة 20 من نفس الشرائح إذا لم تحصل عليها من هذه الصورة.)

(على اليسار هو الإدخال ، في الأعلى هو الإخراج)

البداية الأولى مع إجابة جوران الممتازة - المشكوك فيه أن أي شيء يمكن أن يكون ذلك.

ثم قد يساعد Mnemonics التالي في تذكر الفروق بين كل منها. في حين أن البعض واضح ، فقد يكون البعض الآخر أقل من ذلك --- بالنسبة لهؤلاء ، ستجد مبررًا في مناقشات جوران.

Mnemonics

  • lapply هو قائمة تطبيق ما الذي يعمل على قائمة أو متجه ويعيد قائمة.
  • sapply هو بسيط lapply (الوظيفة الافتراضية لإعادة متجه أو مصفوفة عند الإمكان)
  • vapply هو تم التحقق (يسمح بتحديد نوع كائن الإرجاع مسبقًا)
  • rapply هو العودية التقدم بطلب للحصول على قوائم متداخلة ، أي قوائم ضمن القوائم
  • tapply هو الموسومة تطبيق حيث تحدد العلامات المجموعات الفرعية
  • apply هو نوعي: يطبق وظيفة على صفوف أو أعمدة المصفوفة (أو بشكل عام ، على أبعاد صفيف)

بناء الخلفية الصحيحة

إذا كنت تستخدم apply لا تزال العائلة تشعر بأنها غريبة بعض الشيء عليك ، ثم قد تكون أنك تفتقد وجهة نظر رئيسية.

يمكن أن تساعد هاتان المادتان. أنها توفر الخلفية اللازمة لتحفيز تقنيات البرمجة الوظيفية التي توفرها apply عائلة الوظائف.

سوف يتعرف مستخدمو LISP على النموذج على الفور. إذا لم تكن على دراية بـ Lisp ، فبمجرد أن تحصل على رأسك حول FP ، ستحصل على وجهة نظر قوية للاستخدام في R - و apply سوف يكون له معنى أكثر.

منذ أن أدركت أن الإجابات (الممتازة للغاية) لهذا المنشور النقص by و aggregate تفسيرات. ها هي مساهمتي.

بواسطة

ال by الوظيفة ، كما هو مذكور في الوثائق ، يمكن أن تكون "غلاف" ل tapply. قوة by ينشأ عندما نريد حساب مهمة tapply لا يمكن التعامل معها. مثال واحد هو هذا الرمز:

ct <- tapply(iris$Sepal.Width , iris$Species , summary )
cb <- by(iris$Sepal.Width , iris$Species , summary )

 cb
iris$Species: setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 
-------------------------------------------------------------- 
iris$Species: versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 
-------------------------------------------------------------- 
iris$Species: virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 


ct
$setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 

$versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 

$virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 

إذا طبعنا هذين الكائنين ، ct و cb, ، "لدينا بشكل أساسي" نفس النتائج والاختلافات الوحيدة في كيفية عرضها والمختلف class سمات ، على التوالي by إلى عن على cb و array إلى عن على ct.

كما قلت ، قوة by ينشأ عندما لا يمكننا استخدامه tapply; ؛ الرمز التالي مثال على ذلك:

 tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) : 
  arguments must have same length

يقول R أن الحجج يجب أن يكون لها نفس الأطوال ، قل "نريد حساب summary من كل المتغيرات في iris على طول العامل Species": لكن R فقط لا تستطيع فعل ذلك لأنه لا يعرف كيفية التعامل معه.

مع ال by وظيفة R إرسال طريقة محددة ل data frame الفصل ثم دع summary تعمل الوظيفة حتى لو كان طول الوسيطة الأولى (والنوع أيضًا) مختلفًا.

bywork <- by(iris, iris$Species, summary )

bywork
iris$Species: setosa
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200   versicolor: 0  
 Median :5.000   Median :3.400   Median :1.500   Median :0.200   virginica : 0  
 Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246                  
 3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300                  
 Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600                  
-------------------------------------------------------------- 
iris$Species: versicolor
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  
-------------------------------------------------------------- 
iris$Species: virginica
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
 Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026                  
 3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300                  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500     

إنه يعمل بالفعل والنتيجة مفاجئة للغاية. إنه كائن فئة by ذلك على طول Species (قل ، لكل منهم) يحسب summary من كل متغير.

لاحظ أنه إذا كانت الوسيطة الأولى هي data frame, ، يجب أن يكون للوظيفة التي تم إرسالها طريقة لتلك الفئة من الكائنات. على سبيل المثال ، نحن نستخدم هذا الرمز مع mean الوظيفة سيكون لدينا هذا الرمز ليس له أي معنى على الإطلاق:

 by(iris, iris$Species, mean)
iris$Species: setosa
[1] NA
------------------------------------------- 
iris$Species: versicolor
[1] NA
------------------------------------------- 
iris$Species: virginica
[1] NA
Warning messages:
1: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
2: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
3: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA

مجموع

aggregate يمكن اعتباره طريقة أخرى مختلفة للاستخدام tapply إذا استخدمناها بهذه الطريقة.

at <- tapply(iris$Sepal.Length , iris$Species , mean)
ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)

 at
    setosa versicolor  virginica 
     5.006      5.936      6.588 
 ag
     Group.1     x
1     setosa 5.006
2 versicolor 5.936
3  virginica 6.588

الاختلافان الفوريان هما الحجة الثانية لـ aggregate يجب كن قائمة الوقت tapply يستطيع (غير إلزامي) يكون قائمة وأن ناتج aggregate هو إطار بيانات بينما الإطار tapply هو array.

قوة aggregate هل يمكنه التعامل مع مجموعات فرعية من البيانات بسهولة subset الحجة وأنها لديها طرق ل ts الأشياء و formula كذلك.

هذه العناصر تجعل aggregate أسهل في العمل مع ذلك tapply في بعض الحالات. فيما يلي بعض الأمثلة (متوفرة في الوثائق):

ag <- aggregate(len ~ ., data = ToothGrowth, mean)

 ag
  supp dose   len
1   OJ  0.5 13.23
2   VC  0.5  7.98
3   OJ  1.0 22.70
4   VC  1.0 16.77
5   OJ  2.0 26.06
6   VC  2.0 26.14

يمكننا تحقيق الشيء نفسه مع tapply لكن بناء الجملة أصعب قليلاً والإخراج (في بعض الظروف) أقل قابلية للقراءة:

att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)

 att
       OJ    VC
0.5 13.23  7.98
1   22.70 16.77
2   26.06 26.14

هناك أوقات أخرى لا يمكننا فيها استخدامها by أو tapply وعلينا أن نستخدم aggregate.

 ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)

 ag1
  Month    Ozone     Temp
1     5 23.61538 66.73077
2     6 29.44444 78.22222
3     7 59.11538 83.88462
4     8 59.96154 83.96154
5     9 31.44828 76.89655

لا يمكننا الحصول على النتيجة السابقة مع tapply في مكالمة واحدة ولكن علينا حساب الوسيلة على طول Month لكل عناصر ثم ادمجها (لاحظ أيضًا أنه يتعين علينا الاتصال na.rm = TRUE, ، بسبب ال formula طرق aggregate الوظيفة لديها افتراضيا na.action = na.omit):

ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)

 cbind(ta1, ta2)
       ta1      ta2
5 23.61538 65.54839
6 29.44444 79.10000
7 59.11538 83.90323
8 59.96154 83.96774
9 31.44828 76.90000

بينما مع by لا يمكننا تحقيق ذلك في الواقع ، فإن استدعاء الوظيفة التالية إرجاع خطأ (ولكن على الأرجح يرتبط بالوظيفة المقدمة ، mean):

by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)

في أوقات أخرى ، تكون النتائج هي نفسها والاختلافات موجودة فقط في الفصل (ثم كيف يتم عرضها/طباعتها وليس فقط - على سبيل المثال ، كيفية مجموعة الفرع):

byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)

يحقق الكود السابق نفس الهدف والنتائج ، في بعض النقاط ، ما هي الأداة التي يجب استخدامها هي مجرد مسألة الأذواق والاحتياجات الشخصية ؛ الكائنان السابقان لهما احتياجات مختلفة جدًا من حيث الإعداد الفرعي.

هناك الكثير من الإجابات الرائعة التي تناقش الاختلافات في حالات الاستخدام لكل وظيفة. لم يناقش أي من الإجابة الاختلافات في الأداء. هذا هو السبب المعقول في توقعات مختلفة تتوقع مدخلات مختلفة وتنتج مخرجات مختلفة ، ومع ذلك فإن معظمها له هدف مشترك عام لتقييمه بواسطة السلسلة/المجموعات. إجابتي ستركز على الأداء. نظرًا لأعلى إنشاء الإدخال من المتجهات يتم تضمينه في التوقيت ، وأيضًا apply لا يتم قياس الوظيفة.

لقد اختبرت وظيفتين مختلفتين sum و length ذات مرة. يتم اختبار حجمها 50 مترًا على الإدخال و 50 ألفًا عند الإخراج. لقد قمت أيضًا بتضمين حزمتين شائعتين حاليًا لم يتم استخدامهما على نطاق واسع في الوقت الذي تم فيه طرح السؤال ، data.table و dplyr. كلاهما يستحق بالتأكيد البحث إذا كنت تهدف إلى أداء جيد.

library(dplyr)
library(data.table)
set.seed(123)
n = 5e7
k = 5e5
x = runif(n)
grp = sample(k, n, TRUE)

timing = list()

# sapply
timing[["sapply"]] = system.time({
    lt = split(x, grp)
    r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)
})

# lapply
timing[["lapply"]] = system.time({
    lt = split(x, grp)
    r.lapply = lapply(lt, function(x) list(sum(x), length(x)))
})

# tapply
timing[["tapply"]] = system.time(
    r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x)))
)

# by
timing[["by"]] = system.time(
    r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)

# aggregate
timing[["aggregate"]] = system.time(
    r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)

# dplyr
timing[["dplyr"]] = system.time({
    df = data_frame(x, grp)
    r.dplyr = summarise(group_by(df, grp), sum(x), n())
})

# data.table
timing[["data.table"]] = system.time({
    dt = setnames(setDT(list(x, grp)), c("x","grp"))
    r.data.table = dt[, .(sum(x), .N), grp]
})

# all output size match to group count
sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table), 
       function(x) (if(is.data.frame(x)) nrow else length)(x)==k)
#    sapply     lapply     tapply         by  aggregate      dplyr data.table 
#      TRUE       TRUE       TRUE       TRUE       TRUE       TRUE       TRUE 

# print timings
as.data.table(sapply(timing, `[[`, "elapsed"), keep.rownames = TRUE
              )[,.(fun = V1, elapsed = V2)
                ][order(-elapsed)]
#          fun elapsed
#1:  aggregate 109.139
#2:         by  25.738
#3:      dplyr  18.978
#4:     tapply  17.006
#5:     lapply  11.524
#6:     sapply  11.326
#7: data.table   2.686

ربما يجدر الإشارة ave. ave هو tapplyابن عم ودود. يعود النتائج إلى نموذج يمكنك توصيله مباشرة إلى إطار البيانات الخاص بك.

dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
means <- tapply(dfr$a, dfr$f, mean)
##  A    B    C    D    E 
## 2.5  6.5 10.5 14.5 18.5 

## great, but putting it back in the data frame is another line:

dfr$m <- means[dfr$f]

dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed!
dfr
##   a f    m   m2
##   1 A  2.5  2.5
##   2 A  2.5  2.5
##   3 A  2.5  2.5
##   4 A  2.5  2.5
##   5 B  6.5  6.5
##   6 B  6.5  6.5
##   7 B  6.5  6.5
##   ...

لا يوجد شيء في الحزمة الأساسية التي تعمل ave لإطارات البيانات بأكملها (كما by يشبه tapply لإطارات البيانات). لكن يمكنك حلها:

dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
    x <- dfr[x,]
    sum(x$m*x$m2)
})
dfr
##     a f    m   m2    foo
## 1   1 A  2.5  2.5    25
## 2   2 A  2.5  2.5    25
## 3   3 A  2.5  2.5    25
## ...

على الرغم من كل الإجابات الرائعة هنا ، هناك وظيفتان أساسيتان يستحقان ذكرهما ، المفيدة outer الوظيفة والغموض eapply وظيفة

خارجي

outer هل وظيفة مفيدة للغاية مخبأة باعتبارها وظيفة أكثر دنيوية. إذا قرأت المساعدة outer وصفه يقول:

The outer product of the arrays X and Y is the array A with dimension  
c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] =   
FUN(X[arrayindex.x], Y[arrayindex.y], ...).

مما يجعل الأمر يبدو أن هذا مفيد فقط للأشياء الجبر الخطي. ومع ذلك ، يمكن استخدامه مثل mapply لتطبيق وظيفة على متجهين من المدخلات. الفرق هو ذلك mapply سيتم تطبيق الوظيفة على العنصرين الأولين ، ثم الثانية وما إلى ذلك ، في حين outer سيتم تطبيق الوظيفة على كل مجموعة من عنصر واحد من المتجه الأول وواحد من الثانية. فمثلا:

 A<-c(1,3,5,7,9)
 B<-c(0,3,6,9,12)

mapply(FUN=pmax, A, B)

> mapply(FUN=pmax, A, B)
[1]  1  3  6  9 12

outer(A,B, pmax)

 > outer(A,B, pmax)
      [,1] [,2] [,3] [,4] [,5]
 [1,]    1    3    6    9   12
 [2,]    3    3    6    9   12
 [3,]    5    5    6    9   12
 [4,]    7    7    7    9   12
 [5,]    9    9    9    9   12

لقد استخدمت هذا الأمر هذا عندما يكون لدي متجه من القيم ومتجه من الظروف وأرغب في معرفة القيم التي تلبي الشروط.

إيبر

eapply يشبه lapply باستثناء ذلك بدلاً من تطبيق وظيفة على كل عنصر في القائمة ، فإنه يطبق وظيفة على كل عنصر في البيئة. على سبيل المثال ، إذا كنت ترغب في العثور على قائمة بالوظائف المحددة للمستخدم في البيئة العالمية:

A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
C<-list(x=1, y=2)
D<-function(x){x+1}

> eapply(.GlobalEnv, is.function)
$A
[1] FALSE

$B
[1] FALSE

$C
[1] FALSE

$D
[1] TRUE 

بصراحة ، لا أستخدم هذا كثيرًا ولكن إذا كنت تقوم ببناء الكثير من الحزم أو إنشاء الكثير من البيئات التي قد تكون مفيدة.

لقد اكتشفت مؤخرًا مفيدًا إلى حد ما sweep الوظيفة وأضفها هنا من أجل الاكتمال:

مسح

الفكرة الأساسية هي مسح من خلال صف صفيف أو عمود من حيث الأعمدة وإرجاع صفيف معدّل. مثال على ذلك سيوضح هذا (المصدر: Datacamp):

لنفترض أن لديك مصفوفة وتريد ذلك توحيد إنه عمود:

dataPoints <- matrix(4:15, nrow = 4)

# Find means per column with `apply()`
dataPoints_means <- apply(dataPoints, 2, mean)

# Find standard deviation with `apply()`
dataPoints_sdev <- apply(dataPoints, 2, sd)

# Center the points 
dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
print(dataPoints_Trans1)
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Return the result
dataPoints_Trans1
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Normalize
dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/")

# Return the result
dataPoints_Trans2
##            [,1]       [,2]       [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950  1.1618950

NB: لهذا المثال البسيط ، يمكن بالطبع تحقيق النتيجة نفسها بسهولة أكبر
apply(dataPoints, 2, scale)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top