YOGYUI

R NA 처리하기 (with dplyr) 본문

Software/R

R NA 처리하기 (with dplyr)

요겨 2021. 6. 13. 16:47
반응형

R에서는 데이터셋의 NA를 대체(Imputation)하는 여러가지 방법을 제공한다

(ex: DMwR 패키지의 centralImputation)

 

본 포스트에서는 dplyr 패키지를 활용해서 데이터셋에 NA가 있는지 파악하고, NA값들을 대체하는 방법에 대해 알아보도록 한다

 

1. 데이터 준비

mtcars 데이터셋의 일부 속성을 임의로 NA로 만들어보자

set.seed(1234)
# load dataset
df <- mtcars
# sample index
idx1 <- sample(1:nrow(df), 4)
idx2 <- sample(1:nrow(df), 6)
# replace 'drat' attribute to NA
df$drat[idx1] <- NA
# replace 'qsec' attribute to NA
df$qsec[idx2] <- NA
idx1
> [1] 28 16 26 22

idx2
> [1]  5 12 15  9 32  6

summary(df)
>      mpg             cyl             disp             hp             drat             wt             qsec             vs               am              gear            carb      
 Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0   Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000   Min.   :0.0000   Min.   :3.000   Min.   :1.000  
 1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5   1st Qu.:3.132   1st Qu.:2.581   1st Qu.:16.74   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
 Median :19.20   Median :6.000   Median :196.3   Median :123.0   Median :3.695   Median :3.325   Median :17.51   Median :0.0000   Median :0.0000   Median :4.000   Median :2.000  
 Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7   Mean   :3.624   Mean   :3.217   Mean   :17.58   Mean   :0.4375   Mean   :0.4062   Mean   :3.688   Mean   :2.812  
 3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0   3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.83   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
 Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0   Max.   :4.930   Max.   :5.424   Max.   :20.01   Max.   :1.0000   Max.   :1.0000   Max.   :5.000   Max.   :8.000  
                                                                 NA's   :4                       NA's   :6   

(16, 22, 26, 28)번째 레코드의 drat 속성값을 NA로, (5, 6, 9, 12, 15, 32)번째 레코드의 qsec 속성값을 NA로 대체했다

 

2. NA 속성 찾기

위에서 본것처럼 summary 함수를 활용하면 NA가 존재하는지 쉽게 파악할 수 있다

dplyr 패키지를 사용해서 NA 속성을 찾아보자

library(dplyr)
df_na <- df %>% 
    select_if(function(x) any(is.na(x)))

select_if 구문과 is.na 함수를 활용해 NA값이 존재하는 속성만 추출할 수 있다

(any 함수를 통해 레코드 1개라도 NA값이 존재하면 해당 속성이 선택됨)

summary(df_na)
>      drat            qsec      
 Min.   :2.760   Min.   :14.50  
 1st Qu.:3.132   1st Qu.:16.74  
 Median :3.695   Median :17.51  
 Mean   :3.624   Mean   :17.58  
 3rd Qu.:3.920   3rd Qu.:18.83  
 Max.   :4.930   Max.   :20.01  
 NA's   :4       NA's   :6  
 
names(df_na)
> [1] "drat" "qsec"

complete.cases 함수를 활용한 다음 구문과 동일한 동작
(complete.cases: NA인 속성이 모두 없는 레코드에 대해서만 TRUE 반환)

df_na <- df[!complete.cases(df),]

 

dplyr summarise_each 구문을 활용해 각 속성별 NA의 개수를 구할 수 있다

df_na %>% 
    summarise_each(funs(sum(is.na(.))))
> 
  drat qsec
1    4    6

※ summarise_each와 funs 구문은 deprecated되었다는 경고메시지가 뜬다 (0.7, 0.8 버전 이후)

다음과 같이 최신 문법(lambda 활용)으로 구현해도 결과는 동일하다

df_na %>% 
    summarise_all(list(~ sum(is.na(.))))

 

3. NA값 대체하기

(1) 0으로 대체하기

df %>% 
    select_if(function(x) any(is.na(x))) %>% 
    replace(is.na(.), 0)
