3.2 결측치(Missing Values) 처리
3.2.1 결측값이 포함되어 있는지 확인하는 방법: is.na()
3.2.1.1 데이터 세트 1
<- c(1, 2, 3, 4, NA, 6, 7, 8, 9, NA) x
3.2.1.2 is.na()
함수 사용
is.na(x)
## [1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE
- 벡터
x
의 각 요소에 대해NA
여부를 확인. 논리형 벡터 반환.
3.2.1.3 데이터 세트 2
library(MASS)
##
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
##
## select
str(Cars93)
## 'data.frame': 93 obs. of 27 variables:
## $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
## $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
## $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
## $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
## $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
## $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
## $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ...
## $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ...
## $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
## $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
## $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
## $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
## $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ...
## $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
## $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
## $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
## $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
## $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ...
## $ Length : int 177 195 180 193 186 189 200 216 198 206 ...
## $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ...
## $ Width : int 68 71 67 70 69 69 74 78 73 73 ...
## $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ...
## $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
## $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ...
## $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
## $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
## $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
MASS
패키지에 있는Cars93
데이터 세트- 27개의 컬럼과 93개의 행으로 구성되어 있다.
is.na()
함수는 데이터 구조의 각 요소별로 NA 여부를 확인한다.
위의 벡터처럼 구성요소 갯수가 몇 개 안될 경우 is.na()
한 후에 TRUE
, FALSE
논리형 값을 눈으로 보고 확인할 수 있다.
3.2.1.4 is.na()
함수 사용
하지만 Cars93
데이터 세트처럼 변수(열) 갯수도 많고, 관측치(행) 갯수도 많은 경우 (대부분의 실무에서 쓰는 데이터 세트는 이처럼 변수도 많고 관측치도 많다.)
is.na()
함수만 가지고서는 아무래도 결측치 현황을 파악하는데 무리가 있다.
head(is.na(Cars93))
## Manufacturer Model Type Min.Price Price Max.Price MPG.city MPG.highway
## 1 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 2 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 3 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 4 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 5 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 6 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## AirBags DriveTrain Cylinders EngineSize Horsepower RPM Rev.per.mile
## 1 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 2 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 3 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 4 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 5 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## 6 FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## Man.trans.avail Fuel.tank.capacity Passengers Length Wheelbase Width
## 1 FALSE FALSE FALSE FALSE FALSE FALSE
## 2 FALSE FALSE FALSE FALSE FALSE FALSE
## 3 FALSE FALSE FALSE FALSE FALSE FALSE
## 4 FALSE FALSE FALSE FALSE FALSE FALSE
## 5 FALSE FALSE FALSE FALSE FALSE FALSE
## 6 FALSE FALSE FALSE FALSE FALSE FALSE
## Turn.circle Rear.seat.room Luggage.room Weight Origin Make
## 1 FALSE FALSE FALSE FALSE FALSE FALSE
## 2 FALSE FALSE FALSE FALSE FALSE FALSE
## 3 FALSE FALSE FALSE FALSE FALSE FALSE
## 4 FALSE FALSE FALSE FALSE FALSE FALSE
## 5 FALSE FALSE FALSE FALSE FALSE FALSE
## 6 FALSE FALSE FALSE FALSE FALSE FALSE
3.2.2 결측치 갯수: sum(is.na())
3.2.2.1 벡터의 경우
sum(is.na(x))
## [1] 2
3.2.2.2 데이터 프레임의 경우 : Cars93
데이터 프레임.
sum(is.na(Cars93))
## [1] 13
3.2.2.3 Cars93
의 각 변수(컬럼)별로 결측값 개수
총 27개의 변수 중에서 Manufacturer
, Price
, Rear.seat.room
, Luggage.room
등의 4개만 예시로 살펴본다.
sum(is.na(Cars93$Manufacturer))
## [1] 0
sum(is.na(Cars93$Price))
## [1] 0
sum(is.na(Cars93$Rear.seat.room))
## [1] 2
sum(is.na(Cars93$Luggage.room))
## [1] 11
- 각 컬럼 별로
NA
요소 갯수가 반환된다.
sum(is.na())
함수를 이용하면 변수가 많거나 관측값이 많은 경우도 결측값 현황을 금방 파악할 수 있다.
R은 TRUE
를 ‘1
’로, FALSE
를’0
‘으로 인식하기 때문에 sum(is.na())
를 하게 되면 TRUE
값을’1
’로 인식해서 합계를 구한다.
3.2.2.4 colSums()
함수 사용
colSums()
함수를 사용하면 데이터 프레임 내 다수 변수들에 대해서 한번에 각 개별 변수별 결측값의 개수 합계를 구할 수 있다.
바로 위에서 개별 함수별로 일일이 sum(is.na(Cars93$Manufacturer))
…이런 식으로 변수의 개수만큼 쓰는 것을 colSums()
함수로는 한줄이면 해결할 수 있으니 훨씬 편하다.
colSums(is.na(Cars93))
## Manufacturer Model Type Min.Price
## 0 0 0 0
## Price Max.Price MPG.city MPG.highway
## 0 0 0 0
## AirBags DriveTrain Cylinders EngineSize
## 0 0 0 0
## Horsepower RPM Rev.per.mile Man.trans.avail
## 0 0 0 0
## Fuel.tank.capacity Passengers Length Wheelbase
## 0 0 0 0
## Width Turn.circle Rear.seat.room Luggage.room
## 0 0 2 11
## Weight Origin Make
## 0 0 0
colSums(is.na(Cars93))
: 함수로 간단하게 각 컬럼별 요소에 있는 NA의 갯수를 계산하였다.
3.2.3 결측치를 통계 분석시 제외시키기 : na.rm = TRUE
다음과 같은 함수는 데이터 세트에 NA가 포함되어 있으면 그 결과도 NA가 된다.
sum(x)
## [1] NA
mean(x)
## [1] NA
- 벡터
x
에 결측치(NA
)가 포함되어 있으면 그 결과는NA
로 반환됨.
이런 경우에 na.rm = TRUE
를 함수의 인수로 사용한다.
sum(x, na.rm = TRUE)
## [1] 40
mean(x, na.rm = TRUE)
## [1] 5
- 데이터 세트에서
NA
를 제외하고 그 결과를 반환한다.
결측값이 들어있는 벡터에 대해서 sum()
, mean()
, sd()
, min()
, max()
, range()
등의 통계 함수를 적용하면 NA
만 나오게 된다.
결측값을 제외하고 통계 함수 계산을 하는 옵션은 na.rm = TRUE
가 된다.
NA를 고려하지 않은 통계 처리의 경우
sum(Cars93$Luggage.room)
## [1] NA
mean(Cars93$Luggage.room)
## [1] NA
- 모두 NA 결과
NA를 제외한 통계처리의 경우
sum(Cars93$Luggage.room, na.rm = TRUE)
## [1] 1139
mean(Cars93$Luggage.room, na.rm = TRUE)
## [1] 13.89024
- NA를 제외한 통계 처리 결과
위 예제는 결측값이 포함된 데이터 프레임의 특정 변수에 대해 indexing을 해서 통계 함수를 적용해본 경우이다.
역시 na.rm = TRUE
옵션을 설정해 주어야 통계 계산이 제대로 됨을 알 수 있다.
na.rm = FALSE
가 디폴트이므로 na.rm = TRUE
를 설정하지 않는 경우 결측값이 포함되어 있으면 NA
가 결과로 나타나게 된다.
3.2.4 결측치가 있는 행 제거: na.omit()
na.rm = TRUE
옵션은 원래의 데이터 세트는 그대로 둔 채, 통계량 계산할 때만 NA
를 포함하지 않게 된다.
따라서 다수의 통계 함수 혹은 다수의 변수에 통계 함수를 사용해야 하는 경우 매번 na.rm = TRUE
옵션을 설정해주는게 번거로울 수 있다.
따라서 차라리 원래 데이터 세트에서 결측값을 제거하면 된다.
결측값이 들어있는 행을 제거해 주는 함수가 na.omit()
함수이다.
좀 더 예리하게 특정 행과 열을 지정해서 그곳에 결측값이 있는 경우만 메스로 정밀 수술하는 함수가 complete.cases()
함수이다.
3.2.4.1 na.omit()
함수의 사용 예
<- na.omit(Cars93)
Cars93_1 str(Cars93_1)
## 'data.frame': 82 obs. of 27 variables:
## $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
## $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
## $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
## $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
## $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
## $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
## $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ...
## $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ...
## $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
## $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
## $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
## $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
## $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ...
## $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
## $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
## $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
## $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
## $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ...
## $ Length : int 177 195 180 193 186 189 200 216 198 206 ...
## $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ...
## $ Width : int 68 71 67 70 69 69 74 78 73 73 ...
## $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ...
## $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
## $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ...
## $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
## $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
## $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
## - attr(*, "na.action")= 'omit' Named int [1:11] 16 17 19 26 36 56 57 66 70 87 ...
## ..- attr(*, "names")= chr [1:11] "16" "17" "19" "26" ...
Cars93_1 <- na.omit(Cars93)
:NA
를 가진 행을 제외한 새로운 데이터 세트 Cars93_1을 생성한다.str(Cars93_1)
: 원래의 데이터 세트Cars93
에 있던93
개의 행이82
개로 줄어 들었다. 즉, 11개의 행을 삭제했다.
위의 예처럼 결측값이 들어있는 행을 통째로 삭제할 때는 만약의 사태를 대비해서 원본 Cars93
은 그대로 유지하고, 행을 삭제한 데이터 셋을 별도의 이름 Cars93_1
로 저장해서 분석을 진행하는 것을 추천한다.
3.2.4.2 특정 열에 결측값 존재 시 행 제거하기 : complete.cases()
함수
sum(is.na(Cars93))
## [1] 13
- Cars93 데이터 세트에 있는
NA
요소의 갯수를 출력한다.
3.2.4.2.1 Cars93
데이터 프레임의 Rear.seat.room
컬럼의 결측치 행을 데이터 세트에서 제거하기
Cars93[ , “Rear.seat.room”] 또는 Cars93$Rear.seat.room 변수를 이용할 수 있다.
<- Cars93[ complete.cases(Cars93[ , c("Rear.seat.room")]), ]
Cars93_2 sum(is.na(Cars93_2))
## [1] 9
위의 구문은 다음과 같은 논리로 문제를 해결한다.
1. x <- Cars93$Rear.seat.room
: 해당 컬럼을 변수 x
로 대체
2. y <- complete.cases(x)
: x 벡터의 요소에 대해 NA 존재 여부 확인 -> logical 벡터가 나온다.
3. Cars93_2 <- Cars93[y, ]
: y
가 TRUE
행만을 Cars93_2로 추출한다.
<- Cars93$Rear.seat.room; head(x) x
## [1] 26.5 30.0 28.0 31.0 27.0 28.0
<- complete.cases(x); sum(!y) y
## [1] 2
<- is.na(x); sum(z) z
## [1] 2
<- Cars93[y, ]; str(Cars93_2); sum(is.na(Cars93_2)) Cars93_2
## 'data.frame': 91 obs. of 27 variables:
## $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
## $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
## $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
## $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
## $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
## $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
## $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ...
## $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ...
## $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
## $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
## $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
## $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
## $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ...
## $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
## $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
## $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
## $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
## $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ...
## $ Length : int 177 195 180 193 186 189 200 216 198 206 ...
## $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ...
## $ Width : int 68 71 67 70 69 69 74 78 73 73 ...
## $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ...
## $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
## $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ...
## $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
## $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
## $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
## [1] 9
compleste.cases(x)
: 요소가NA
가 아니면TRUE
,NA
면FALSE
반환. - 따라서,NA
인 요소의 갯수를 구하기 위해sum(!y)
를 사용. :x
컬럼의NA
갯수는2
개.is.na(x)
:complete.cases()
함수와 비교하기 위함.is.na()
함수는 요소가NA
이면TRUE
,NA
가 아니면FALSE
반환.- 따라서
sum(z)
로NA
인 요소의 갯수를 구한다. :x
컬럼의NA
갯수는2
개. Cars93[y, ]
:Cars93
데이터 세트 중에서 벡터y
가TRUE
인 행인 모든 열([y, ]
)을 추출한다.sum(is.na(Cars93_2))
: Cars93_2 데이터 세트에 NA 요소가 아직도 9개 남아 있음을 확인할 수 있다.
3.2.4.2.2 Cars93
데이터 프레임의 23~24
번째 칼럼 내 결측값이 있는 행 전체 삭제
이의 해결은 다음과 같은 논리 절차과정을 거치게 된다.
- Cars93의 23, 24번쨰 컬럼은 다음과 같이 표현된다. :
Cars93[ , 23:24]
- 이를
x
변수에 대입한다. :x <- Cars93[, 23:24]
- 이 x 변수에 대해 결측치가 없는 행을 확인하고 이를
y
변수에 대입한다. :y <- complete_cases(x)
- Cars93에서 결측치가 없는 행들을 Cars93_3에 대입한다. :
Cars93_3 <- Cars93[y, ]
- Cars93_3의 결측치 요소 갯수를 확인한다. :
sum(is.na(Cars93_3))
이 절차를 스크립트로 작성하면 다음과 같다.
<- Cars93[, 23:24]
x <- complete.cases(x)
y <- Cars93[y, ]
Cars93_3 sum(is.na(Cars93_3))
## [1] 0
Cars93_3
데이터 세트에는NA
요소가 하나도 없다.
이러한 논리를 한 줄에 표현한 것이 다음의 스크립트이다.
<- Cars93[ complete.cases(Cars93[ , c(23:24)]), ]
Cars93_3 sum(is.na(Cars93_3))
## [1] 0
행의 갯수 즉, 관측치의 갯수를 확인해 보자.
dim(Cars93_3)
## [1] 82 27
str(Cars93_3)
## 'data.frame': 82 obs. of 27 variables:
## $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
## $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
## $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
## $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
## $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
## $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
## $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ...
## $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ...
## $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
## $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
## $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
## $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
## $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ...
## $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
## $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
## $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
## $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
## $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ...
## $ Length : int 177 195 180 193 186 189 200 216 198 206 ...
## $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ...
## $ Width : int 68 71 67 70 69 69 74 78 73 73 ...
## $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ...
## $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
## $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ...
## $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
## $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
## $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
- 관측값이 82개로서 전체 93개 중에서 11개가 줄어들었음을 알 수 있다.
3.2.5 결측치를 다른 값으로 대체
3.2.5.1 기본 형식
결측치를 다른 값으로 대체하는 기본적인 형식은 다음과 같다.
dataset$var[is.na(dataset$var)] <- new_value
이 구문의 논리적 흐름은 다음과 같다.
1. dataset$var
: dataset
데이터 세트의 var
컬럼 (x
)
2. is.na(x)
: x
에 NA가 있는가 (y
)
3. dataset$var[y] <- new_value
: dataset
의 var
컬럼 중 y
가 TRUE
인 경우 new_valu
e를 대입.
3.2.5.2 결측치 대체 예
Cars93
데이터 세트에서 Luggage.room
컬럼에 있는 결측치를 모두 0
으로 변경해 본다.
3.2.5.2.1 결측치 요소 확인
Cars93$Luggage.room
변수에 대해 결측치 요소 확인
$Luggage.room Cars93
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 NA NA 20 NA 15 14 17 11 13 14
## [26] NA 16 11 11 15 12 12 13 12 18 NA 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 NA NA 12 15 6 15 11 14 12 14 NA 14 14 16 NA 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 NA 10 NA 14 15 14 15
sum(is.na(Cars93$Luggage.room))
## [1] 11
3.2.5.2.2 결측치를 0
으로 대체하기
원래의 데이터 세트 Cars93을 Cars93_4로 복사한 다음, Cars93_4$Luggage.room의 결측치를 0으로 대체한다.
이 구문의 논리적 흐름은 다음과 같다.
1. Cars93_4 <- Cars93
: Cars93
을 Cars93_4
에 복사한다.
2. Cars93_4$Luggage.room
: Cars93_4
데이터 세트의 var
컬럼 (x
)
3. is.na(x)
: x
에 NA가 있는가 (y
)
4. Cars93_4$var[y] <- 0
: Cars93_4
의 Luggage.room
컬럼 중 y
가 TRUE
인 경우 new_value
0`를 대입.
이를 스크립트로 표현하면 다음과 같다.
<- Cars93
Cars93_4 <- Cars93_4$Luggage.room; x # 수정 전 Luggage.room 컬럼 x
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 NA NA 20 NA 15 14 17 11 13 14
## [26] NA 16 11 11 15 12 12 13 12 18 NA 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 NA NA 12 15 6 15 11 14 12 14 NA 14 14 16 NA 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 NA 10 NA 14 15 14 15
<- is.na(Cars93_4$Luggage.room)
y $Luggage.room[y] <- 0
Cars93_4$Luggage.room # 수정 후 Luggage.room 컬럼 Cars93_4
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 0 0 20 0 15 14 17 11 13 14
## [26] 0 16 11 11 15 12 12 13 12 18 0 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 0 0 12 15 6 15 11 14 12 14 0 14 14 16 0 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 0 10 0 14 15 14 15
- NA값들이 모두 0으로 대체되었음을 알 수 있다.
위의 스크립트를 간단하게 표현하면 다음과 같다. 여기서는 Cars93을 Cars93_5로 복사하여 처리한다.
# Luggage.room 변수 내 결측값을 '0'으로 대체
<- Cars93
Cars93_5 $Luggage.room # 수정 전 Luggage.room 컬럼 Cars93_5
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 NA NA 20 NA 15 14 17 11 13 14
## [26] NA 16 11 11 15 12 12 13 12 18 NA 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 NA NA 12 15 6 15 11 14 12 14 NA 14 14 16 NA 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 NA 10 NA 14 15 14 15
$Luggage.room[is.na(Cars93_5$Luggage.room)] <- 0
Cars93_5$Luggage.room # 수정 후 Luggage.room 컬럼 Cars93_5
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 0 0 20 0 15 14 17 11 13 14
## [26] 0 16 11 11 15 12 12 13 12 18 0 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 0 0 12 15 6 15 11 14 12 14 0 14 14 16 0 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 0 10 0 14 15 14 15
Cars93_5$Luggage.room[is.na(Cars93_5$Luggage.room)] <- 0
과 같은 스크립트에 익숙해질 필요가 있는 것이다.
3.2.5.2.3 결측치를 컬럼의 평균값
으로 대체하기
sum(is.na(Cars93$Luggage.room))
함수를 사용해 Cars93
의 Luggage.room
변수에 보면 11개의 결측값이 있음을 알 수 있다.
Luggage.room
변수 내 결측값을 indexing 기법을 활용해서 ’0
’로 대체하는 방법을 살펴보았다.
물론 ’0
’이 아니라 다른 값으로도 대체가 가능하다.
아래의 예제에서는 결측값을 미포함(`na.rm = TRUE
)했을 때의 Luggage.room
의 평균값으로 결측값을 대체하여 보자,
<- Cars93
Cars93_6 $Luggage.room # 수정 전 Luggage.room Cars93_6
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 NA NA 20 NA 15 14 17 11 13 14
## [26] NA 16 11 11 15 12 12 13 12 18 NA 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 NA NA 12 15 6 15 11 14 12 14 NA 14 14 16 NA 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 NA 10 NA 14 15 14 15
$Luggage.room[is.na(Cars93_6$Luggage.room)] <- mean(Cars93_6$Luggage.room, na.rm = TRUE)
Cars93_6sum(is.na(Cars93_6$Luggage.room))
## [1] 0
round(Cars93_6$Luggage.room) # 수정 후 Luggage.room
## [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 14 14 20 14 15 14 17 11 13 14
## [26] 14 16 11 11 15 12 12 13 12 18 14 18 21 10 11 8 12 14 11 12 9 14 15 14 9
## [51] 19 22 16 13 14 14 14 12 15 6 15 11 14 12 14 14 14 14 16 14 17 8 17 13 13
## [76] 16 18 14 12 10 15 14 10 11 13 15 14 10 14 14 15 14 15
0
값 대신에Luggage.room
컬럼의 평균인mean(Cars93_6$Luggage.room, na.rm = TRUE)
로 대체하고 있음을 알 수 있다.
3.2.5.2.4 일괄 대체
데이터프레임의 모든 행의 결측값을 특정 값(가령, ‘0
’)으로 일괄 대체 : dataset[is.na(dataset)] <- 0
<- Cars93
Cars93_7 # counting the number of missing values in Cars93 dataset
sum(is.na(Cars93_7)) # 13
## [1] 13
# converting the missing value in Cars93 dataframe to '0'
is.na(Cars93_7)] <- 0
Cars93_7[
# counting the number of missing values in Cars93 dataset
sum(is.na(Cars93_7)) # 0
## [1] 0
3.2.5.2.5 데이터프레임의 각 변수의 결측값을 각 변수 별 평균값으로 일괄 대체
위의 3)번에서 결측값을 그 열의 평균으로 대체하는 방법을 알아보았다.
만약 결측값을 포함한 열이 매우 많다면 일일이 변수이름을 지정해가면서 나열해서 입력하는 것은 번거로운 일이다.
이럴 때 sapply()
함수를 사용하면 일괄로 결측값을 포함한 변수에 대해서는 해당 변수의 평균으로 대체하라고 프로그래밍할 수 있다.
sapply()
를 적용하면 matrix를 반환하므로 dataframe으로 만들기 위해서 앞에 data.frame()
을 추가로 붙여주었다.
sapply(dataset, function(x) ifelse(is.na(x), mean(x, na.rm=TRUE), x))
이러한 문제의 해결을 위한 스크립트는 다음과 같다.
# converting the missing value in Cars93 dataframe to each column's mean value
<- Cars93[1:20,c("Rear.seat.room", "Luggage.room")]
Cars93_8 colSums(is.na(Cars93_8))
## Rear.seat.room Luggage.room
## 1 3
head(Cars93_8)
## Rear.seat.room Luggage.room
## 1 26.5 11
## 2 30.0 15
## 3 28.0 14
## 4 31.0 17
## 5 27.0 13
## 6 28.0 16
sapply(Cars93_8, function(x) mean(x, na.rm=T))
## Rear.seat.room Luggage.room
## 29.10526 15.35294
<- data.frame(sapply(Cars93_8,
Cars93_8 function(x) ifelse(is.na(x),
mean(x, na.rm = TRUE),
x)
)
)head(Cars93_8)
## Rear.seat.room Luggage.room
## 1 26.5 11
## 2 30.0 15
## 3 28.0 14
## 4 31.0 17
## 5 27.0 13
## 6 28.0 16
3.2.5.2.6 그룹별 평균값으로 결측값 대체하기
base
패키지를 사용할 수도 있다.
그런데 여기서는 dplyr
패키지의 group_by()
와 ifelse()
조건문을 사용한 mutate()
함수로 그룹 별 평균값으로 결측값 채우는 방법에 대하여 알아보자.
3.2.5.2.6.1 데이터 세트 생성
# make a sample DataFrame
<- c(rep('a', 5), rep('b', 5))
grp <- c(1, 2, 3, NaN, 6, 2, 4, NaN, 10, 8)
val <- data.frame(grp, val)
df df
## grp val
## 1 a 1
## 2 a 2
## 3 a 3
## 4 a NaN
## 5 a 6
## 6 b 2
## 7 b 4
## 8 b NaN
## 9 b 10
## 10 b 8
3.2.5.2.6.2 그룹별 평균 구하기
결측값을 제외하고, 그룹 ‘a’와 그룹 ’b’ 별 평균을 계산해보자.
# mean value by group 'a' and 'b'
library(dplyr)
%>% group_by(grp) %>% summarise(grp_mean = mean(val, na.rm = TRUE)) df
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 2 x 2
## grp grp_mean
## <chr> <dbl>
## 1 a 3
## 2 b 6
그룹 ‘a
’의 평균은 3, 그룹’b
’의 평균은 6이다.
3.2.5.2.6.3 결측치를 그룹별 평균으로 대체하기
그러면 4번째 행에 있는 그룹 ‘a
’의’val
’ 칼럼 결측값을 그룹 ‘a
’의 평균 3으로 대체(replace) 또는 채워넣기(fill in)를 하면 다음과 같다.
그리고 8번째 행에 있는 그룹’b
‘의’val
’ 칼럼 결측값을 그룹 ’b
’의 평균 6으로 대체하면 다음과 같다.
%>%
df group_by(grp) %>%
mutate(val = ifelse(is.na(val), mean(val, na.rm=TRUE), val))
## # A tibble: 10 x 2
## # Groups: grp [2]
## grp val
## <chr> <dbl>
## 1 a 1
## 2 a 2
## 3 a 3
## # ... with 7 more rows
위에서 NA
(Not Available)에 대해서 소개를 했는데요, 참고로 벡터에서 NaN
(Not a Number), 무한값 Inf
(Infinity), 유한값 finite
확인하는 방법도 마저 소개하겠습니다.
유의할 것은, is.na()
에서 NA
뿐만 아니라 NaN
도 TRUE
를 반환합니다.
<- c(-1, 0, 10, NA, NaN, Inf)
my_data my_data
## [1] -1 0 10 NA NaN Inf
is.finite(my_data)
## [1] TRUE TRUE TRUE FALSE FALSE FALSE
is.na(my_data)
## [1] FALSE FALSE FALSE TRUE TRUE FALSE
is.nan(my_data)
## [1] FALSE FALSE FALSE FALSE TRUE FALSE
is.infinite(my_data)
## [1] FALSE FALSE FALSE FALSE FALSE TRUE
Reference
- https://rfriend.tistory.com/34 [R, Python 분석과 프로그래밍의 친구 (by R Friend)]