본문 바로가기
IT & 데이터 사이언스/Python

[Python Data Analytics] Python을 활용한 데이터 전처리(1)

by 바른 호랑이 2023. 6. 19.
728x90
반응형

안녕하세요. 바른 호랑이입니다.

이번 게시글에서는 Python을 활용한 데이터 전처리 방법에 대해 알아볼 예정입니다.

데이터 분석이라고 하면 많은 사람들이 AI, 머신러닝, 딥러닝과 같은 부분들을 먼저 떠올리지만 데이터 분석의 대부분의 업무는 그와 같은 모델들보다는 로우 데이터들을 얼마나 잘 가공하는가에 집중되어 있다고 해도 과언이 아니라고할 수 있습니다. 이와 같은 데이터 전처리를 지원하기 위해 Python에서는 Pandas, sklearn과 같은 다양한 패키지들이 작성되어있어 참고하기 쉽고, 이를 통해 데이터 전처리과정을 보다 원활하게 진행할 수 있습니다. 데이터 전처리라고 지칭되기는 하나 그 범위가 상당히 넓기 때문에 이번에는 가장 기본적인 부분인 누락, 중복 데이터 처리, 데이터 표준화 등과 같은 부분들에 대해서 알아볼 예정입니다. 기본적으로 개발환경은 Colab을 활용하여 구성하였으니 해당 사항은 참고하시기 바랍니다.

※ 실습파일

Python_DataAnalysis_06(Data_Preprocessing).ipynb
0.04MB

# 필요 패키지 설치
!pip install numpy
!pip install pandas

# 필요 패키지 로드
import numpy as np
import pandas as pd
import seaborn as sns

# seaborn 데이터 셋 종류 확인
sns.get_dataset_names()

# 사용할 데이터 셋 로드
df = sns.load_dataset('titanic')

# 데이터 셋 구조 확인
df.info()

# 특정열의 누락 데이터 개수 확인
print(df['age'].value_counts(dropna=False))

# isnull 메소드로 실제 누락데이터 확인 : isnull() 메소드는 값이 NaN이면 True를 반환
print('\n', df.isnull())

# notnull 메소드로 누락 데이터 확인 : not nul() 메소드는 값이 NaN이면 False를 반환
print('\n', df.notnull())

# isnull() 메소드와 sum함수를 사용하여 각 열의 누락값 개수 확인 : sum의 axis가 0이면 참의 합을 출력해줌
print(df.isnull().sum(axis=0))

# for반복문과 value_counts를 활용하여 각 열의 누락값 개수 확인
print('\n')
for column in df.columns:
  df_values = df[column].value_counts(dropna=False)
  try:
    print(f'{column} : {df_values[np.nan]}')
  except:
    print(f'{column} : 0')
    
# 행기준 누락데이터 제거, axis옵션 0 : 행기준 제거 / 1 : 열기준 제거 - Default값은 0
df_dropna_01 = df.dropna(axis=0)
# NaN의 개수가 500개 이상인 열만 제거하기 위해 axis, thresh 옵션 추가
df_dropna_02 = df.dropna(axis=1, thresh=500) 
# 행 기준 삭제시 특정 열의 데이터 값이 NaN인 값만 제거하도록 적용 / subset과 how를 사용
df_dropna_03 = df.dropna(axis=0, subset=['age'], how='any')

print(
      f'--------------------------------------------------------------------------------\
      \n{df.columns}\n{len(df)}\
      \n--------------------------------------------------------------------------------\
      \n\n{df_dropna_01.columns}\n{len(df_dropna_01)}\
      \n--------------------------------------------------------------------------------\
      \n{df_dropna_02.columns}\n{len(df_dropna_02)}\
      \n--------------------------------------------------------------------------------\
      \n{df_dropna_03.columns}\n{len(df_dropna_03)}\
      \n--------------------------------------------------------------------------------'
     )
     
 # 누락 데이터 치환 
# replace 함수로 치환하기 - 주의사항 : replace 함수는 categorical 자료 유형에는 적용이 안됨(int, float, string 가능)
df_chna_00 = df.copy()
df_chna_00.replace(np.nan, 0, inplace=True)
print(f'기존 값 : {df.isnull().sum()}\n\n변경 값 : {df_chna_00.isnull().sum()}\n')

# 평균으로 치환하기
df_chna_01 = df.copy()
mean_age = df['age'].mean(axis=0)
df_chna_01['age'].fillna(mean_age, inplace=True)
print(f'기존 값 : {df["age"].isnull().sum()} / 변경 값 : {df_chna_01["age"].isnull().sum()}')

