Philogrammer

Aggregate 와 Plyr

Aggregate

Aggregate는 SQL의 group by 와 유사한 기능을 합니다.

require(ggplot2)
## Loading required package: ggplot2
data(diamonds)
head(diamonds)
## # A tibble: 6 × 10
##   carat       cut color clarity depth table price     x     y     z
##   <dbl>     <ord> <ord>   <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1  0.23     Ideal     E     SI2  61.5    55   326  3.95  3.98  2.43
## 2  0.21   Premium     E     SI1  59.8    61   326  3.89  3.84  2.31
## 3  0.23      Good     E     VS1  56.9    65   327  4.05  4.07  2.31
## 4  0.29   Premium     I     VS2  62.4    58   334  4.20  4.23  2.63
## 5  0.31      Good     J     SI2  63.3    58   335  4.34  4.35  2.75
## 6  0.24 Very Good     J    VVS2  62.8    57   336  3.94  3.96  2.48
# Cut 으로 Price 가 집계됨을 뜻함. 

aggregate(price ~ cut, diamonds, mean)
##         cut    price
## 1      Fair 4358.758
## 2      Good 3928.864
## 3 Very Good 3981.760
## 4   Premium 4584.258
## 5     Ideal 3457.542
# 더하기로 추가 변수를 지정하여 여러개의 변수로 데이터를 그룹화

aggregate(price ~ cut + color, diamonds, mean)
##          cut color    price
## 1       Fair     D 4291.061
## 2       Good     D 3405.382
## 3  Very Good     D 3470.467
## 4    Premium     D 3631.293
## 5      Ideal     D 2629.095
## 6       Fair     E 3682.312
## 7       Good     E 3423.644
## 8  Very Good     E 3214.652
## 9    Premium     E 3538.914
## 10     Ideal     E 2597.550
## 11      Fair     F 3827.003
## 12      Good     F 3495.750
## 13 Very Good     F 3778.820
## 14   Premium     F 4324.890
## 15     Ideal     F 3374.939
## 16      Fair     G 4239.255
## 17      Good     G 4123.482
## 18 Very Good     G 3872.754
## 19   Premium     G 4500.742
## 20     Ideal     G 3720.706
## 21      Fair     H 5135.683
## 22      Good     H 4276.255
## 23 Very Good     H 4535.390
## 24   Premium     H 5216.707
## 25     Ideal     H 3889.335
## 26      Fair     I 4685.446
## 27      Good     I 5078.533
## 28 Very Good     I 5255.880
## 29   Premium     I 5946.181
## 30     Ideal     I 4451.970
## 31      Fair     J 4975.655
## 32      Good     J 4574.173
## 33 Very Good     J 5103.513
## 34   Premium     J 6294.592
## 35     Ideal     J 4918.186
aggregate(cbind(price, carat) ~ cut, diamonds, mean)
##         cut    price     carat
## 1      Fair 4358.758 1.0461366
## 2      Good 3928.864 0.8491847
## 3 Very Good 3981.760 0.8063814
## 4   Premium 4584.258 0.8919549
## 5     Ideal 3457.542 0.7028370
aggregate(cbind(price, carat) ~ cut + color, diamonds, mean)
##          cut color    price     carat
## 1       Fair     D 4291.061 0.9201227
## 2       Good     D 3405.382 0.7445166
## 3  Very Good     D 3470.467 0.6964243
## 4    Premium     D 3631.293 0.7215471
## 5      Ideal     D 2629.095 0.5657657
## 6       Fair     E 3682.312 0.8566071
## 7       Good     E 3423.644 0.7451340
## 8  Very Good     E 3214.652 0.6763167
## 9    Premium     E 3538.914 0.7177450
## 10     Ideal     E 2597.550 0.5784012
## 11      Fair     F 3827.003 0.9047115
## 12      Good     F 3495.750 0.7759296
## 13 Very Good     F 3778.820 0.7409612
## 14   Premium     F 4324.890 0.8270356
## 15     Ideal     F 3374.939 0.6558285
## 16      Fair     G 4239.255 1.0238217
## 17      Good     G 4123.482 0.8508955
## 18 Very Good     G 3872.754 0.7667986
## 19   Premium     G 4500.742 0.8414877
## 20     Ideal     G 3720.706 0.7007146
## 21      Fair     H 5135.683 1.2191749
## 22      Good     H 4276.255 0.9147293
## 23 Very Good     H 4535.390 0.9159485
## 24   Premium     H 5216.707 1.0164492
## 25     Ideal     H 3889.335 0.7995249
## 26      Fair     I 4685.446 1.1980571
## 27      Good     I 5078.533 1.0572222
## 28 Very Good     I 5255.880 1.0469518
## 29   Premium     I 5946.181 1.1449370
## 30     Ideal     I 4451.970 0.9130291
## 31      Fair     J 4975.655 1.3411765
## 32      Good     J 4574.173 1.0995440
## 33 Very Good     J 5103.513 1.1332153
## 34   Premium     J 6294.592 1.2930941
## 35     Ideal     J 4918.186 1.0635937

