R data.table 패키지를 사용하다 보면 base R이나 tydeverse 에서는 사용하지 않는, data.table에서만 사용하는 .SD, .SDcols, .N 등의 매개변수를 볼 수 있는데요, 이게 data.table 패키지를 굉장히 이질적이고 코드가 이해가 되지 않는 어려운 프로그래밍 언어라는 첫인상을 주는 것 같습니다.  사실 이 첫번째 관문만 무사히 넘으면 data.table 의 강력함과 간결함에 매료될만도 한데 말이지요.

 

이번 포스팅에서는 data.table의 vignettes을 참조하여 R의 data.table 패키지에서

 

(1) .SD는 무엇인가? (What is .SD in data.table?)

(2) .SDcols 로 일부 칼럼만 가져오기 (Column Subsetting using .SDcols)

(3) lapply()와 .SDcols로 칼럼 유형 변환하기 (Column Type Conversion)

(4) 패턴이 일치하는 특정 칼럼만 가져오기 (Column subsetting using pattern-based matching)

 

에 대해서 소개하겠습니다.

 

 

(1) .SD는 무엇인가? (What is .SD in data.table package?)

 

data.table 패키지에서 .SD 는 "데이터의 부분집합, 데이터 자기 자신, 데이터의 자기 참조" ( 'Subset, Selfsame, or Self-reference of the Data')를 의미한다고 이해할 수 있습니다.  다시 말하자면, .SD는 data.table 자기 자신에 대한 재귀적인 참조 (a reflexive reference to the data.table), data.table 그 자체 (data.table itself) 를 의미합니다.

 

 

R의 Lahman database에 있는 야구 팀과 투수의 통계 데이터를 사용해서 예를 들어보겠습니다.  setDT(Pitching) 은 Lists와 DataFrame을 참조해서 Data.Table로 만들어줍니다.

 

먼저, 아래의 예는 .SD 를 사용해서 'Pitching' data.table 자체를 재귀적으로 참조해오는 예입니다. (복사가 아니라 참조임. not copy, but referece to the Pitching data.table)

 

library(data.table)

## .SD stands for Subset, Selfsame, or Self-reference of the Data.
## .SD is a reflexive reference to the data.table itself. 
## .SD is helpful for chaining together "queries" (extractions/subsets/etc using [). 


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

data("Pitching")
setDT(Pitching)
str(Pitching)
# Classes 'data.table' and 'data.frame':	47628 obs. of  30 variables:
#   $ playerID: chr  "bechtge01" "brainas01" "fergubo01" "fishech01" ...
# $ yearID  : int  1871 1871 1871 1871 1871 1871 1871 1871 1871 1871 ...
# $ stint   : int  1 1 1 1 1 1 1 1 1 1 ...
# $ teamID  : Factor w/ 149 levels "ALT","ANA","ARI",..: 97 142 90 111 90 136 111 56 97 136 ...
# $ lgID    : Factor w/ 7 levels "AA","AL","FL",..: 4 4 4 4 4 4 4 4 4 4 ...
# $ W       : int  1 12 0 4 0 0 0 6 18 12 ...
# $ L       : int  2 15 0 16 1 0 1 11 5 15 ...
# $ G       : int  3 30 1 24 1 1 3 19 25 29 ...
# $ GS      : int  3 30 0 24 1 0 1 19 25 29 ...
# $ CG      : int  2 30 0 22 1 0 1 19 25 28 ...
# $ SHO     : int  0 0 0 1 0 0 0 1 0 0 ...
# $ SV      : int  0 0 0 0 0 0 0 0 0 0 ...
# $ IPouts  : int  78 792 3 639 27 3 39 507 666 747 ...
# $ H       : int  43 361 8 295 20 1 20 261 285 430 ...
# $ ER      : int  23 132 3 103 10 0 5 97 113 153 ...
# $ HR      : int  0 4 0 3 0 0 0 5 3 4 ...
# $ BB      : int  11 37 0 31 3 0 3 21 40 75 ...
# $ SO      : int  1 13 0 15 0 0 1 17 15 12 ...
# $ BAOpp   : num  NA NA NA NA NA NA NA NA NA NA ...
# $ ERA     : num  7.96 4.5 27 4.35 10 0 3.46 5.17 4.58 5.53 ...
# $ IBB     : int  NA NA NA NA NA NA NA NA NA NA ...
# $ WP      : int  7 7 2 20 0 0 1 15 3 44 ...
# $ HBP     : int  NA NA NA NA NA NA NA NA NA NA ...
# $ BK      : int  0 0 0 0 0 0 0 2 0 0 ...
# $ BFP     : int  146 1291 14 1080 57 3 70 876 1059 1334 ...
# $ GF      : int  0 0 0 1 0 1 1 0 0 0 ...
# $ R       : int  42 292 9 257 21 0 30 243 223 362 ...
# $ SH      : int  NA NA NA NA NA NA NA NA NA NA ...
# $ SF      : int  NA NA NA NA NA NA NA NA NA NA ...
# $ GIDP    : int  NA NA NA NA NA NA NA NA NA NA ...
# - attr(*, ".internal.selfref")=<externalptr>