>                   drat  qsec
Mazda RX4           3.90 16.46
Mazda RX4 Wag       3.90 17.02
Datsun 710          3.85 18.61
Hornet 4 Drive      3.08 19.44
Hornet Sportabout   3.15  0.00
Valiant             2.76  0.00
Duster 360          3.21 15.84
Merc 240D           3.69 20.00
Merc 230            3.92  0.00
Merc 280            3.92 18.30
Merc 280C           3.92 18.90
Merc 450SE          3.07  0.00
Merc 450SL          3.07 17.60
Merc 450SLC         3.07 18.00
Cadillac Fleetwood  2.93  0.00
Lincoln Continental 0.00 17.82
Chrysler Imperial   3.23 17.42
Fiat 128            4.08 19.47
Honda Civic         4.93 18.52
Toyota Corolla      4.22 19.90
Toyota Corona       3.70 20.01
Dodge Challenger    0.00 16.87
AMC Javelin         3.15 17.30
Camaro Z28          3.73 15.41
Pontiac Firebird    3.08 17.05
Fiat X1-9           0.00 18.90
Porsche 914-2       4.43 16.70
Lotus Europa        0.00 16.90
Ford Pantera L      4.22 14.50
Ferrari Dino        3.62 15.50
Maserati Bora       3.54 14.60
Volvo 142E          4.11  0.00

select_if문은 굳이 없어도 무방하다

다음 구문과 동일한 동작을 한다

df$drat[is.na(df$drat)] <- 0
df$qsec[is.na(df$qsec)] <- 0

 

(2) 평균값으로 대체하기

mutatereplace 구문 활용

평균 구하기: mean(x, na.rm = TRUE)

df %>% 
    select_if(function(x) any(is.na(x))) %>% 
    mutate(drat = replace(drat, is.na(drat), mean(drat, na.rm = TRUE))) %>% 
    mutate(qsec = replace(qsec, is.na(qsec), mean(qsec, na.rm = TRUE)))
>      drat     qsec
1  3.900000 16.46000
2  3.900000 17.02000
3  3.850000 18.61000
4  3.080000 19.44000
5  3.150000 17.57846
6  2.760000 17.57846
7  3.210000 15.84000
8  3.690000 20.00000
9  3.920000 17.57846
10 3.920000 18.30000
11 3.920000 18.90000
12 3.070000 17.57846
13 3.070000 17.60000
14 3.070000 18.00000
15 2.930000 17.57846
16 3.624286 17.82000
17 3.230000 17.42000
18 4.080000 19.47000
19 4.930000 18.52000
20 4.220000 19.90000
21 3.700000 20.01000
22 3.624286 16.87000
23 3.150000 17.30000
24 3.730000 15.41000
25 3.080000 17.05000
26 3.624286 18.90000
27 4.430000 16.70000
28 3.624286 16.90000
29 4.220000 14.50000
30 3.620000 15.50000
31 3.540000 14.60000
32 4.110000 17.57846

위와 같이 열 이름을 하나하나 붙여가는 것도 나쁘지 않은 방법인데, dplyr 고수라면 속성도 한번에 묶어서 함수처리하면 될듯하다 (난 잘 모르겠다 ㅎㅎ)

 

다음 구문과 동일 동작

df$drat[is.na(df$drat)] <- mean(df$drat, na.rm = TRUE)
df$qsec[is.na(df$qsec)] <- mean(df$qsec, na.rm = TRUE)

 

(3) 중앙값으로 대체하기

평균값 대체하기 구문에서 mean 대신 median 사용하면 된다

df %>% 
    select_if(function(x) any(is.na(x))) %>% 
    mutate(drat = replace(drat, is.na(drat), median(drat, na.rm = TRUE))) %>% 
    mutate(qsec = replace(qsec, is.na(qsec), median(qsec, na.rm = TRUE)))
>   drat  qsec
1  3.900 16.46
2  3.900 17.02
3  3.850 18.61
4  3.080 19.44
5  3.150 17.51
6  2.760 17.51
7  3.210 15.84
8  3.690 20.00
9  3.920 17.51
10 3.920 18.30
11 3.920 18.90
12 3.070 17.51
13 3.070 17.60
14 3.070 18.00
15 2.930 17.51
16 3.695 17.82
17 3.230 17.42
18 4.080 19.47
19 4.930 18.52
20 4.220 19.90
21 3.700 20.01
22 3.695 16.87
23 3.150 17.30
24 3.730 15.41
25 3.080 17.05
26 3.695 18.90
27 4.430 16.70
28 3.695 16.90
29 4.220 14.50
30 3.620 15.50
31 3.540 14.60
32 4.110 17.51

 

 

반응형
Comments