# 빈도수가 가장 높은 값으로 치환
df_chna_02 = df.copy()
most_freq = df['embark_town'].value_counts(dropna=True).idxmax(axis=0)
df_chna_02['embark_town'].fillna(most_freq, inplace=True) 
print(f'기존 값 : {df["embark_town"].isnull().sum()} / 변경 값 : {df_chna_02["embark_town"].isnull().sum()}')

# 서로 이웃하고 있는 값으로 바꾸기
df_chna_03 = df.copy()
df_chna_03['embark_town'].fillna(method='ffill', inplace=True)
print(f'기존 값 : {df["embark_town"].isnull().sum()} / 변경 값 : {df_chna_03["embark_town"].isnull().sum()}')

# 중복데이터 확인 및 처리
'''
duplicated() 메소드는 전에 나온 행들과 현재 행의 값을 비교하여 중복되는 행이면
True를 아니면 False를 반환함. 즉, 처음 나온행은 True 그 이후에 추가로 나온 행은 False를 반환함.
'''
df_dup_00 = df['class'].head().duplicated()
print(df_dup_00, '\n')

# 중복데이터 제거 - 기준으로 할 열을 설정하고 싶으면 subset 옵션 사용
df_dup_01 = df['class'].head().drop_duplicates()
print(df_dup_01)

'''
* 데이터 표준화 
- 다양한 소스에서 다른 사람들에 의해 수집되는 데이터들은 단위, 대소문자, 약칭 등의
이유로 인해 각기 다른 형태로 표현됨. 표현방식의 차이는 분석의 정확도 저하에 직접적인
영향을 미치기 때문에 일관성있게 표준화하는 과정이 필요함
'''
# 단위환산
df_plus = sns.load_dataset('mpg')
# mile per gallon을 kilometoer per liter로 치환
t_value = 1.60934/3.78541
df_plus['kpl'] = df_plus['mpg']*t_value
df_plus.head()

# 자료형 변환 - 예시 : 숫자로 이루어진 데이터가 object형과 같이 문자로 저장되어 있다면 해당 자료형은 변경해주어야함.
df_plus_tt_00 = df_plus.copy()
df_plus_tt_00['horsepower'] = df_plus_tt_00['horsepower'].astype('object')
print(df_plus_tt_00['horsepower'].dtypes)
df_plus_tt_00['horsepower'] = df_plus_tt_00['horsepower'].astype('float')
print(df_plus_tt_00['horsepower'].dtypes)

# 범주형(카테고리) 데이터 처리 : 연속변수를 일정한 구간으로 나누고, 각 구간을 범주형 이산변수로 변환하는 과정을 구간분할이라고 함.
# np.histogram 함수로 3개의 bin으로 구분할 경계값 리스트 구하기
df_plus_tt_01 = df_plus.copy()
df_plus_tt_01.dropna(subset=['horsepower'], axis=0, inplace=True)
# df_plus_tt_01[np.isfinite(df_plus_tt_01['horsepower'])] # NaN 또는 INF값 여부 확인
df_plus_tt_01.dropna(axis=0, inplace=True)
count, bin_divider = np.histogram(df_plus_tt_01['horsepower'], bins=3)
bin_names = ['Low', 'Medium', 'High']

print(bin_divider, '\n')
# cut함수로 각 데이터에 3개의 bin 할당
df_plus_tt_01['hp_bin'] = pd.cut(
                                    x=df_plus_tt_01['horsepower']
                                  , bins=bin_divider
                                  , labels=bin_names
                                   , include_lowest=True 
                     )
print(df_plus_tt_01[['horsepower', 'hp_bin']].head(20))

'''
* 더미변수(dummy variable)의 개념과 사용방법
- 카테고리를 나타내는 범주형 데이터를 회귀분석 등 머신러닝 알고리즘에 사용하기 위해서는 컴퓨터가 인식가능한 
값으로 변경 해주어야함. 이 경우 0또는 1로 표현되는 더미변수를 사용하며, 이 경우 0과 1은 크고 작음의 의미는 없고
단순히 어떤 특성이 있는지 여부만 표시함.
- 범주형 데이터를 컴퓨터가 인식할 수 있도록 0과 1로만 구성되는 원핫벡터(one hot vector)로 변환한다고 하여 
원핫인코딩(one-hot-encoding)이라고도 부름.
- get_dummies()함수를 보편적으로 사용하며, 해당 함수를 사용시 범주형 변수의 모든 고유값을 각각 새로운 더미 변수로 변환함.
- sklearn 라이브러리를 추가사용하여 원핫 인코딩을 진행할 수도 있음.
'''
hp_dummies = pd.get_dummies(df_plus_tt_01['hp_bin'])
print(hp_dummies)

# sklearn 패키지 로드
import sklearn