plyr

plyr 의 핵심은 ddply, llply, ldply 같은 함수들로 구성된다.

ddply

require(plyr)
## Loading required package: plyr
head(baseball)
##            id year stint team lg  g  ab  r  h X2b X3b hr rbi sb cs bb so
## 4   ansonca01 1871     1  RC1    25 120 29 39  11   3  0  16  6  2  2  1
## 44  forceda01 1871     1  WS3    32 162 45 45   9   4  0  29  8  0  4  0
## 68  mathebo01 1871     1  FW1    19  89 15 24   3   1  0  10  2  1  2  0
## 99  startjo01 1871     1  NY2    33 161 35 58   5   1  1  34  4  2  3  0
## 102 suttoez01 1871     1  CL1    29 128 35 45   3   7  3  23  3  1  1  0
## 106 whitede01 1871     1  CL1    29 146 40 47   6   5  1  21  2  2  4  1
##     ibb hbp sh sf gidp
## 4    NA  NA NA NA   NA
## 44   NA  NA NA NA   NA
## 68   NA  NA NA NA   NA
## 99   NA  NA NA NA   NA
## 102  NA  NA NA NA   NA
## 106  NA  NA NA NA   NA
#출루율 계산방법
# 출루율  = H(안타) + BB(볼넷) + HBP(사구) / AB(타수) + BB(볼넷) + HBP(사구) + SF(희생 플라이)

#데이터 일부 추출
#규칙 변화로 인한 1954년 이전 희생 플라이는 0 으로 처리
baseball$sf[baseball$year < 1954] <- 0
#잘 되었나 확인
any(is.na(baseball$sf))
## [1] FALSE
#hbp 가 NA 인 경우가 있어 NA 를 0으로 설정 
baseball$hbp[is.na(baseball$hbp)] <- 0
#제대로 되었나 확인
any(is.na(baseball$hbp))
## [1] FALSE
#한시즌에 50타석 이상인 선수만 남겨둠
baseball <- baseball[baseball$ab >= 50, ]

#특정연도 특정선수 OBP 계산은 vector 연산으로 쉽게 가능 
#with 함수는 data frame 의 이름을 명시하지 않고 열을 지정할 수 있도록 해줌
baseball$OBP <- with(baseball, (h+bb+hbp) / sum(ab+bb+hbp+sf))
tail(baseball)
##              id year stint team lg   g  ab  r   h X2b X3b hr rbi sb cs  bb
## 89499 claytro01 2007     1  TOR AL  69 189 23  48  14   0  1  12  2  1  14
## 89502 cirilje01 2007     1  MIN AL  50 153 18  40   9   2  2  21  2  0  15
## 89521 bondsba01 2007     1  SFN NL 126 340 75  94  14   0 28  66  5  0 132
## 89523 biggicr01 2007     1  HOU NL 141 517 68 130  31   3 10  50  4  3  23
## 89530 ausmubr01 2007     1  HOU NL 117 349 38  82  16   3  3  25  6  1  37
## 89533  aloumo01 2007     1  NYN NL  87 328 51 112  19   1 13  49  3  0  27
##        so ibb hbp sh sf gidp          OBP
## 89499  50   0   1  3  3    8 1.178054e-05
## 89502  13   0   1  3  2    9 1.047159e-05
## 89521  54  43   3  0  2   13 4.282133e-05
## 89523 112   0   3  7  5    5 2.917086e-05
## 89530  74   3   6  4  1   11 2.337409e-05
## 89533  30   5   2  0  3   13 2.636597e-05
#계산 수행하는 함수 만들고 ddply 사용하여 각 선수에 대해 계산 수행
obp <- function(data) {
  
  c(OBP = with(data, sum(h+bb+hbp) / sum(ab+bb+hbp+sf)))
  
}

