Группировка функций (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 91
mapply -когда у вас есть несколько структуры данных (например, векторы, списки) и вы хотите применить функцию к 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] 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 -когда вы хотите применить функцию к подмножеств в вектор и подмножества определяются некоторым другим вектором, обычно 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
более сложные примеры могут быть обработаны там, где определены подгруппы по уникальным сочетаниям можно составить список из нескольких факторов.
tapply
is аналогично по духу функции 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
это тегом применить, где теги идентифицируют подмножестваapply
is 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 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
это то, что он может легко обрабатывать подмножества данных с
есть много отличных ответов, которые обсуждают различия в вариантах использования для каждой функции. Ни один из ответов не обсуждает различия в производительности. Это разумно, потому что различные функции ожидают различных входных данных и производят различные выходные данные, но большинство из них имеют общую общую цель для оценки по рядам/группам. Мой ответ будет сосредоточен на производительности. Из-за выше создание входных данных из векторов входит в сроки, а также
возможно, стоит упомянуть
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
честно говоря, я не использую это очень много, но если вы создаете много пакетов или создаете много сред, это может пригодиться.