Функции группировки (tapply, by, aggregate) и семейство * apply - PullRequest
983 голосов
/ 17 августа 2010

Всякий раз, когда я хочу сделать что-то «map» py в R, я обычно стараюсь использовать функцию из семейства apply.

Однако я никогда не понимал различий между ними - как {sapply, lapply и т. Д.} Применить функцию к входу / сгруппированному входу, как будет выглядеть выход,или даже то, что может быть входом - так что я часто просто просматриваю их все, пока не получу то, что хочу.

Может кто-нибудь объяснить, как использовать какой, когда?

Мой ток (возможноневерное / неполное) понимание ...

  1. sapply(vec, f): вход - вектор.output представляет собой вектор / матрицу, где элемент i равен f(vec[i]), что дает вам матрицу, если f имеет многоэлементный выход

  2. lapply(vec, f): то же, что и sapply, но вывод - это список?

  3. apply(matrix, 1/2, f): вход - это матрица.output - это вектор, где элемент i - это f (строка / столбец i матрицы)
  4. tapply(vector, grouping, f): output - это матрица / массив, где элемент в матрице / массиве - это значениеf при группировке g вектора, а g выталкивается в имена строк / столбцов
  5. by(dataframe, grouping, f): пусть g будет группировкой.применить f к каждому столбцу группы / фрейма данных.довольно печатать группировку и значение f в каждом столбце.
  6. aggregate(matrix, grouping, f): аналогично by, но вместо симпатичной печати выходных данных агрегат помещает все данные в кадр данных.

Дополнительный вопрос: я до сих пор не изучил plyr и не изменил форму - plyr или reshape заменит все это полностью?

Ответы [ 9 ]

1269 голосов
/ 22 августа 2011

R имеет множество * примененных функций, которые умело описаны в файлах справки (например, ?apply).Однако их достаточно, чтобы начинающим пользователям было трудно решить, какой из них подходит для их ситуации, или даже запомнить их все.У них может быть общее чувство, что «я должен использовать * функцию здесь», но поначалу бывает сложно держать их все прямо.

Несмотря на тот факт (отмеченный в других ответах), что большая частьфункциональность семейства * apply покрыта чрезвычайно популярным пакетом plyr, базовые функции остаются полезными и их стоит знать.

Этот ответ предназначен для своего рода указателя для новых пользователей, чтобы помочь направить их к правильной функции * apply для их конкретной проблемы.Обратите внимание, что это , а не , предназначенное просто для регургитации или замены документации 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-матрицы, обязательно изучите высокооптимизированную, молниеносную-quick colMeans, rowMeans, colSums, rowSums.

  • lapply - Когда вы хотите применить функцию к каждомуэлемент списка в свою очередь и получить список обратно.

    Это рабочая лошадка многих других * применяют функции.Отогните их код, и вы часто найдете 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
    
  • sapply - Когда вы хотите применить функцию к каждому элементусписка по очереди, но вам нужен вектор назад, а не список.

    Если вы наберете 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 - Для случаев, когда у вас есть несколько структур данных(например, векторы,списки), и вы хотите применить функцию к первым элементам каждого элемента, а затем ко вторым элементам каждого и т. д., приведя результат к вектору / массиву, как в 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
    
  • Map - Оболочка для 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 и т. Д.) Отсюда его статус "овец".

181 голосов
/ 17 августа 2010

Обратите внимание, что различные функции 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() легко переносятся на * 1009.* для получения полезного результата и т. д.

Концептуально изучение plyr не сложнее, чем понимание базовых *apply функций.

plyr и reshape функции почти полностью замениливсе эти функции в моем повседневном использовании, но также и из документа «Введение в Plyr»:

Связанные функции tapply и sweep не имеют соответствующей функции в plyr и остаются полезными. merge полезно для объединения сводок с исходными данными.

128 голосов
/ 09 октября 2011

Из слайда 21 из http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy:

apply, sapply, lapply, by, aggregate

(Надеюсь, ясно, что apply соответствует aaply @ Хэдли, а aggregate соответствует ddply @ Хэдли и т. Д. Слайд 20 того же слайдшера прояснит, если вы не получите его из этого изображения. )

(слева ввод, сверху вывод)

96 голосов
/ 25 апреля 2014

Сначала начните с Отличный ответ Джорана - сомнительно, что все может быть лучше.

Тогда следующая мнемоника может помочь вспомнить различия между ними. В то время как некоторые из них очевидны, другие могут быть не так - для них вы найдете оправдание в обсуждениях Джорана.

Мнемоника

  • lapply - это список , который применяется к списку или вектору и возвращает список.
  • sapply - это простой lapply (функция по умолчанию возвращает вектор или матрицу, когда это возможно)
  • vapply проверено применяется (позволяет предварительно указать тип возвращаемого объекта)
  • rapply - это рекурсивная , применяемая для вложенных списков, то есть списков в списках
  • tapply является тегом и применяется там, где теги идентифицируют подмножества
  • apply is generic : применяет функцию к строкам или столбцам матрицы (или, в более общем случае, к измерениям массива)

Создание правильного фона

Если использование семейства apply все еще кажется вам немного чуждым, возможно, вы упускаете ключевую точку зрения.

Эти две статьи могут помочь. Они обеспечивают необходимую основу для мотивации методов функционального программирования , которые предоставляются семейством функций apply.

Пользователи Lisp сразу распознают эту парадигму. Если вы не знакомы с Лиспом, как только вы разберетесь с FP, вы получите мощную точку зрения для использования в R - и apply будет иметь гораздо больше смысла.

45 голосов
/ 28 августа 2015

Поскольку я понял, что (очень превосходные) ответы на этот пост не содержат by и aggregate объяснений. Вот мой вклад.

BY

Функция 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.

Эти элементы облегчают работу с tapply в некоторых ситуациях с *1074*. Вот несколько примеров (доступно в документации):

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)

Предыдущий код достиг той же цели и результатов, в какой-то момент, какой инструмент использовать, это просто вопрос личных вкусов и потребностей; предыдущие два объекта имеют очень разные потребности с точки зрения поднабора.

31 голосов
/ 09 декабря 2015

Есть много отличных ответов, которые обсуждают различия в вариантах использования для каждой функции.Ни один из ответов не обсуждает различия в производительности.Это разумно, потому что различные функции ожидают различного ввода и производят различный вывод, но большинство из них имеют общую общую цель - оценивать по сериям / группам.Мой ответ будет сосредоточен на производительности.Из-за вышеизложенного создание ввода из векторов включено в синхронизацию, также функция 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
23 голосов
/ 16 мая 2016

Несмотря на все отличные ответы, есть еще 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 

Честно говоря, я не очень часто этим пользуюсь, но если вы создаете много пакетов или создаете много сред, это может пригодиться.

23 голосов
/ 06 ноября 2014

Возможно, стоит упомянуть 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
## ...
9 голосов
/ 16 июня 2017

Недавно я обнаружил довольно полезную функцию 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

Примечание: для этого простого примера тот же результат, конечно, может быть достигнут легче с помощью
apply(dataPoints, 2, scale)

...