وظائف التجميع (tapply ، بواسطة ، excregate) و *تطبيق الأسرة
سؤال
كلما أردت أن أفعل شيئًا "خريطة" PY في R ، عادة ما أحاول استخدام وظيفة في apply
الأسرة.
ومع ذلك ، لم أفهم أبدًا الاختلافات بينهما - كيف {sapply
, lapply
, ، إلخ.
هل يمكن لأحد أن يشرح كيفية استخدام أي واحد متى؟
فهمي الحالي (ربما غير صحيح/غير مكتمل) هو ...
sapply(vec, f)
: الإدخال هو متجه. الإخراج هو متجه/مصفوفة ، حيث العنصرi
هوf(vec[i])
, ، يعطيك مصفوفة إذاf
لديه إخراج متعدد العناصرlapply(vec, f)
: مثلsapply
, ، لكن الإخراج هو قائمة؟apply(matrix, 1/2, f)
: الإدخال مصفوفة. الإخراج هو ناقل ، حيث العنصرi
IS F (الصف/العقيد الأول من المصفوفة)tapply(vector, grouping, f)
: الإخراج مصفوفة/صفيف ، حيث يكون عنصر في المصفوفة/الصفيف هو قيمةf
في مجموعةg
من المتجه ، وg
يتم دفعها إلى أسماء الصف/العقيدby(dataframe, grouping, f)
: يتركg
كن مجموعة. يتقدمf
إلى كل عمود من مجموعة/dataframe. طباعة التجميع وقيمةf
في كل عمود.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
يتوافق مع @هادلي aaply
و aggregate
يتوافق مع @هادلي ddply
إلخ. ستوضح الشريحة 20 من نفس الشرائح إذا لم تحصل عليها من هذه الصورة.)
(على اليسار هو الإدخال ، في الأعلى هو الإخراج)
البداية الأولى مع إجابة جوران الممتازة - المشكوك فيه أن أي شيء يمكن أن يكون ذلك.
ثم قد يساعد Mnemonics التالي في تذكر الفروق بين كل منها. في حين أن البعض واضح ، فقد يكون البعض الآخر أقل من ذلك --- بالنسبة لهؤلاء ، ستجد مبررًا في مناقشات جوران.
Mnemonics
lapply
هو قائمة تطبيق ما الذي يعمل على قائمة أو متجه ويعيد قائمة.sapply
هو بسيطlapply
(الوظيفة الافتراضية لإعادة متجه أو مصفوفة عند الإمكان)vapply
هو تم التحقق (يسمح بتحديد نوع كائن الإرجاع مسبقًا)rapply
هو العودية التقدم بطلب للحصول على قوائم متداخلة ، أي قوائم ضمن القوائمtapply
هو الموسومة تطبيق حيث تحدد العلامات المجموعات الفرعيةapply
هو نوعي: يطبق وظيفة على صفوف أو أعمدة المصفوفة (أو بشكل عام ، على أبعاد صفيف)
بناء الخلفية الصحيحة
إذا كنت تستخدم apply
لا تزال العائلة تشعر بأنها غريبة بعض الشيء عليك ، ثم قد تكون أنك تفتقد وجهة نظر رئيسية.
يمكن أن تساعد هاتان المادتان. أنها توفر الخلفية اللازمة لتحفيز تقنيات البرمجة الوظيفية التي توفرها apply
عائلة الوظائف.
سوف يتعرف مستخدمو LISP على النموذج على الفور. إذا لم تكن على دراية بـ Lisp ، فبمجرد أن تحصل على رأسك حول FP ، ستحصل على وجهة نظر قوية للاستخدام في R - و apply
سوف يكون له معنى أكثر.
- R المتقدمة: البرمجة الوظيفية, ، بقلم هادلي ويكهام
- برمجة وظيفية بسيطة في ص, ، بقلم مايكل بارتون
منذ أن أدركت أن الإجابات (الممتازة للغاية) لهذا المنشور النقص 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)