#각 선수에 대한 커리어 OBP 를 계산하기 위해 ddply 를 사용
#SQL group by 의 by 에 해당한다. fun 은 function 의 obp 
careerOBP <- ddply(baseball, .variables = "id", .fun = obp)

#OBP로 결과를 정렬한다.
careerOBP <- careerOBP[order(careerOBP$OBP, decreasing = TRUE), ]

#결과를 확인한다.
head(careerOBP, 10)
##             id       OBP
## 1089 willite01 0.4816861
## 875   ruthba01 0.4742209
## 658  mcgrajo01 0.4657478
## 356  gehrilo01 0.4477848
## 85   bondsba01 0.4444622
## 476  hornsro01 0.4339068
## 184   cobbty01 0.4329655
## 327   foxxji01 0.4290509
## 953  speaktr01 0.4283386
## 191  collied01 0.4251246

llply

결과값을 반환할때

theList <- list(A = matrix(1:9, 3), B = 1:5, C = matrix(1:4, 2), D = 2)
lapply(theList, sum)
## $A
## [1] 45
## 
## $B
## [1] 15
## 
## $C
## [1] 10
## 
## $D
## [1] 2
llply(theList, sum)
## $A
## [1] 45
## 
## $B
## [1] 15
## 
## $C
## [1] 10
## 
## $D
## [1] 2
#list로 반환
sapply(theList, sum)
##  A  B  C  D 
## 45 15 10  2
laply(theList, sum)
## [1] 45 15 10  2
#Vector 로 반환하지만 이름있음
sapply(theList, sum)
##  A  B  C  D 
## 45 15 10  2
#Vector로 반환하지만 이름 없음
laply(theList, sum)
## [1] 45 15 10  2

plyr 보조함수

each 는 aggregate 함수에 여러 함수 사용 할 수 있게 해준다.

aggregate(price ~ cut, diamonds, each(mean, median))
##         cut price.mean price.median
## 1      Fair   4358.758     3282.000
## 2      Good   3928.864     3050.500
## 3 Very Good   3981.760     2648.000
## 4   Premium   4584.258     3185.000
## 5     Ideal   3457.542     1810.000

data.table

require(data.table)
## Loading required package: data.table
# Data Frame 생성
theDF <- data.frame(A = 1:10,
                    B = letters[1:10],
                    C = LETTERS[11:20],
                    D = rep(c("One", "Two", "Three"), length.out = 10))

# 하나의 data.table을 생성
theDT <- data.table(A = 1:10,
                    B = letters[1:10],
                    C = LETTERS[11:20],
                    D = rep(c("One", "Two", "Three"), length.out = 10))

#두개비교
theDF
##     A B C     D
## 1   1 a K   One
## 2   2 b L   Two
## 3   3 c M Three
## 4   4 d N   One
## 5   5 e O   Two
## 6   6 f P Three
## 7   7 g Q   One
## 8   8 h R   Two
## 9   9 i S Three
## 10 10 j T   One
theDT
##      A B C     D
##  1:  1 a K   One
##  2:  2 b L   Two
##  3:  3 c M Three
##  4:  4 d N   One
##  5:  5 e O   Two
##  6:  6 f P Three
##  7:  7 g Q   One
##  8:  8 h R   Two
##  9:  9 i S Three
## 10: 10 j T   One
# Data frame 은 문자 데이터를 factor로 변환
# Data table 은 character 로 남겨둠

class(theDF$B)
## [1] "factor"
class(theDT$B)
## [1] "character"
# Data frame을 Data table 로 변환도 가능