## .SD on Ungrouped Data
## In terms of subsetting, .SD is a subset of the data, the set itself. 

Pitching[, .SD]
# playerID yearID stint teamID lgID  W  L  G GS CG SHO SV IPouts   H  ER HR BB SO BAOpp
# 1: bechtge01   1871     1    PH1   NA  1  2  3  3  2   0  0     78  43  23  0 11  1    NA
# 2: brainas01   1871     1    WS3   NA 12 15 30 30 30   0  0    792 361 132  4 37 13    NA
# 3: fergubo01   1871     1    NY2   NA  0  0  1  0  0   0  0      3   8   3  0  0  0    NA
# 4: fishech01   1871     1    RC1   NA  4 16 24 24 22   1  0    639 295 103  3 31 15    NA
# 5: fleetfr01   1871     1    NY2   NA  0  1  1  1  1   0  0     27  20  10  0  3  0    NA
# ---                                                                                       
#   47624: zamorda01   2019     1    NYN   NL  0  1 17  0  0   0  0     26  10   5  1  5  8 0.294
# 47625: zeuchtj01   2019     1    TOR   AL  1  2  5  3  0   0  0     68  22  12  2 11 20 0.250
# 47626: zimmejo02   2019     1    DET   AL  1 13 23 23  0   0  0    336 145  86 19 25 82 0.311
# 47627: zimmeky01   2019     1    KCA   AL  0  1 15  0  0   0  0     55  28  22  2 19 18 0.337
# 47628: zobribe01   2019     1    CHN   NL  0  0  1  0  0   0  0      3   0   0  0  2  1 0.000
# ERA IBB WP HBP BK  BFP GF   R SH SF GIDP
# 1:  7.96  NA  7  NA  0  146  0  42 NA NA   NA
# 2:  4.50  NA  7  NA  0 1291  0 292 NA NA   NA
# 3: 27.00  NA  2  NA  0   14  0   9 NA NA   NA
# 4:  4.35  NA 20  NA  0 1080  1 257 NA NA   NA
# 5: 10.00  NA  0  NA  0   57  0  21 NA NA   NA
# ---                                           
#   47624:  5.19   1  0   1  0   41  3   5  0  1    0
# 47625:  4.76   0  2   0  0   99  0  13  0  0    1
# 47626:  6.91   2  3   6  0  504  0  89  3  4    5
# 47627: 10.80   0  2   0  0  102  3  22  0  0    1
# 47628:  0.00   0  0   0  0    5  1   0  0  0    0

 

 

Pitching[, .SD] 는 단순히 Pitching data.table 자체를 그대로 반환하는데요, identical() 로 두 data.table이 동일한지 여부를 확인해보면 TRUE 입니다.

 

identical(Pitching, Pitching[ , .SD])
# [1] TRUE

 

 

(2) .SDcols 로 일부 칼럼만 가져오기 (Column Subsetting using .SDcols)

 

.SD 를 사용하는 가장 일반적인 예는 .SDcols와 함께 일부 칼럼의 부분집합을 선택해서 가져오는 것입니다. 

 

