그룹화 기능 (적용, 집계,) 및 * 적용 제품군


1040

R에서 "map"py를하고 싶을 때마다 나는 보통 apply가족 의 함수를 사용하려고합니다 .

그러나 { sapply,, lapply등}이 함수를 입력 / 그룹화 된 입력에 적용하는 방법, 출력 모양 또는 입력 수에 대한 차이점을 이해하지 못했습니다 . 내가 원하는 것을 얻을 때까지 모든 것을 살펴보십시오.

누군가 언제 어떤 것을 사용하는지 설명 할 수 있습니까?

내 현재 (아마 부정확하거나 불완전한) 이해는 ...

  1. sapply(vec, f): 입력은 벡터입니다. 출력은 벡터 / 행렬입니다. 여기서 element if(vec[i])이며 f다중 요소 출력이있는 경우 행렬을 제공합니다

  2. lapply(vec, f):와 동일 sapply하지만 출력이 목록입니까?

  3. apply(matrix, 1/2, f): 입력은 행렬입니다. 출력은 벡터이며, 여기서 요소 i는 f (행렬의 행 / col i)
  4. tapply(vector, grouping, f): 출력은 행렬 / 배열입니다. 여기서 행렬 / 배열의 요소 는 벡터 f그룹화 값이며 행 / 열 이름으로 푸시됩니다.gg
  5. by(dataframe, grouping, f): g그룹화 하자 . f그룹 / 데이터 프레임의 각 열에 적용 됩니다. 그룹화와 f각 열의 값을 인쇄하십시오 .
  6. aggregate(matrix, grouping, f):은 비슷 by하지만 출력을 예쁘게 인쇄하는 대신 모든 것을 데이터 프레임에 붙입니다.

사이드 질문 : 난 아직도 배우지 plyr 또는 모양 변경이 - 것 plyr또는 reshape완전히 모든 이들의 대체?


33
당신의 부수적 인 질문으로 : 많은 것들에 대해 plyr은 *apply()and를 직접 대체합니다 by. plyr (적어도 나에게)는 내가 기대하는 데이터 형식과 정확히 어떤 데이터가 튀어 나올지 항상 알고 있다는 점에서 훨씬 일관성이있는 것 같습니다. 그것은 많은 번거 로움을 덜어줍니다.
JD Long

12
또한의 추가 doBy및 선택 기능을 추가하는 것이 좋습니다 data.table.
Iterator

7
sapply그냥 lapply의 추가로 simplify2array출력합니다. apply원자 벡터로 강제 변환하지만 출력은 벡터 또는 목록 일 수 있습니다. by데이터 프레임을 하위 데이터 프레임으로 분할하지만 f열에서는 별도로 사용하지 않습니다 . 'data.frame'클래스에 대한 메소드가있는 경우에만 f에 의해 열 방식으로 적용 될 수 by있습니다. aggregate제네릭이므로 첫 번째 인수의 다른 클래스에 대해 다른 메소드가 존재합니다.
IRTFM

8
니모닉 : l은 'list', s는 'simplifying', t는 'per type'(각 그룹화 유형)입니다.
Lutz Prechelt

Rfast 패키지에는 다음과 같은 기능도 있습니다. eachcol.apply, apply.condition 등 R의 기능보다 빠릅니다.
Stefanos

답변:


1330

R에는 도움말 파일 (예 :)에 설명 된 많은 * 적용 기능이 있습니다 ?apply. 그러나 처음 사용하는 사람이 상황에 적합한 것을 결정하거나 모든 것을 기억하는 데 어려움이있을 수 있습니다. "여기에 * 적용 기능을 사용해야합니다"라는 일반적인 의미가있을 수 있지만 처음에는 모두 똑바로 유지하기가 어려울 수 있습니다.

* 응용 프로그램 제품군의 많은 기능이 매우 널리 사용되는 plyr패키지에 포함되어 있다는 사실에도 불구하고 (다른 답변으로 표시됨) 기본 기능은 유용하고 알 가치가 있습니다.

