지난번 포스팅에서는 R data.table에서 .SD[]와 by를 사용해서 그룹별로 부분집합 가져오기 (Group Subsetting) 하는 방법을 소개하였습니다. (rfriend.tistory.com/611)

이번 포스팅에서는 R data.table에서 .SD[which.max()], .SD[which.min()]과 by 를 사용해서 그룹별로 최소값 행, 최대값 행을 indexing해서 가져오는 방법(Group Optima)을 소개하겠습니다.

 

(1) 그룹별로 특정 칼럼의 최대값인 행 가져오기 (get the minumum row for each group)

(2) 그룹별로 특정 칼럼의 최소값인 행 가져오기 (get the maximum row for each group)

 

 

먼저, data.table 패키지를 불러오고, 예제로 사용할 데이터로 Lahman 패키지에 들어있는 야구 팀들의 통계 데이터인 'Teams' 데이터셋을 Data.Table로 참조해서 불러오겠습니다.

 

library(data.table)

## Lahman database on baseball
#install.packages("Lahman")
library(Lahman)
data("Teams")

## coerce lists and data.frame to data.table by reference
setDT(Teams)

str(Teams)
# Classes 'data.table' and 'data.frame':	2925 obs. of  48 variables:
#   $ yearID        : int  1871 1871 1871 1871 1871 1871 1871 1871 1871 1872 ...
# $ lgID          : Factor w/ 7 levels "AA","AL","FL",..: 4 4 4 4 4 4 4 4 4 4 ...
# $ teamID        : Factor w/ 149 levels "ALT","ANA","ARI",..: 24 31 39 56 90 97 111 136 142 8 ...
# $ franchID      : Factor w/ 120 levels "ALT","ANA","ARI",..: 13 36 25 56 70 85 91 109 77 9 ...
# $ divID         : chr  NA NA NA NA ...
# $ Rank          : int  3 2 8 7 5 1 9 6 4 2 ...
# $ G             : int  31 28 29 19 33 28 25 29 32 58 ...
# $ Ghome         : int  NA NA NA NA NA NA NA NA NA NA ...
# $ W             : int  20 19 10 7 16 21 4 13 15 35 ...
# $ L             : int  10 9 19 12 17 7 21 15 15 19 ...
# $ DivWin        : chr  NA NA NA NA ...
# $ WCWin         : chr  NA NA NA NA ...
# $ LgWin         : chr  "N" "N" "N" "N" ...
# $ WSWin         : chr  NA NA NA NA ...
# $ R             : int  401 302 249 137 302 376 231 351 310 617 ...
# $ AB            : int  1372 1196 1186 746 1404 1281 1036 1248 1353 2571 ...
# $ H             : int  426 323 328 178 403 410 274 384 375 753 ...
# $ X2B           : int  70 52 35 19 43 66 44 51 54 106 ...
# $ X3B           : int  37 21 40 8 21 27 25 34 26 31 ...
# $ HR            : int  3 10 7 2 1 9 3 6 6 14 ...
# $ BB            : int  60 60 26 33 33 46 38 49 48 29 ...
# $ SO            : int  19 22 25 9 15 23 30 19 13 28 ...
# $ SB            : int  73 69 18 16 46 56 53 62 48 53 ...
# $ CS            : int  16 21 8 4 15 12 10 24 13 18 ...
# $ HBP           : int  NA NA NA NA NA NA NA NA NA NA ...
# $ SF            : int  NA NA NA NA NA NA NA NA NA NA ...
# $ RA            : int  303 241 341 243 313 266 287 362 303 434 ...
# $ ER            : int  109 77 116 97 121 137 108 153 137 166 ...
# $ ERA           : num  3.55 2.76 4.11 5.17 3.72 4.95 4.3 5.51 4.37 2.9 ...
# $ CG            : int  22 25 23 19 32 27 23 28 32 48 ...
# $ SHO           : int  1 0 0 1 1 0 1 0 0 1 ...
# $ SV            : int  3 1 0 0 0 0 0 0 0 1 ...
# $ IPouts        : int  828 753 762 507 879 747 678 750 846 1548 ...
# $ HA            : int  367 308 346 261 373 329 315 431 371 573 ...
# $ HRA           : int  2 6 13 5 7 3 3 4 4 3 ...
# $ BBA           : int  42 28 53 21 42 53 34 75 45 63 ...
# $ SOA           : int  23 22 34 17 22 16 16 12 13 77 ...
# $ E             : int  243 229 234 163 235 194 220 198 218 432 ...
# $ DP            : int  24 16 15 8 14 13 14 22 20 22 ...
# $ FP            : num  0.834 0.829 0.818 0.803 0.84 0.845 0.821 0.845 0.85 0.83 ...
# $ name          : chr  "Boston Red Stockings" "Chicago White Stockings" "Cleveland Forest Citys" "Fort Wayne Kekiongas" ...
# $ park          : chr  "South End Grounds I" "Union Base-Ball Grounds" "National Association Grounds" "Hamilton Field" ...
# $ attendance    : int  NA NA NA NA NA NA NA NA NA NA ...
# $ BPF           : int  103 104 96 101 90 102 97 101 94 106 ...
# $ PPF           : int  98 102 100 107 88 98 99 100 98 102 ...
# $ teamIDBR      : chr  "BOS" "CHI" "CLE" "KEK" ...
# $ teamIDlahman45: chr  "BS1" "CH1" "CL1" "FW1" ...
# $ teamIDretro   : chr  "BS1" "CH1" "CL1" "FW1" ...
# - attr(*, ".internal.selfref")=<externalptr>

 

 

 

 