아래 예에서는 Pitching[, .SD] 로 Pitching data.table 자체를 재귀적으로 참조한 후에 --> Pitching[, .SD, .SDcols = c("playerID", "W", "L", "G")] 처럼 .SDcols 에 칼럼 이름을 넣어줘서 일부 칼럼만 선택적으로 가져와 보겠습니다.

 

library(data.table)

library(Lahman)
data("Pitching")

## Coerce lists and Data.Frames to Data.Table by Reference
setDT(Pitching)


## ** Column Subsetting: .SDcols **
Pitching[, .SD, .SDcols = c("playerID", "W", "L", "G")]

# playerID  W  L  G
# 1: bechtge01  1  2  3
# 2: brainas01 12 15 30
# 3: fergubo01  0  0  1
# 4: fishech01  4 16 24
# 5: fleetfr01  0  1  1
# ---                   
#   47624: zamorda01  0  1 17
# 47625: zeuchtj01  1  2  5
# 47626: zimmejo02  1 13 23
# 47627: zimmeky01  0  1 15
# 47628: zobribe01  0  0  1



 

(3) 칼럼 유형 변환하기 (Column Type Conversion)

 

이번에는 lapply()와 .SDcols를 사용해서 data.table에서 여러개 칼럼의 유형을 한꺼번에 변환해보겠습니다.

 

먼저, setDT(Teams) 로 'Teams' DataFrame을 참조하여 Data.Table로 만들고, c('teamIDBR', 'teamIDlahman45', 'teamIDretro') 의 3개 칼럼이 '문자형 인지 여부(is.character)'를 확인해보겠습니다. 3개 칼럼 모두 is.character()가 TRUE 이므로 문자형 맞군요.

 

library(data.table)

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

## Coerce lists and Data.Frames 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> 


## check whether columns are character type or not
col_lists = c('teamIDBR', 'teamIDlahman45', 'teamIDretro')
Teams[, sapply(.SD, is.character), .SDcols = col_lists]
# teamIDBR teamIDlahman45    teamIDretro 
# TRUE           TRUE           TRUE


head(unique(Teams[[col_lists[1L]]]))
# [1] "BOS" "CHI" "CLE" "KEK" "NYU" "ATH"

 

 

이제 위의 3개 문자형 칼럼들을 요인형(factor type)으로 lapply()와 .SDcols 를 사용해서 한꺼번에 데이터 유형을 변환해보겠습니다.

 

이때 주의를 해야 할 것이 있는데요, 아래에 색깔을 칠해 놓은 것처럼, (col_lists) 처럼 괄호 () 로 싸주어야 col_lists 안의 3개 칼럼 이름을 인식해서 할당을 해줍니다. (괄호를 안쳐주면 'col_lists' 라는 이름으로 할당해버립니다.)

 

Teams[ , (col_lists) := lapply(.SD, factor), .SDcols = col_lists]

 

## Converting columns to factor by adding the := assignment operator.
## we must wrap fkt in parentheses () to force data.table to interprete this as column names.
Teams[ , (col_lists) := lapply(.SD, factor), .SDcols = col_lists]

 

col_lists의 3개 칼럼의 데이터 유형이 무엇인지 확인해보면, 요인형(factor)로 잘 변환이 되었네요.

sapply(Teams[, .SD, .SDcols = col_lists], class)
# teamIDBR teamIDlahman45    teamIDretro 
# "factor"       "factor"       "factor"



head(unique(Teams[[col_lists[1L]]]))
# [1] BOS CHI CLE KEK NYU ATH
# 101 Levels: ALT ANA ARI ATH ATL BAL BLA BLN BLU BOS BRA BRG BRO BSN BTT BUF BWW CAL CEN CHC ... WSN

 

 

(4) 패턴이 일치하는 특정 칼럼만 가져오기 (Column subsetting using pattern-based matching)

 

data.table의 .SDcols는 패턴 매칭을 지원합니다. 아래의 예에서는 'Teams' Data.Table에서 'team' 이라는 단어가 들어가 있는 칼럼 이름(.SDcols = patterns('team'))을 선별해서 가져와 보겠습니다.

 

names(Teams)

