Группировка функций (tapply, в совокупности) и *применение семейного
всякий раз, когда я хочу сделать что-то "карта"py в R, я обычно пытаюсь использовать функцию в apply семья.
однако, я никогда не совсем понимал различия между ними -- как {sapply,lapply и т. д.} примените функцию к входному / сгруппированному входу, как будет выглядеть выход или даже какой может быть вход-поэтому я часто просто просматриваю их все, пока не получу то, что хочу.
может кто-нибудь объяснить, как использовать, какой, когда?
мой текущее (возможно неправильное/неполное) понимание есть...
sapply(vec, f): вход-это вектор. выход-это вектор / Матрица, где элементiиf(vec[i]), давая вам матрицу, еслиfимеет многоэлементный выходlapply(vec, f): какsapply, но вывод список?-
apply(matrix, 1/2, f): вход-это матрица. выход-это вектор, где элементiявляется f (строка / col I матрицы) -
tapply(vector, grouping, f): выход представляет собой матрицу / массив, где элемент в матрице / массиве является значениемfв группуgвектор, аgполучает толкнул в строку / col имена -
by(dataframe, grouping, f): даgбыть группировка. применитьfк каждому столбцу группы / фрейма данных. довольно распечатать группировку и значениеfв каждом столбце. -
aggregate(matrix, grouping, f): аналогичноby, но вместо того, чтобы довольно печатать вывод, aggregate вставляет все в a фрейм данных.
побочный вопрос: я до сих пор не узнал plyr или изменить форму-бы plyr или reshape заменить все это полностью?
9 ответов:
R имеет много * применить функции, которые умело описаны в файлах справки (например,
?apply). Однако их достаточно, чтобы начинающие пользователи могли с трудом решить, какой из них подходит для их ситуации или даже запомнить их все. Они могут иметь общее представление о том, что "я должен использовать функцию *apply здесь", но сначала может быть сложно сохранить их все прямо.несмотря на то (как отмечалось в других ответах), что большая часть функциональности * применить семья покрыта чрезвычайно популярным
plyrпакет, базовые функции остаются полезными и стоит знать.этот ответ призван действовать как своего рода указатель для новых пользователей, чтобы помочь направить их к правильной * применить функцию для их конкретной проблемы. Обратите внимание, это не предназначен для простого отрыгивания или замены документации R! Надеюсь, что этот ответ поможет вам решить, какая функция * apply подходит для вашей ситуации и тогда это до вас, чтобы исследовать его дальше. За одним исключением, различия в производительности не будут устранены.
применить -когда вы хотите применить функцию к строкам или столбцам матрицы (и многомерных аналогов); как правило, не рекомендуется для фреймов данных, поскольку он будет принуждать к матрице в первую очередь.
# 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если вы хотите, чтобы строки / столбцы означали или суммировали для 2D-матрицы, обязательно исследуйте сильно оптимизировано, молниеносно
colMeans,rowMeans,colSums,rowSums.lapply -если вы хотите применить функцию к каждому элементу a список в свою очередь и получить список обратно.
это рабочая лошадка многих других *применять функции. Корка верните их код и вы часто будете находить
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))если наша функция возвращает 2-мерную матрицу,
sapplyбудет делать по существу то же самое, рассматривая каждую возвращенную матрицу как один длинный вектор:sapply(1:5,function(x) matrix(x,2,2))если мы не указать
simplify = "array"в этом случае он будет использовать отдельных матриц для построения многомерного массива:sapply(1:5,function(x) matrix(x,2,2), simplify = "array")каждое из этих поведений, конечно, зависит от нашей функции, возвращающей векторы или матрицы одинаковой длины или измерение.
vapply -когда вы хотите использовать
sapplyно, возможно, нужно выжать еще немного скорости из вашего кода.на
vapply, вы в основном даете R пример того, что это такое ваша функция вернется, что может сэкономить некоторое время принуждения возвращается значения для размещения в одном атомарном векторе.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 91mapply -когда у вас есть несколько структуры данных (например, векторы, списки) и вы хотите применить функцию к 1-й элементов каждого, а затем 2-х элементов каждого и т. д., принуждая результат к вектору / массиву, как в
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, поэтому гарантируется возврат a список.Map(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15rapply -когда вы хотите применить функцию к каждому элементу вложенного списка структура, рекурсивно.
чтобы дать вам некоторое представление о том, как редкость
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 -когда вы хотите применить функцию к подмножеств в вектор и подмножества определяются некоторым другим вектором, обычно a факторный.
паршивая овца * применить семьи, своего рода. Использование файла справки фраза "рваный массив" может быть немного сбивает с толку, но это на самом деле совсем простой.
A вектор:
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более сложные примеры могут быть обработаны там, где определены подгруппы по уникальным сочетаниям можно составить список из нескольких факторов.
tapplyis аналогично по духу функции split-apply-combine, которые являются общие в R (aggregate,by,ave,ddplyи т. д.) Следовательно свой статус белой вороны.
на стороне обратите внимание, вот как различные
plyrфункции соответствуют основанию*applyфункции (из введения в документ plyr с веб-страницы plyrhttp://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()произвести полезный выход, etc.концептуально, обучение
plyrне сложнее, чем понять базу*applyфункции.
plyrиreshapeфункции заменили почти все эти функции в моем ежедневном использовании. Но, также из вступления к документу Plyr:соответствующие функции
tapplyиsweepне имеют соответствующей функции вplyr, и остаются полезными.mergeполезно для объединения резюме с исходными данными.
со слайда 21 из http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy:
(надеюсь, это ясно, что
applyсоответствует @ иaggregateсоответствует @ etc. Слайд 20 того же slideshare прояснит, если вы не получите его из этого изображения.)(слева-вход, сверху-выход)
начинается с отличный ответ Джорана -- сомнительно, что что-нибудь может лучше этого.
тогда следующие мнемоники могут помочь запомнить различия между ними. Хотя некоторые из них очевидны, другие могут быть менее очевидными-для них вы найдете оправдание в дискуссиях Джорана.
Мнемоника
lapplyЭто список применить, который действует на список или вектор и возвращает a список.sapplyэто простойlapply(функция по умолчанию возвращает вектор или матрицу, когда это возможно)vapply- это проверил применить (позволяет предварительно задать тип возвращаемого объекта)rapplyэто рекурсивные применить для вложенных списков, т. е. списков в спискахtapplyэто тегом применить, где теги идентифицируют подмножестваapplyis generic: применяет функцию к строкам или столбцам матрицы (или, в более общем случае, к размерам массива)создание правильного фона
при использовании
applyсемья все еще чувствует себя немного чуждым для вас, то это может быть, что вы упускаете ключевую точку зрения.эти две статьи могут помочь. Они обеспечивают необходимый фон для мотивации методы функционального программирования что в настоящее время предоставляются
applyсемейство функций.пользователи Lisp сразу же распознают парадигму. Если вы не знакомы с Lisp, как только вы получите свою голову вокруг FP, вы получите мощную точку зрения для использования в R-и
applyбудет иметь гораздо больше смысла.
- Расширенный R: Функциональное Программирование, Хэдли Уикхэм
- простое функциональное программирование в R, Майкл Бартон
так как я понял, что (очень отличные) ответы на этот пост не хватает
byиaggregateобъяснения. Вот мой вклад.BY
The
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 lengthR говорит, что аргументы должны иметь одинаковую длину, скажем :" мы хотим вычислить
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это то, что он может легко обрабатывать подмножества данных с
есть много отличных ответов, которые обсуждают различия в вариантах использования для каждой функции. Ни один из ответов не обсуждает различия в производительности. Это разумно, потому что различные функции ожидают различных входных данных и производят различные выходные данные, но большинство из них имеют общую общую цель для оценки по рядам/группам. Мой ответ будет сосредоточен на производительности. Из-за выше создание входных данных из векторов входит в сроки, а также
возможно, стоит упомянуть
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 ## ...
несмотря на все отличные ответы здесь, есть еще 2 базовые функции, которые заслуживают упоминания, полезные
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
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честно говоря, я не использую это очень много, но если вы создаете много пакетов или создаете много сред, это может пригодиться.