diamondsDT <- data.table(diamonds)
diamondsDT
##        carat       cut color clarity depth table price    x    y    z
##     1:  0.23     Ideal     E     SI2  61.5    55   326 3.95 3.98 2.43
##     2:  0.21   Premium     E     SI1  59.8    61   326 3.89 3.84 2.31
##     3:  0.23      Good     E     VS1  56.9    65   327 4.05 4.07 2.31
##     4:  0.29   Premium     I     VS2  62.4    58   334 4.20 4.23 2.63
##     5:  0.31      Good     J     SI2  63.3    58   335 4.34 4.35 2.75
##    ---                                                               
## 53936:  0.72     Ideal     D     SI1  60.8    57  2757 5.75 5.76 3.50
## 53937:  0.72      Good     D     SI1  63.1    55  2757 5.69 5.75 3.61
## 53938:  0.70 Very Good     D     SI1  62.8    60  2757 5.66 5.68 3.56
## 53939:  0.86   Premium     H     SI2  61.0    58  2757 6.15 6.12 3.74
## 53940:  0.75     Ideal     D     SI2  62.2    55  2757 5.83 5.87 3.64
#Data.table 은 첫 다섯줄과 끝 다섯줄만 출력함

#행의 접근방법은 data.frame 행 접근법과 동일함
theDT[1:2, ]
##    A B C   D
## 1: 1 a K One
## 2: 2 b L Two
theDT[theDT$A >= 7, ]
##     A B C     D
## 1:  7 g Q   One
## 2:  8 h R   Two
## 3:  9 i S Three
## 4: 10 j T   One
#data.table 에서는 열들을 character가 아닌 실제 이름의 list 로 명시

theDT[, list(A,C)]
##      A C
##  1:  1 K
##  2:  2 L
##  3:  3 M
##  4:  4 N
##  5:  5 O
##  6:  6 P
##  7:  7 Q
##  8:  8 R
##  9:  9 S
## 10: 10 T
#하나의 열만 선택
theDT[, B]
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
#data.table의 구조를 유지하면서 하나의 열을 선택
theDT[, list(B)]
##     B
##  1: a
##  2: b
##  3: c
##  4: d
##  5: e
##  6: f
##  7: g
##  8: h
##  9: i
## 10: j
#열 이름이 함수의 인자로 전달될 수도 있기 때문에, character 로 열 이름을 명시해야 한다면 with 를 FALSE 로 둠
b <- "B"
theDT[, b, with = FALSE]
##     B
##  1: a
##  2: b
##  3: c
##  4: d
##  5: e
##  6: f
##  7: g
##  8: h
##  9: i
## 10: j
theDT[, c("A", "C"), with = FALSE]
##      A C
##  1:  1 K
##  2:  2 L
##  3:  3 M
##  4:  4 N
##  5:  5 O
##  6:  6 P
##  7:  7 Q
##  8:  8 R
##  9:  9 S
## 10: 10 T
# 이러한 사용방식은 list 대신 vector에 열 이름을 지정한 것이다.

키(key)

#몇 가지 data.table이 메모리에 있으며 그것에 대한 많은 정보를 볼 수 있는지 알려줌
tables()
##      NAME         NROW NCOL MB
## [1,] diamondsDT 53,940   10  4
## [2,] theDT          10    4  1
##      COLS                                            KEY
## [1,] carat,cut,color,clarity,depth,table,price,x,y,z    
## [2,] A,B,C,D                                            
## Total: 5MB
#Key 는 data.table 열을 인덱싱하는데 사용되며 속도 향상을 제공함
# 키 설정법
setkey(theDT, D)
# data Table 다시보기
tables()
##      NAME         NROW NCOL MB
## [1,] diamondsDT 53,940   10  4
## [2,] theDT          10    4  1
##      COLS                                            KEY
## [1,] carat,cut,color,clarity,depth,table,price,x,y,z    
## [2,] A,B,C,D                                         D  
## Total: 5MB
key(theDT)
## [1] "D"
#키 열의 값으로도 행 선택이 가능하다.
theDT["One", ]
##     A B C   D
## 1:  1 a K One
## 2:  4 d N One
## 3:  7 g Q One
## 4: 10 j T One
theDT[c("One", "Two"), ]
##     A B C   D
## 1:  1 a K One
## 2:  4 d N One
## 3:  7 g Q One
## 4: 10 j T One
## 5:  2 b L Two
## 6:  5 e O Two
## 7:  8 h R Two
# 다수의 열도 키 설정 가능
setkey(diamondsDT, cut, color)