# [1] "yearID"         "lgID"           "teamID"         "franchID"       "divID"         
# [6] "Rank"           "G"              "Ghome"          "W"              "L"             
# [11] "DivWin"         "WCWin"          "LgWin"          "WSWin"          "R"             
# [16] "AB"             "H"              "X2B"            "X3B"            "HR"            
# [21] "BB"             "SO"             "SB"             "CS"             "HBP"           
# [26] "SF"             "RA"             "ER"             "ERA"            "CG"            
# [31] "SHO"            "SV"             "IPouts"         "HA"             "HRA"           
# [36] "BBA"            "SOA"            "E"              "DP"             "FP"            
# [41] "name"           "park"           "attendance"     "BPF"            "PPF"           
# [46] "teamIDBR"       "teamIDlahman45" "teamIDretro" 



## pattern-based matching of columns in .SDcols to select all columns 
## which contain team back to factor. 
Teams[ , .SD, .SDcols = patterns('team')]

# teamID teamIDBR teamIDlahman45 teamIDretro
# 1:    BS1      BOS            BS1         BS1
# 2:    CH1      CHI            CH1         CH1
# 3:    CL1      CLE            CL1         CL1
# 4:    FW1      KEK            FW1         FW1
# 5:    NY2      NYU            NY2         NY2
# ---                                           
#   2921:    SLN      STL            SLN         SLN
# 2922:    TBA      TBR            TBA         TBA
# 2923:    TEX      TEX            TEX         TEX
# 2924:    TOR      TOR            TOR         TOR
# 2925:    WAS      WSN            MON         WAS

 

아래의 예는 team_idx = grep('team', names(Teams), value=TRUE)로, 먼저 names(Teams)를 통해 얻은 전체 칼럼 이름들 중에서 'team'이 들어가 있는 칼럼 이름을 찾아서 team_idx 에 저장을 해주었습니다.  다음으로, 위의 (3)번에서 했던 lapply()와 .SD, .SDcols를 이용하여 칼럼 이름에 'team'이 들어간 모든 칼럼의 데이터 유형을 요인형(factor type)으로 일괄 변환해주었습니다.

 

team_idx = grep('team', names(Teams), value = TRUE)
team_idx

# [1] "teamID"         "teamIDBR"       "teamIDlahman45" "teamIDretro"



Teams[ , (team_idx) := lapply(.SD, factor), .SDcols = team_idx]
sapply(Teams[, .SD, .SDcols = team_idx], class)

# teamID       teamIDBR teamIDlahman45    teamIDretro 
# "factor"       "factor"       "factor"       "factor"

 

