ticks - r ggplot2 plot name




Функции группировки(напр., По совокупности) и семейства*apply (6)

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

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

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

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

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

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

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

Боковой вопрос: я до сих пор не узнал plyr или не изменил - будет ли plyr или plyr заменить все это полностью?


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

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

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

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

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

    Вектор:

    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 похож по духу на функции split-apply- ddply , которые являются общими в R ( aggregate , by , ave , ddply и т. д.). Отсюда и статус его черных овец.


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

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 для целых фреймов данных (так как это похоже на использование фреймов данных). Но вы можете выдумать это:

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
## ...

Недавно я обнаружил довольно полезную функцию sweep и добавил ее здесь для полноты:

подметать

Основная идея состоит в том, чтобы прокручивать массив по столбцу или столбцу и возвращать модифицированный массив. Пример сделает это ясно (source: datacamp ):

Предположим, у вас есть матрица и вы хотите standardize ее по столбцам:

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)


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

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


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

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

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


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

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

мнемоника

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

Построение правильного фона

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

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

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







r-faq