#모든 키에 따라 행에 접근하기 위한 J 함수가 있음
#J 함수는 여러 인자를 취하는데 각각은 선택할 값의 vector 임

diamondsDT[J("Ideal", "E"), ]
##       carat   cut color clarity depth table price    x    y    z
##    1:  0.23 Ideal     E     SI2  61.5    55   326 3.95 3.98 2.43
##    2:  0.26 Ideal     E    VVS2  62.9    58   554 4.02 4.06 2.54
##    3:  0.70 Ideal     E     SI1  62.5    57  2757 5.70 5.72 3.57
##    4:  0.59 Ideal     E    VVS2  62.0    55  2761 5.38 5.43 3.35
##    5:  0.74 Ideal     E     SI2  62.2    56  2761 5.80 5.84 3.62
##   ---                                                           
## 3899:  0.70 Ideal     E     SI1  61.7    55  2745 5.71 5.74 3.53
## 3900:  0.51 Ideal     E    VVS1  61.9    54  2745 5.17 5.11 3.18
## 3901:  0.56 Ideal     E    VVS1  62.1    56  2750 5.28 5.29 3.28
## 3902:  0.77 Ideal     E     SI2  62.1    56  2753 5.84 5.86 3.63
## 3903:  0.71 Ideal     E     SI1  61.9    56  2756 5.71 5.73 3.54
diamondsDT[J("Ideal", c("E", "D"))]
##       carat   cut color clarity depth table price    x    y    z
##    1:  0.23 Ideal     E     SI2  61.5    55   326 3.95 3.98 2.43
##    2:  0.26 Ideal     E    VVS2  62.9    58   554 4.02 4.06 2.54
##    3:  0.70 Ideal     E     SI1  62.5    57  2757 5.70 5.72 3.57
##    4:  0.59 Ideal     E    VVS2  62.0    55  2761 5.38 5.43 3.35
##    5:  0.74 Ideal     E     SI2  62.2    56  2761 5.80 5.84 3.62
##   ---                                                           
## 6733:  0.51 Ideal     D    VVS2  61.7    56  2742 5.16 5.14 3.18
## 6734:  0.51 Ideal     D    VVS2  61.3    57  2742 5.17 5.14 3.16
## 6735:  0.81 Ideal     D     SI1  61.5    57  2748 6.00 6.03 3.70
## 6736:  0.72 Ideal     D     SI1  60.8    57  2757 5.75 5.76 3.50
## 6737:  0.75 Ideal     D     SI2  62.2    55  2757 5.83 5.87 3.64

data.table 집계

data.table의 내장된 집계 기능은 상당히 빠르다.

