본문 바로가기

데이터 전처리/R

수치형 데이터 전처리 [in R]

https://pastryofjsmath.tistory.com/27

 

파이썬 범주화,결측치 처리기법

A = { 'subject' : ["미적분학","선형대수학","해석학","정수론","수리통계학","수학을 위한 프로그래밍"], '평점' : [3.8,4.5,4.1,3.2,4.7,5], '학점' : [3,3,3,2,2,2], } df=pd.DataFrame(A) #결측치 처리. #1.결측치 행을 제

pastryofjsmath.tistory.com

위의 수치형 데이터전처리의 R버전입니다.

아래 csv파일을 이용했습니다.

df_merged_mean.csv
0.00MB

 

 

df <- read.csv('',fileEncoding = 'CP949')
library(magrittr)
library(dplyr)
df<-df[-1]

df[-1]은 행의 인덱스를 제거하기 위함입니다.

 

df %>% str()
summary(df)

데이터의 구조와 수치형이면 어떻게 분포되어있는지 확인해줍니다.

#총 NA개수
df %>% is.na() %>% sum()
>3

#각 col마다 결측치가 얼마나 있는지 확인
colSums(is.na(df))
>     subject         평점         학점     선수과목       강사진     수강금액     수강인원       좌석수 강의시작시간 
           0            0            0            0            0            1            1            1            0 
       개강일       종강일 
           0            0

총 NA개수와 col마다 결측치가 얼마나 있는지 확인하는 방법입니다.

 

아래는 결측치를 아예 제거하는 방법입니다.

두가지 모두 사용가능합니다.

##NA를 제거한 컬을 모두 보고 싶을 때
#sol1)
na.omit(df)

#sol2)
df[complete.cases(df),]

 

만약 어디에서 결측치가 발생했는지 보고싶다면?

df[apply(df,MARGIN = 1,FUN = function(x) any(is.na(x))),]

좀 더 쉽게 쓸 수 있을 것같은데 막상 떠오르지가 않네요;; filter로는 특정 컬럼만 보통 추출하니까요.

만약 filter를 사용한다 해도 결과는 같습니다. 

 df %>% filter(apply(df,MARGIN = 1,FUN = function(x) any(is.na(x))))

apply계열 함수 중 apply를 사용해서 행으로 FUN을 이용한 계산을 해줍니다. 

any(is.na(x))에서 any는 하나라도 있으면 True를 반환해줍니다.

그러니까 any(is.na(x))는 x라는 것에서 결측치가 적어도 하나라도 있다면  True를 반환하는 것이죠.

결측치 발생한 행 추출 (결과는 같다.)

다음은 수치형 컬럼과 그 외의 컬럼으로 분리하는 과정입니다.

 

#수치형과 그 외로 분리하는 과정
num_col <-df %>% select_if(is.numeric) %>% colnames() 
char_col <-df %>% select_if(function(col) is.character(col) || is.factor(col)) %>% colnames()
#?select_if
#select_if를 사용하긴 위해선 dplyr패키지가 필요합니다.

select_if 를 사용했습니다. char_col에서 함수를 걸어주는 이유는 주석처리한 ?select_if를 실행하시면,

 

함수자체로 사용해야 하기 때문이죠.

 

다음은 수치형 결측치 대체방법입니다. 하나의 컬럼씩 하는 방법은 간단합니다.

df$수강인원 %>% mean(,na.rm = T)
ifelse(is.na(df$수강인원),mean(df$수강인원,na.rm = T),df$수강인원)

na.rm=T를 해줌으로 인해 NA값은 제외해서 평균으로 나타냅니다. 이를 이용해 ifelse문을 사용해줍니다.

하지만 모든 컬럼에 대해서 해줄려면? 모든 컬럼마다 ifelse을 사용해야할까요?

apply계열 함수를 사용하면 쉽게 가능합니다.

#수치형 컬럼의 결측값을 평균으로 대체
apply(df[num_col],MARGIN = 2,FUN = function(x) ifelse(is.na(x), mean(x, na.rm = TRUE), x))

모든 컬럼에 대해 수치형데이터 결측치 처리

 

++20240609 추가사항

R에서도 파이썬의 str.contains()과 비슷한 기능이 있습니다.

library(stringr)

#강사진 브레젤의 '브레'만으로 추출해내보자.
df$강사진[str_detect(df$강사진,'브레')]

> df$강사진[str_detect(df$강사진,'브레')]
[1] "브레젤"

#또는 기본 함수 grep()을 이용
grep(pattern='브레',x=df$강사진)
#출력이 순서로 나오므로, 
df$강사진[grep(pattern='브레',x=df$강사진)]



> grep(pattern = '브레',x = df$강사진)
[1] 6
> df$강사진[grep(pattern = '브레',x = df$강사진)]
[1] "브레젤"

 

추가적으로 선수과목을 분리하고 싶다면, 다음과 같이 사용하시면 됩니다.

#일정패턴으로 분리되어있음.
str_split(string = df$선수과목,pattern = '/')

>str_split(string = df$선수과목,pattern = '/')
[[1]]
[1] "없음"

[[2]]
[1] "미적분학"

[[3]]
[1] "선형대수학" "미적분학"  

[[4]]
[1] "선형대수학" "미적분학"  

[[5]]
[1] "선형대수학" "미적분학"  

[[6]]
[1] "선형대수학" "미적분학"   "수리통계학"

#이를 데이터 프레임으로 변환하면? 
str_df <- str_split(string = df$선수과목,pattern = '/') %>% unlist() %>% table() %>% as.data.frame()
> str_df
           . Freq
1   미적분학    5
2 선형대수학    4
3 수리통계학    1
4       없음    1