(1) 그룹별로 특정 칼럼의 최대값인 행 가져오기

    (get the minumum row for each group)

 

팀 ID 그룹 별로 (by = teamID) 승리 회수가 최대인 행(which.max(W))을 indexing 해서 가져오기 (.SD[which.max(W)] 해보겠습니다.  .SD 는 data.table 그 자체를 참조하는데요, 여기에 .SD[which.max(W)]로 W 가 최대인 index 의 위치의 행 전체를 subset 해오는 것입니다.  indexing  해오는 위치가 특정 숫자로 고정된 것이 아니라 which.max() 로 최대값의 위치를 동적으로 (dynamic indexing) 가져오게 할 수 있습니다.

 

## (1) Get the best year for each team, as measured by 'W'(Win)
Teams[ , .SD[which.max(W)]
       , .SDcols = c('teamID', 'yearID', 'lgID', 'franchID', 'divID', 'Rank', 'W') 
       , by = teamID]
#    teamID teamID yearID lgID franchID divID Rank   W
# 1:    BS1    BS1   1875   NA      BNA  <NA>    1  71
# 2:    CH1    CH1   1871   NA      CNA  <NA>    2  19
# 3:    CL1    CL1   1871   NA      CFC  <NA>    8  10
# 4:    FW1    FW1   1871   NA      KEK  <NA>    7   7
# 5:    NY2    NY2   1872   NA      NNA  <NA>    3  34
# ---                                                  
# 145:    ANA    ANA   2000   AL      ANA     W    3  82
# 146:    ARI    ARI   1999   NL      ARI     W    1 100
# 147:    MIL    MIL   1999   NL      MIL     C    5  74
# 148:    TBA    TBA   2009   AL      TBD     E    3  84
# 149:    MIA    MIA   2017   NL      FLA     E    2  77

 

 

 

(2) 그룹별로 특정 칼럼의 최소값인 행 가져오기

    (get the maximum row for each group)

 

팀 ID 그룹별로(by = teamID) 승리 회수가 최소인 년도의 행 전체를 가져오려면 .SD[which.min(W)] 로 dynamic indexing 을 해서 그룹별 부분집합을 가져오면 됩니다.

.SDcols 는 원하는 특정 칼럼들만 선별적으로 가져올 때 사용합니다.

 

## (2) Get the worst year for each team, as measured by 'W'(Win)
Teams[ , .SD[which.min(W)]
       , .SDcols = c('teamID', 'yearID', 'lgID', 'franchID', 'divID', 'Rank', 'W') 
       , by = teamID]
#    teamID teamID yearID lgID franchID divID Rank  W
# 1:    BS1    BS1   1871   NA      BNA  <NA>    3 20
# 2:    CH1    CH1   1871   NA      CNA  <NA>    2 19
# 3:    CL1    CL1   1872   NA      CFC  <NA>    7  6
# 4:    FW1    FW1   1871   NA      KEK  <NA>    7  7
# 5:    NY2    NY2   1871   NA      NNA  <NA>    5 16
# ---                                                 
# 145:    ANA    ANA   1999   AL      ANA     W    4 70
# 146:    ARI    ARI   2004   NL      ARI     W    5 51
# 147:    MIL    MIL   2002   NL      MIL     C    6 56
# 148:    TBA    TBA   2002   AL      TBD     E    5 55
# 149:    MIA    MIA   2019   NL      FLA     E    5 57

 

 

참고로, .SD[which(조건, condition)] 을 해서 특정 조건을 만족하는 행을 동적으로 인덱싱 (dynamic indexing with conditions) 해서 부분집합을 가져올 수 도 있습니다. 

아래 예에서는 야구팀 그룹(by = teamID)별로 승리 회수가 100회 이상 (.SD[which(W >= 100)] 인 년도의 행들의 부분집합을 가져온 것입니다.

 

## Get the year over 100 Wins for each team
Teams[ , .SD[which(W >= 100)]
       , .SDcols = c('teamID', 'yearID', 'lgID', 'franchID', 'divID', 'Rank', 'W') 
       , by = teamID]
       
# ## or equivalently
# Teams[W >= 100 , .SD
#        , .SDcols = c('teamID', 'yearID', 'lgID', 'franchID', 'divID', 'Rank', 'W') 
#        , by = teamID]

#    teamID teamID yearID lgID franchID divID Rank   W
# 1:    BSN    BSN   1892   NL      ATL  <NA>    1 102
# 2:    BSN    BSN   1898   NL      ATL  <NA>    1 102
# 3:    CHN    CHN   1906   NL      CHC  <NA>    1 116
# 4:    CHN    CHN   1907   NL      CHC  <NA>    1 107
# 5:    CHN    CHN   1909   NL      CHC  <NA>    2 104
# ---                                                  
# 105:    OAK    OAK   2001   AL      OAK     W    2 102
# 106:    OAK    OAK   2002   AL      OAK     W    1 103
# 107:    KCA    KCA   1977   AL      KCR     W    1 102
# 108:    SEA    SEA   2001   AL      SEA     W    1 116
# 109:    ARI    ARI   1999   NL      ARI     W    1 100

 

 

[ Reference ]

* R data.table vignettes 'Using .SD for Data Analysis'
  : cran.r-project.org/web/packages/data.table/vignettes/datatable-sd-usage.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다.

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,