#aggregate group by 하는 경우
aggregate(price ~ cut, diamonds, mean)
##         cut    price
## 1      Fair 4358.758
## 2      Good 3928.864
## 3 Very Good 3981.760
## 4   Premium 4584.258
## 5     Ideal 3457.542
# data.table을 사용하여 같은 결과 내는 방법
diamondsDT[, mean(price), by = cut]
##          cut       V1
## 1:      Fair 4358.758
## 2:      Good 3928.864
## 3: Very Good 3981.760
## 4:   Premium 4584.258
## 5:     Ideal 3457.542
diamondsDT[, list(price = mean(price)), by = cut]
##          cut    price
## 1:      Fair 4358.758
## 2:      Good 3928.864
## 3: Very Good 3981.760
## 4:   Premium 4584.258
## 5:     Ideal 3457.542
# 여러 열에 대해 집계하기 위해 list() 로 해당 열을 지정
diamondsDT[, mean(price), by = list(cut, color)]
##           cut color       V1
##  1:      Fair     D 4291.061
##  2:      Fair     E 3682.312
##  3:      Fair     F 3827.003
##  4:      Fair     G 4239.255
##  5:      Fair     H 5135.683
##  6:      Fair     I 4685.446
##  7:      Fair     J 4975.655
##  8:      Good     D 3405.382
##  9:      Good     E 3423.644
## 10:      Good     F 3495.750
## 11:      Good     G 4123.482
## 12:      Good     H 4276.255
## 13:      Good     I 5078.533
## 14:      Good     J 4574.173
## 15: Very Good     D 3470.467
## 16: Very Good     E 3214.652
## 17: Very Good     F 3778.820
## 18: Very Good     G 3872.754
## 19: Very Good     H 4535.390
## 20: Very Good     I 5255.880
## 21: Very Good     J 5103.513
## 22:   Premium     D 3631.293
## 23:   Premium     E 3538.914
## 24:   Premium     F 4324.890
## 25:   Premium     G 4500.742
## 26:   Premium     H 5216.707
## 27:   Premium     I 5946.181
## 28:   Premium     J 6294.592
## 29:     Ideal     D 2629.095
## 30:     Ideal     E 2597.550
## 31:     Ideal     F 3374.939
## 32:     Ideal     G 3720.706
## 33:     Ideal     H 3889.335
## 34:     Ideal     I 4451.970
## 35:     Ideal     J 4918.186
##           cut color       V1
#aggregate 와 다르게 각 열에 대해 다른 메트릭이 계산될 수 있다.
diamondsDT[, list(price = mean(price), carat = mean(carat)), by = cut]
##          cut    price     carat
## 1:      Fair 4358.758 1.0461366
## 2:      Good 3928.864 0.8491847
## 3: Very Good 3981.760 0.8063814
## 4:   Premium 4584.258 0.8919549
## 5:     Ideal 3457.542 0.7028370
diamondsDT[, list(price = mean(price), carat = mean(carat), caratsum = sum(carat)), by = cut]
##          cut    price     carat caratsum
## 1:      Fair 4358.758 1.0461366  1684.28
## 2:      Good 3928.864 0.8491847  4166.10
## 3: Very Good 3981.760 0.8063814  9742.70
## 4:   Premium 4584.258 0.8919549 12300.95
## 5:     Ideal 3457.542 0.7028370 15146.84
#다수의 메트릭에 대한 계산과 다수의 변수에 대한 그룹화는 동시에 가능
diamondsDT[, list(price = mean(price), carat = mean(carat)), by = list(cut, color)]
##           cut color    price     carat
##  1:      Fair     D 4291.061 0.9201227
##  2:      Fair     E 3682.312 0.8566071
##  3:      Fair     F 3827.003 0.9047115
##  4:      Fair     G 4239.255 1.0238217
##  5:      Fair     H 5135.683 1.2191749
##  6:      Fair     I 4685.446 1.1980571
##  7:      Fair     J 4975.655 1.3411765
##  8:      Good     D 3405.382 0.7445166
##  9:      Good     E 3423.644 0.7451340
## 10:      Good     F 3495.750 0.7759296
## 11:      Good     G 4123.482 0.8508955
## 12:      Good     H 4276.255 0.9147293
## 13:      Good     I 5078.533 1.0572222
## 14:      Good     J 4574.173 1.0995440
## 15: Very Good     D 3470.467 0.6964243
## 16: Very Good     E 3214.652 0.6763167
## 17: Very Good     F 3778.820 0.7409612
## 18: Very Good     G 3872.754 0.7667986
## 19: Very Good     H 4535.390 0.9159485
## 20: Very Good     I 5255.880 1.0469518
## 21: Very Good     J 5103.513 1.1332153
## 22:   Premium     D 3631.293 0.7215471
## 23:   Premium     E 3538.914 0.7177450
## 24:   Premium     F 4324.890 0.8270356
## 25:   Premium     G 4500.742 0.8414877
## 26:   Premium     H 5216.707 1.0164492
## 27:   Premium     I 5946.181 1.1449370
## 28:   Premium     J 6294.592 1.2930941
## 29:     Ideal     D 2629.095 0.5657657
## 30:     Ideal     E 2597.550 0.5784012
## 31:     Ideal     F 3374.939 0.6558285
## 32:     Ideal     G 3720.706 0.7007146
## 33:     Ideal     H 3889.335 0.7995249
## 34:     Ideal     I 4451.970 0.9130291
## 35:     Ideal     J 4918.186 1.0635938
##           cut color    price     carat

Share this: