Функции группировки (Tapply, by, Aggregate) и *Применить семью

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 это f (ряд/col I из матрицы)
  4. tapply(vector, grouping, f): Выходная матрица / массив, где элемент в матрице / массиве является значением f на группировке g вектора, а также g оттолкнутся к имена ряд / с
  5. by(dataframe, grouping, f): позволять g быть группировкой. применять f каждому столбцу группы / dataframe. довольно печатать группировку и ценность f на каждом столбце.
  6. aggregate(matrix, grouping, f): похожий на by, но вместо того, чтобы довольно печатать вывод, совокупность падает все в DataFrame.

Вопрос на сторону: я до сих пор не выучил Plyr или Reshigeape - будет 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
    

    Если вы хотите, чтобы строки/столбцы средств или суммы для 2D-матрицы, обязательно изучите высоко оптимизированную, молниеносную Quick 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))
    

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

    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")
    
  • ТАППЛИ - Когда вы хотите применить функцию к подвеска вектора и подмножества определяются некоторыми другими вектором, как правило, является фактором.

    Черные овцы * применяют семью, родами. Использование файла справки фразы «рваный массив» может быть немного сбивает с толку, но на самом деле это довольно просто.

    Вектор:

    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-analytic-strategy:

apply, sapply, lapply, by, aggregate

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

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

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

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

Мнемоника

  • lapply это список Применить, который действует в списке или вектор и возвращает список.
  • sapply это просто lapply (Функция по умолчанию возвращает вектор или матрицу, когда это возможно)
  • vapply это Проверено применимо (позволяет предопределить тип объекта возврата)
  • rapply это рекурсивный Подать заявку на вложенные списки, то есть списки в списках
  • tapply это Tagged Применить там, где теги идентифицируют подмножества
  • 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

Г говорит, что аргументы должны иметь одинаковую длину, говорят: «Мы хотим рассчитать 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 м при входе и 50K на выходе. Я также включил два популярных пакета, которые не использовались в то время, когда был задан вопрос, 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
## ...

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