이 답변은 새로운 useR이 특정 문제에 대한 올바른 * 적용 기능을 지시 할 수 있도록 일종의 푯말 역할을하기위한 것 입니다. 이것은 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 매트릭스에 대한 행 / 열 수단 또는 금액을 원하는 경우, 확인 조사하는 고도로 최적화 된, 번개 빠른 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 요소 및 각의 두 번째 요소에 함수를 적용 할 때 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
    
  • 지도 - 래퍼로 mapplySIMPLIFY = 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따라서 그 검은 양 상태 등)를 포함한다.


32
당신이 찾을 것으로 예상 by순수한 분할 lapply하고 aggregate있다 tapply그들의 코어에서. 나는 검은 양이 훌륭한 천을 만든다고 생각합니다.
IRTFM

21
환상적인 응답! 이것은 공식 R 문서의 일부 여야합니다 :). 하나 개의 작은 제안 : 아마도 사용에 대한 몇 가지 글 머리 기호를 추가 aggregate하고 by뿐만 아니라? (나는 마침내 당신의 설명 후에 그것들을 이해합니다! 그러나 그것들은 꽤 일반적이므로 두 기능을 분리하고 특정 예제를 갖는 것이 유용 할 것입니다.)
grautur

3
@ grautur 나는 (a) 너무 길고 (b) 문서를 다시 쓰는 것을 피하기 위해이 답변에서 적극적으로 정리했습니다. , 등은 * 적용 함수를 기반으로 하는 반면 aggregate, by함수를 사용하는 방법은 사용자의 관점과는 완전히 다르므로 별도의 답변으로 요약해야합니다. 시간이 있으면 다른 사람이 나를 때려서 공감대를 얻습니다.
joran

4
또한?Mapmapply
baptiste

3
@jsanders-나는 그것에 동의하지 않을 것입니다. data.frames는 R의 절대적인 중심 부분이며 특히 list객체를 사용하여 자주 조작됩니다 lapply. 또한 전통적인 직사각형 데이터 세트에서 여러 유형의 벡터 / 인자를 그룹화하기위한 컨테이너 역할을합니다. 동안 data.tableplyr몇 가지 더 편안하다는 사실을 발견 구문의 특정 유형을 추가 할 수 있습니다, 그들은 연장에 작용하는 data.frame각각의.
thelatemail

191