# 전처리를 위한 encoder 객체 생성-
label_encoder = sklearn.preprocessing.LabelEncoder()
onehot_encoder = sklearn.preprocessing.OneHotEncoder()

# label encoder로 문자형 범주를 숫자형 범주로 변환
onehot_labeled = label_encoder.fit_transform(df_plus_tt_01['hp_bin'].head(15))
print(f'{onehot_labeled} / {type(onehot_labeled)}\n')

# 2차원 행렬로 변경
onehot_reshaped = onehot_labeled.reshape(len(onehot_labeled), 1)
print(f'{onehot_reshaped}\n{type(onehot_reshaped)}\n')

# 희소행렬로 변환
onehot_fitted = onehot_encoder.fit_transform(onehot_reshaped)
print(f'{onehot_fitted}\n{type(onehot_fitted)}')

'''
* 정규화(Normalization)의 개념과 사용방법
- 숫자데이터의 상대적인 크기 차이는 머신러닝 분석결과가 달라질 수 있기에 해당 차이를 제거할 필요가 있음
예를 들어 A변수의 범위가 0~1000이고 B변수의 범위가 0~1이면 B변수보다 A변수의 영향이 더 커지게 됨.
각 열(변수)에 속하는 데이터 값을 동일크기 기준으로 나눈 비율로 나타내는 것을 정규화라고 지칭함.
- 가장 간편하게 할 수 있는 정규화는 Min-Max 정규화가 있으며, 수식으로 직접 구현해도 되고, 패키지를 불러와서 
내부 함수를 사용해도 됨.
'''

df_plus_x = df_plus['horsepower'] - df_plus['horsepower'].min()
min_max = df_plus['horsepower'].max() - df_plus['horsepower'].min()
df_plus_minmax1 = df_plus_x / min_max

# 필요패키지 로드
import sklearn

mm_scaler = sklearn.preprocessing.MinMaxScaler()
df_plus_minmax2 = pd.DataFrame(mm_scaler.fit_transform(df_plus[['horsepower']]), columns=['horsepower'])
print(df_plus_minmax1.describe(), '\n\n',df_plus_minmax2.describe())

# 시계열 데이터
# 문자열을 시계열 데이터로 변환 : to_datetime()함수 사용

df_dow = sns.load_dataset('dowjones')
df_dow['Date'] = df_dow['Date'].astype('object')
df_dow['New_Date'] = pd.to_datetime(df_dow['Date'])
print(df_dow.info())

# Timestamp를 Period로 변환
'''
* to_period 함수의 freq 종류
- D : day
- W : week
- M : month end
- MS : month start
- Q : quarter_end
- QS : quarter start
- A : year end
- AS : year start
- B : business day(휴일제외)
- H : hour
- T : minute
- S : Second
- L : millisecond
- U : microsecond
- N : nanosecond
'''
ts_dates = pd.to_datetime(list(df_dow['New_Date']))
ts_day = ts_dates.to_period(freq='D')
ts_month = ts_dates.to_period(freq='M')
ts_year = ts_dates.to_period(freq='A')
print(f'{ts_day}\n{ts_month}\n{ts_year}')

# Timestamp 배열 만들기
dates_01 = pd.date_range(
                         start='2021-01-01'# 날짜 범위 시작
                       , end=None # 날짜범위 끝
                       , periods=6 # 생성할 Timestamp 수
                       , freq='MS' # 시간간격
                       , tz='Asia/Seoul' # 시간대(timezone)
                        )

dates_02 = pd.date_range(
                         start='2021-01-01'# 날짜 범위 시작
                       , end=None # 날짜범위 끝
                       , periods=6 # 생성할 Timestamp 수
                       , freq='M' # 시간간격
                       , tz='Asia/Seoul' # 시간대(timezone)
                        )

dates_03 = pd.date_range(
                         start='2021-01-01'# 날짜 범위 시작
                       , end=None # 날짜범위 끝
                       , periods=6 # 생성할 Timestamp 수
                       , freq='3M' # 시간간격
                       , tz='Asia/Seoul' # 시간대(timezone)
                        )

print(dates_01, '\n\n', dates_02, '\n\n', dates_03, '\n\n')

dates_04 = pd.period_range(
                            start='2021-01-01'# 날짜 범위 시작
                          , end=None # 날짜범위 끝
                          , periods=6 # 생성할 Timestamp 수
                          , freq='M' # 시간간격
                          )

print(dates_04, '\n\n')

P.S 더 나은 개발자가 되기위해 공부중입니다. 잘못된 부분을 댓글로 남겨주시면 학습하는데 큰 도움이 될 거 같습니다

728x90
반응형

댓글