[ 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
,

지난번 포스팅에서는 R data.table의 DT[i, j, by] 에서 by 구문을 사용하여 "그룹별 집계" 하는 방법을 소개하였습니다. 


Base R만 쓰다가 몇 년 전에 처음으로 dplyr 의 chaining 형태의 구문을 봤을 때 '이게 R 맞나?' 갸우뚱 했었는데요, data.table 의 .N, .SD, .SDcols 등의 특수 부호가 포함된 data.table의 구문을 처음으로 봤을 때 역시나 '이게 R 맞나? 무슨 언어지?' 하고 의아해했었고 또 낯설기에 거부감도 들었습니다. 하지만 지난 포스팅에 이어 이번 포스팅까지 보고 나면 data.table에 대해 한결 친숙하게 느껴지실 거예요.  


이번 포스팅에서는 data.table 에서만 볼 수 있는 특수 부호로서 여러개의 칼럼을 처리할 때 사용하는 .SD, .SDcols  특수 부호의 기능과 사용법을 소개하겠습니다. 


(1) data.table .SD 특수 부호를 사용하여 여러개의 모든 칼럼 처리하기

(2) data.table .SDcols 특수 부호를 사용하여 특정 다수의 칼럼을 지정하여 처리하기

(3) data.table .SD, .SDcols 와 lapply 함수를 사용하여 여러개의 숫자형 변수를 표준화하기

     (How to standardize or transform the multiple numeric columns using .SD, .SDcols, lapply in R data.table?)


하는 방법을 소개하겠습니다. 




먼저, 예제로 사용할 1개의 문자형 변수와 3개의 숫자형 변수로 구성된 간단한 data.table 을 만들어보겠습니다. 



library(data.table)

library(dplyr)


grp <- c(rep('F', 5), rep('B', 3), rep('A', 2))

x1 <- c(1:10)

x2 <- seq(from = 11, to = 29, by = 2)

x3 <- seq(from = 30, to = 59, by = 3)

DT <- data.table(grp, x1, x2, x3)


> print(DT)

    grp x1 x2 x3

 1:   F  1 11 30

 2:   F  2 13 33

 3:   F  3 15 36

 4:   F  4 17 39

 5:   F  5 19 42

 6:   B  6 21 45

 7:   B  7 23 48

 8:   B  8 25 51

 9:   A  9 27 54

10:   A 10 29 57

 




  (1) data.table .SD 특수 부호를 사용하여 여러개의 모든 칼럼 처리하기


data.table 의 .SD 는 'Subset of Data' 의 첫머리 글자를 따서 만든 특수 부호로서, 그룹 칼럼(by grouping columns)을 제외한 모든 칼럼을 대상으로 연산을 수행할 때 사용합니다. 


아래 예에서는 'grp' 칼럼 ('F' 5행, 'B' 3행, 'A' 2행) 의 그룹을 기준으로 모든 변수(.SD)를 출력 (print(.SD))한 예입니다. 



# Special symbol .SD : Subset of Data

# It by itself is a data.table that holds the data for the current group defined using by.

# .SD contains all the columns except the grouping columns by default.

> DT[, print(.SD), by = grp]


   x1 x2 x3

1:  1 11 30

2:  2 13 33

3:  3 15 36

4:  4 17 39

5:  5 19 42


   x1 x2 x3

1:  6 21 45

2:  7 23 48

3:  8 25 51


   x1 x2 x3

1:  9 27 54

2: 10 29 57


Empty data.table (0 rows and 1 cols): grp

 



'grp' 그룹 별(by = grp)로 모든 변수(.SD)에 대해 요약통계량(summary statistics)을 계산하려면 R lapply(var_name, function) 함수를 적용해주면 됩니다. 


DT data.table 데이터셋에 대해 'grp' 그룹 별로 모든 변수에 대해 최소값(min), 최대값(max), 표준편차(standard deviation), 평균(mean) 을 계산하시오. 



> # To compute on (multiple) columns, 

> # we can then simply use the base R function lapply().

> DT[, lapply(.SD, min), by = grp]

   grp x1 x2 x3

1:   F  1 11 30

2:   B  6 21 45

3:   A  9 27 54

> DT[, lapply(.SD, max), by = grp]

   grp x1 x2 x3

1:   F  5 19 42

2:   B  8 25 51

3:   A 10 29 57

> DT[, lapply(.SD, sd), by = grp]

   grp        x1       x2       x3

1:   F 1.5811388 3.162278 4.743416

2:   B 1.0000000 2.000000 3.000000

3:   A 0.7071068 1.414214 2.121320

> DT[, lapply(.SD, mean), by = grp]

   grp  x1 x2   x3

1:   F 3.0 15 36.0

2:   B 7.0 23 48.0

3:   A 9.5 28 55.5

 



그룹별로 통계량을 계산하는 'grp' 변수를 기준으로 정렬하려면 아래의 두가지 방법을 사용합니다. 

  • DT[, lapply(.SD, mean), by = grp][order(grp)]
  • DT[, lapply(.SD, mean), keyby = grp]

> # ordering group column using 'keyby'

> DT[, lapply(.SD, mean), keyby = grp]

   grp  x1 x2   x3

1:   A 9.5 28 55.5

2:   B 7.0 23 48.0

3:   F 3.0 15 36.0




위와 동일한 결과를 얻기 위해서 dplyr 로는 아래처럼 대상이 되는 숫자형 변수를 summarise_at(vars(x1:x3), mean) 혹은 summarise_at(c("x1", "x2", "x3"), mean, na.rm = T) 처럼 변수 이름을 써줘야 합니다. 변수 개수가 많을 수록 일일이 써주기가 번거로울 수 있습니다. 반면 data.table 은 .SD 특수부호로 모든 변수를 가져올 수 있으니 편리합니다. 


> # by dplyr

library(dplyr)

> DT %>% 

+   group_by(grp) %>% 

+   summarise_at(vars(x1:x3), mean, na.rm = T)

# A tibble: 3 x 4

  grp      x1    x2    x3

  <chr> <dbl> <dbl> <dbl>

1 A       9.5    28  55.5

2 B       7      23  48  

3 F       3      15  36





  (2) data.table .SDcols 특수 부호를 사용하여 특정 다수의 칼럼을 지정하여 처리하기


data.table에서 다수의 특정 칼럼을 지정하고 싶으면 DT[i, j, by, .SDcols] 구문에서 by 다음에 .SDcols = c(var1, var2, ...) 처럼 .SDcols 매개변수를 사용합니다. 즉, .SDcols = c("x1", "x2") 로 "x1", "x2" 의 특정한 두 개 변수로만 지정을 하고, lapply(.SD, mean) 에서 .SD 로 .SDcols에서 특정한 칼럼 내에서의 모든 칼럼(즉, "x1", "x2")에 대해서 평균을 구하게 됩니다. 


DT data.table에서 ("A", "B") 그룹에 속하는 관측치에 한해, "x1", "x2" 칼럼에 대해서만 'grp' 그룹별(by = grp)로 각 변수별 평균(lapply(.SD, mean))을 구하시오.



# .SDcols

# to specify just the columns we want to compute the mean() on using .SDcols.

DT[grp %in% c("A", "B"),    # subset of ("A", "B") in grp

   lapply(.SD, mean),       # compute the mean for multiple columns except grp. col.

   by = grp,             # by 'grp' groups, orginal order

   .SDcols = c("x1", "x2")] # for just those specified in .SDcols


[Out]

   grp  x1 x2

1:   B 7.0 23

2:   A 9.5 28

 




  (3) data.table .SD, .SDcols 와 lapply 함수를 사용하여 여러개의 숫자형 변수를 표준화

      (How to standardize or transform the multiple numeric columns using .SD, .SDcols, lapply in R data.table?)


만약 여러개의 숫자형 변수 모두를 한꺼번에 z-표준화(z = (x - mean) / std_dev)를 하고 싶다면 for loop 반복문을 사용하는 대신에 R lapply(변수, 함수) 를 사용하면 쉽고 깔끔하게 코드를 짤 수 있습니다. 


DT data.table 의 숫자형 변수인 ("x1", "x2", "x3") 특정 변수(.SDcols)들 모두에(.SD) 대해서 한꺼번에 표준화(standardization)를 하시오.  


> # Standardization of all numeric columns

> DT_scaled <- DT[, lapply(.SD, scale), .SDcols = c("x1", "x2", "x3")]

> print(DT_scaled)

         x1.V1      x2.V1      x3.V1

 1: -1.4863011 -1.4863011 -1.4863011

 2: -1.1560120 -1.1560120 -1.1560120

 3: -0.8257228 -0.8257228 -0.8257228

 4: -0.4954337 -0.4954337 -0.4954337

 5: -0.1651446 -0.1651446 -0.1651446

 6:  0.1651446  0.1651446  0.1651446

 7:  0.4954337  0.4954337  0.4954337

 8:  0.8257228  0.8257228  0.8257228

 9:  1.1560120  1.1560120  1.1560120

10:  1.4863011  1.4863011  1.4863011




위의 실행 결과는 결과값의 변수 이름이 "x1.V1", "x2.V1", "x3.V1" 으로 나와서 마음에 안드네요. 이번에는  ':=' 특수 부호를 사용하여 새로운 변수 이름을 부여(set new column names)해 보겠습니다. data.table 만의 특수 부호인 '.SD', '.SDcols', ':=' 가 총 출동해서 정신이 없네요. 허허.. ^^;;;


DT data.table 의 숫자형 변수인 ("x1", "x2", "x3") 특정 변수(.SDcols)들 모두에(.SD) 대해서 한꺼번에 표준화(standardization)를 하여 DT data.table에 ("scaled_x1", "scaled_x2", "scaled_x3") 라는 새로운 이름의 변수를 추가하시오. 


> # Standardization of all numeric columns with new column names

> num_vars <- c('x1', 'x2', 'x3')

> new_num_vars <- paste0('scaled_', num_vars)

> # In data.table, the := operator and all the set* (e.g., setkey, setorder, setnames etc..) functions 

> # are the only ones which modify the input object by reference

> DT_scaled_2 <- DT[, (new_num_vars) := lapply(.SD, scale), .SDcols = num_vars]

> print(DT_scaled_2)

    grp x1 x2 x3  scaled_x1  scaled_x2  scaled_x3

 1:   F  1 11 30 -1.4863011 -1.4863011 -1.4863011

 2:   F  2 13 33 -1.1560120 -1.1560120 -1.1560120

 3:   F  3 15 36 -0.8257228 -0.8257228 -0.8257228

 4:   F  4 17 39 -0.4954337 -0.4954337 -0.4954337

 5:   F  5 19 42 -0.1651446 -0.1651446 -0.1651446

 6:   B  6 21 45  0.1651446  0.1651446  0.1651446

 7:   B  7 23 48  0.4954337  0.4954337  0.4954337

 8:   B  8 25 51  0.8257228  0.8257228  0.8257228

 9:   A  9 27 54  1.1560120  1.1560120  1.1560120

10:   A 10 29 57  1.4863011  1.4863011  1.4863011




물론, Base R에서 사용하는 apply(dataset, 2 for col, function) 의 구문으로 각 칼럼별 평균과 표준편차를 구해서 별도의 객체로 저장을 해놓고, t(apply(dataset, 1 for row, function(x) {transformation_function} 의 형태로 데이터를 변환할 수도 있습니다. 위의 예제 코드보다 조금 복잡하기는 하지만 function(x){custom_function} 부분에 custom function 도 넣을 수 있고 해서 유연하게 쓸 수 있으므로 알아두면 좋겠습니다. 아래에서 t() 는 전치(transpose) 하라는 뜻입니다. 



> # -- standardization using apply() and function()

> #-- mean by columns

> num_vars_mean <- apply(DT[, .(x1, x2, x3)], 2, mean)

> print(num_vars_mean)

  x1   x2   x3 

 5.5 20.0 43.5 

> # sd by columns

> num_vars_sd <- apply(DT[, .(x1, x2, x3)], 2, sd)

> print(num_vars_sd)

      x1       x2       x3 

3.027650 6.055301 9.082951 

> t(apply(DT[, 2:4], 1, function(x){

+   (x - num_vars_mean) / num_vars_sd})

+ )

              x1         x2         x3

 [1,] -1.4863011 -1.4863011 -1.4863011

 [2,] -1.1560120 -1.1560120 -1.1560120

 [3,] -0.8257228 -0.8257228 -0.8257228

 [4,] -0.4954337 -0.4954337 -0.4954337

 [5,] -0.1651446 -0.1651446 -0.1651446

 [6,]  0.1651446  0.1651446  0.1651446

 [7,]  0.4954337  0.4954337  0.4954337

 [8,]  0.8257228  0.8257228  0.8257228

 [9,]  1.1560120  1.1560120  1.1560120

[10,]  1.4863011  1.4863011  1.4863011




아래 예제는 여러개의 모든 숫자형 변수의 최대값과 최소값을 계산하여 객체로 저장해놓고, apply() 함수로 [0-1] 변환을 해보았습니다. 



> # [0-1] transformation using apply() and function()

> # max and min for each columns

> num_vars_max <- apply(DT[, .(x1, x2, x3)], 2, max)

> num_vars_min <- apply(DT[, .(x1, x2, x3)], 2, min)

> # [0-1] transformation

> t(apply(DT[, 2:4], 1, function(x){

+   (x - num_vars_min) / (num_vars_max - num_vars_min)})

+ )

             x1        x2        x3

 [1,] 0.0000000 0.0000000 0.0000000

 [2,] 0.1111111 0.1111111 0.1111111

 [3,] 0.2222222 0.2222222 0.2222222

 [4,] 0.3333333 0.3333333 0.3333333

 [5,] 0.4444444 0.4444444 0.4444444

 [6,] 0.5555556 0.5555556 0.5555556

 [7,] 0.6666667 0.6666667 0.6666667

 [8,] 0.7777778 0.7777778 0.7777778

 [9,] 0.8888889 0.8888889 0.8888889

[10,] 1.0000000 1.0000000 1.0000000




[Reference]

https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,