참고로, 여기에 다양한 plyr기능이 기본 *apply기능에 어떻게 대응 되는지가 나와 있습니다 (plyr 웹 페이지 http://had.co.nz/plyr/ 의 소개에서 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기능을 이해하는 것보다 어렵지 않습니다 .

plyrreshape기능 내 매일 사용하는 거의 모든 기능을 대체했다. 그러나 인트로에서 Plyr 문서까지도 :

관련 함수 tapply및에 sweep해당하는 함수가 없으며 plyr유용합니다. merge요약을 원본 데이터와 결합하는 데 유용합니다.


13
처음부터 R을 배우기 시작했을 때 plyr MUCH *apply()는 함수 계열 보다 배우기가 더 쉽습니다 . 저에게는 ddply()SQL 집계 함수에 익숙했기 때문에 매우 직관적이었습니다. ddply()많은 문제를 해결하는 데 제 망치가되었습니다. 그 중 일부는 다른 명령으로 더 잘 해결 될 수있었습니다.
JD Long

1
plyr함수 의 개념이 함수와 유사 하다는 것을 알았 *apply으므로 하나를 수행하면 다른 것을 수행 할 수 있지만 plyr함수는 기억하기 쉽습니다. 그러나 나는 ddply()망치 에 전적으로 동의한다 !
JoFrhwld

1
plyr 패키지에는 join()병합과 유사한 작업을 수행 하는 기능이 있습니다. 아마도 plyr의 맥락에서 언급하는 것이 더 중요합니다.
marbel

가난하고 잊혀지지 않도록하자eapply
JDL

일반적으로 큰 대답이지만 유틸리티의 vapply단점과 단점을 경감한다고 생각합니다 sapply. 가장 큰 장점은 vapply출력 유형과 길이를 강제 적용하므로 정확한 예상 출력 또는 정보 오류가 발생한다는 것입니다. 반면에 sapply항상 명확하지 않은 규칙에 따라 출력을 단순화하려고 시도하고 그렇지 않으면 목록으로 돌아갑니다. 예를 들어 다음과 같이 출력 유형을 예측하십시오 sapply(list(1:5, 6:10, matrix(1:4, 2)), function(x) head(x, 1)). 무엇에 대해 sapply(list(matrix(1:4, 2), matrix(1:4, 2)), ...)?
Alexey Shiklomanov 18:32에

133

http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy의 슬라이드 21에서 :

적용, sapply, lapply, by, 집계

( apply@ Hadley 's aaplyaggregate해당하고 @ Hadley 's ddply등에 해당하는 것이 분명합니다. 동일한 슬라이드 공유 의 슬라이드 20은이 이미지에서 가져 오지 않으면 명확하게 나타납니다.)

(왼쪽은 입력, 상단은 출력)


4
슬라이드에 오타가 있습니까? 왼쪽 상단 셀은 적절해야합니다
JHowIX

100

먼저 Joran의 훌륭한 답변 부터 시작하십시오 .

그런 다음 다음 니모닉은 각각의 차이점을 기억하는 데 도움이 될 수 있습니다. 어떤 사람들은 분명하지만, 다른 사람들은 그렇지 않을 수도 있습니다.-Jolan의 토론에서 정당성을 찾을 수 있습니다.

기억술

  • lapplyA는 리스트 목록 또는 벡터에 작용하고 목록을 반환 적용됩니다.
  • sapplyA는 단순 lapply (a 벡터 또는 행렬 수를 반환하는 함수의 기본값)
  • vapplyA는 적용 확인은 (반환 개체 유형이 미리 지정된 할 수 있습니다)
  • rapplyA는 재귀가 중첩 된 목록, 목록 내에서 즉,리스트 신청
  • tapply태그가 서브 세트를 식별 하는 태그 적용입니다.
  • apply is generic : 함수를 행렬의 행 또는 열에 적용하거나보다 일반적으로 배열의 차원에 적용합니다.

올바른 배경 구축

apply가족을 사용하는 것이 여전히 당신에게 약간 외계인이라고 느껴 지면 , 요점을 놓친 것일 수 있습니다.

이 두 기사가 도움이 될 수 있습니다. 그것들 은 기능 군에 의해 제공되는 기능적 프로그래밍 기술 에 동기를 부여하는 데 필요한 배경을 제공 apply합니다.

Lisp 사용자는 패러다임을 즉시 인식하게됩니다. Lisp에 익숙하지 않다면 FP를 둘러 보면 R에서 사용할 수있는 강력한 관점을 얻게 apply되며 훨씬 더 합리적입니다.


51

이 게시물에 대한 (매우 훌륭한) 답변 byaggregate설명 이 없음을 깨달았습니다 . 여기 내 공헌이 있습니다.

으로

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 

우리가이 두 개체를 인쇄하는 경우 ctcb, 우리는 "기본적으로"동일한 결과를 유일한 차이는 그들이 어떻게 표시되고 다른에있는 class각각의 속성 by에 대한 cbarray대한 ct.

내가 말했듯이, by우리가 사용할 수 없을 때 의 힘이 생깁니다 tapply. 다음 코드는 한 가지 예입니다.

 tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) : 
  arguments must have same length

R은 인수의 길이가 같아야한다고 말합니다. "인수 summaryiris따라 모든 변수 를 계산하고 싶습니다 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

골재

aggregatetapply우리가 그런 식으로 사용 한다면 또 다른 사용 방법으로 볼 수 있습니다 .

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 CAN (필수 생략)리스트하고 출력이 그 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 = TRUEformula메소드는 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)

앞의 코드는 동일한 목표와 결과를 얻습니다. 어떤 시점에서 사용할 도구는 개인적인 취향과 요구의 문제입니다. 앞의 두 객체는 ​​서브 셋팅 측면에서 매우 다른 요구를 가지고 있습니다.


내가 말했듯이, by의 힘은 tapply를 사용할 수 없을 때 발생합니다. 다음 코드는 한 가지 예입니다.이 단어는 위에서 사용한 단어입니다. 요약 계산의 예를 제공했습니다. 요약 통계는 청소가 필요한 경우에만 계산할 수 있다고 말하겠습니다. 예 : data.frame(tapply(unlist(iris[,-5]),list(rep(iris[,5],ncol(iris[-5])),col(iris[-5])),summary))이것은 tapply . With the right splitting there is nothing you cant do with tapply . The only thing is it returns a matrix. Please be careful by saying we cant use tapply를 사용합니다.
Onyambu

35

각 기능에 대한 사용 사례의 차이점을 논의하는 훌륭한 답변이 많이 있습니다. 어떤 대답도 성능 차이에 대해 논의하지 않습니다. 이는 다양한 기능이 다양한 입력을 기대하고 다양한 출력을 생성하는 합리적인 원인이므로 대부분은 시리즈 / 그룹별로 평가하는 일반적인 공통 목표를 가지고 있습니다. 내 대답은 성능에 중점을 둘 것입니다. 벡터로부터의 입력 생성이 타이밍에 포함되어 있기 때문에, apply함수 도 측정되지 않습니다.

나는 두 개의 서로 다른 기능을 테스트 한 sumlength한 번에. 테스트 된 볼륨은 입력에서 50M이고 출력에서 ​​50K입니다. 또한 질문을 질문 할 때 널리 시간에 사용되지 않은이 개 현재 인기있는 패키지를 포함 한 data.tabledplyr. 좋은 성능을 목표로한다면 둘 다 살펴볼 가치가 있습니다.

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

dplyr이 applt 함수보다 낮은 것이 정상입니까?
Mostafa

1
@DimitriPetrenko 나는 그렇게 생각하지 않습니다, 왜 여기 있는지 모르겠습니다. 많은 요인이 작용하기 때문에 자신의 데이터에 대해 테스트하는 것이 가장 좋습니다.
jangorecki

28

여기에 모든 위대한 대답에도 불구하고 언급 할 가치가있는 두 가지 기본 기능, 유용한 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

값 벡터와 조건 벡터가있을 때 개인적으로 이것을 사용했으며 어떤 값이 어떤 조건과 일치하는지 확인하고 싶습니다.

적용

eapplylapply목록의 모든 요소에 함수를 적용하는 대신 환경의 모든 요소에 함수를 적용한다는 점을 제외하고 는 마찬가지 입니다. 예를 들어, 글로벌 환경에서 사용자 정의 함수 목록을 찾으려면 다음을 수행하십시오.

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 

솔직히 나는 이것을 많이 사용하지 않지만 많은 패키지를 작성하거나 많은 환경을 만드는 경우 유용 할 수 있습니다.


25

아마도 언급 할 가치가 있습니다 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
## ...

12

나는 최근에 다소 유용한 sweep기능을 발견하고 완전성을 위해 여기에 추가했습니다.

스위프

기본 아이디어는 배열을 행 또는 열 단위 로 스윕 하고 수정 된 배열을 반환하는 것입니다. 예를 들어 이것을 명확하게합니다 (source : 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)


1
그룹화와 관련이 있습니까?
Frank

2
@ 프랭크 : 글쎄, 당신에게 솔직히 말해서이 게시물의 제목은 다소 오해의 소지가 있습니다. 질문 자체를 읽을 때 "가족 적용"에 관한 것입니다. sweep예를 들어 apply, 여기에 언급 된 다른 모든 함수와 같이 고차 함수입니다 sapply. lapply따라서 1,000 개가 넘는 공감대와 여기에 주어진 예를 통해 동일한 답변을받을 수 있습니다. apply거기에 주어진 예를 살펴보십시오 .
vonjd 2016 년

2
스윕에는 잘못된 이름, 잘못된 기본값 및 잘못된 매개 변수 이름이 있습니다. :). 1) STATS는 첫 번째 입력과 동일한 크기의 행렬을 형성하기 위해 반복되는 벡터 또는 단일 값입니다 .2) 첫 번째 입력 과이 새로운 행렬에 FUN이 적용됩니다. 아마도 더 잘 설명 될 수 있습니다 : sweep(matrix(1:6,nrow=2),2,7:9,list). 루프 가 벡터화 된 함수를 사용할 수 apply있기 때문에 일반적으로보다 효율적 입니다. applysweep
Moody_Mudskipper

2

에서 붕괴 최근 크랑에 발표 패키지, 나는 공통의 대부분이 바로이 기능에 기능을 적용 압축하려고했습니다 :

  1. dapply(Data-Apply)는 행렬 및 data.frames의 행 또는 (기본) 열에 함수를 적용하고 (기본값) 동일한 유형과 동일한 속성을 가진 객체를 반환합니다 (각 계산의 결과가 원자 적이 지 않은 경우 제외 drop = TRUE). 성능은 lapplydata.frame 열과 비교할 수 apply있으며 행렬 행이나 열 보다 약 2 배 빠릅니다 . 병렬 처리는 mclapply(MAC 전용)을 통해 사용할 수 있습니다 .

통사론:

dapply(X, FUN, ..., MARGIN = 2, parallel = FALSE, mc.cores = 1L, 
       return = c("same", "matrix", "data.frame"), drop = TRUE)

예 :

# Apply to columns:
dapply(mtcars, log)
dapply(mtcars, sum)
dapply(mtcars, quantile)
# Apply to rows:
dapply(mtcars, sum, MARGIN = 1)
dapply(mtcars, quantile, MARGIN = 1)
# Return as matrix:
dapply(mtcars, quantile, return = "matrix")
dapply(mtcars, quantile, MARGIN = 1, return = "matrix")
# Same for matrices ...
  1. BY벡터, 행렬 및 data.frame 방법을 사용한 split-apply-combine 컴퓨팅에 일반적인 S3입니다. 그것은 속도가 매우 빠르고보다 tapply, by그리고 aggregate(AN은 빠르게보다 plyr큰 데이터를 dplyr빠르게 생각입니다).

통사론:

BY(X, g, FUN, ..., use.g.names = TRUE, sort = TRUE,
   expand.wide = FALSE, parallel = FALSE, mc.cores = 1L,
   return = c("same", "matrix", "data.frame", "list"))

예 :

# Vectors:
BY(iris$Sepal.Length, iris$Species, sum)
BY(iris$Sepal.Length, iris$Species, quantile)
BY(iris$Sepal.Length, iris$Species, quantile, expand.wide = TRUE) # This returns a matrix 
# Data.frames
BY(iris[-5], iris$Species, sum)
BY(iris[-5], iris$Species, quantile)
BY(iris[-5], iris$Species, quantile, expand.wide = TRUE) # This returns a wider data.frame
BY(iris[-5], iris$Species, quantile, return = "matrix") # This returns a matrix
# Same for matrices ...

그룹화 변수 목록도 제공 할 수 있습니다 g.

성능에 대한 이야기 ​​: 붕괴 의 주요 목표는 R에서 고성능 프로그래밍을 육성하고 분할 적용 조합을 넘어서 나아가는 것입니다. 이를 위해 패키지는 C의 전체 세트 ++ 기반의 빠른 일반적인 기능을 가지고있다 : fmean, fmedian, fmode, fsum, fprod, fsd, fvar, fmin, fmax, ffirst, flast, fNobs, fNdistinct, fscale, fbetween, fwithin, fHDbetween, fHDwithin, flag, fdifffgrowth. 이들은 데이터를 통한 단일 패스로 그룹화 된 계산을 수행합니다 (즉, 분할 및 재결합 없음).

통사론:

fFUN(x, g = NULL, [w = NULL,] TRA = NULL, [na.rm = TRUE,] use.g.names = TRUE, drop = TRUE)

예 :

v <- iris$Sepal.Length
f <- iris$Species

# Vectors
fmean(v)             # mean
fmean(v, f)          # grouped mean
fsd(v, f)            # grouped standard deviation
fsd(v, f, TRA = "/") # grouped scaling
fscale(v, f)         # grouped standardizing (scaling and centering)
fwithin(v, f)        # grouped demeaning

w <- abs(rnorm(nrow(iris)))
fmean(v, w = w)      # Weighted mean
fmean(v, f, w)       # Weighted grouped mean
fsd(v, f, w)         # Weighted grouped standard-deviation
fsd(v, f, w, "/")    # Weighted grouped scaling
fscale(v, f, w)      # Weighted grouped standardizing
fwithin(v, f, w)     # Weighted grouped demeaning

# Same using data.frames...
fmean(iris[-5], f)                # grouped mean
fscale(iris[-5], f)               # grouped standardizing
fwithin(iris[-5], f)              # grouped demeaning

# Same with matrices ...

패키지 비 네트에서 벤치 마크를 제공합니다. 빠른 기능을 사용하여 프로그래밍하는 것은 dplyr 또는 data.table을 사용 하여 프로그래밍하는 것보다 특히 작은 데이터뿐만 아니라 큰 데이터에 비해 훨씬 빠릅니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.