다양한 데이터 전처리 기법

이진·2024년 8월 26일

Aiffel

목록 보기
2/7

결측치

데이터를 수집하는 과정에서 누락되지 않도록하는 것이 가장 좋은 방법이지만, 이미 결측치가 존재한다면 이를 처리해 줘야합니다.

처리하는 방법으로는 크게 2가지가 존재하는데

  1. 결측치가 있는 데이터를 제거한다.
  2. 결측치를 어떤 값으로 대체한다.

DataFrame.isnull() 은 데이터마다 결측치 여부를 True, False로 반환합니다.

DataFrame.any(aaxis = 1) 은 행마다 하나라도 True가 있으면 True, 그렇지 않으면 False를 반환합니다.

두 개를 조합하여

DataFrame[DataFrame.isnull().any(axis = 1)] 를 하게 되면 값이 True인 데이터만 추출을 할 수 있게됩니다.

수치형 데이터 보완

  1. 특정 값을 지정해 줄 수 있습니다.
    • 그러나 결측치가 많은 경우, 모두 같은 값으로 대체한다면 데이터의 분산이 실제보다 작아지는 문제가 생길 수 있습니다.
  2. 평균, 중앙값 등으로 대체할 수 있습니다.
    • 1번에서 특정 값으로 대체했을 때와 마찬가지로 결측치가 많은 경우 데이터의 분산이 실제보다 작아지는 문제가 발생할 수 있습니다.
  3. 다른 데이터를 이용해 예측값으로 대체할 수 있습니다.
    • 예를 들어 머신러닝 모델로 2020년 4월 미국의 예측값을 만들고, 이 값으로 결측치를 보완할 수 있습니다.
  4. 시계열 특성을 가진 데이터의 경우 앞뒤 데이터를 통해 결측치를 대체할 수 있습니다.
    • 예를 들어 기온을 측정하는 센서 데이터에서 결측치가 발생할 경우, 전후 데이터의 평균으로 보완할 수 있습니다.

범주형 데이터 보완

  1. 특정 값을 지정해 줄 수 있습니다.
    • 예를 들어 '기타', '결측'과 같이 새로운 범주를 만들어 결측치를 채울 수 있습니다.
  2. 최빈값 등으로 대체합니다.
    • 결측치가 많은 경우 최빈값이 지나치게 많아질 수 있으므로 결측치가 많을 때는 다른 방법을 사용하는 것이 좋습니다.
  3. 다른 데이터를 이용해 예측값으로 대체할 수 있습니다.
  4. 시계열 특성을 가진 데이터의 경우 앞뒤 데이터를 통해 결측치를 대체할 수 있습니다.

데이터가 범주형이더라도 수치형과 크게 다르지는 않습니다.

중복된 데이터

데이터를 수집하는 과정에서 중복된 데이터가 생길 수 있습니다.

같은 값을 가진 데이터 없이 행별로 값이 유일해야한다면 중복된 데이터를 제거해야합니다.

DataFrame.duplicated() 는 데이터 중복 여부를 불리언 값으로 반환해줍니다.

pandas에서 DataFrame.drop_duplicates를 통해 중복된 데이터를 손쉽게 삭제할 수 있습니다.

df = pd.DataFrame({'id':['001', '002', '003', '004', '002'], 
                   'name':['Park Yun', 'Kim Sung', 'Park Jin', 'Lee Han', 'Kim Min']})
df

# id가 중복된 경우 나중에 들어온 값만 남기는 코드를 작성해보세요!
# ('Kim Sung' 항목이 삭제되어야 합니다.)

df.drop_duplicates(subset=['id'], keep='last', inplace=True)
df

이상치

이상치란 대부분 값의 범위에서 벗어나 극단적으로 크거나 작은 값을 의미합니다.
Min-Max Scaling 해보면 대부분의 값은 0에 가깝고 이상치만 1에 가까운 값을 가지게 될 것입니다.
이렇게 몇 개의 이상치 때문에 대부분 값의 차이는 의미가 거의 없어지게 됩니다.
극단적인 값이 생기는 경우를 제외하고 데이터를 고려하고 싶은 경우 이상치를 제거하고 분석합니다.

  • 이상치 찾기

    Z-score

    • 가장 먼저 생각해 볼 수 있는 간단하고 자주 사용되는 방법입니다.
    • 평균을 빼주고 표준편차로 나눈 후 특정 기준을 넘어서면 이상치라 판단하는 방법입니다.
    • 기준을 작게하면 이상치라 판단하는 데이터가 많아지고
    • 크게하면 이상치라 판단하는 데이터가 적어집니다.
# abs(df[col] - np.mean(df[col])) : 데이터에서 평균을 빼준 것에 절대값을 취합니다.
# abs(df[col] - np.mean(df[col]))/np.std(df[col]) : 위에서 얻은 값을 표준편차로 나눠줍니다.
# df[abs(df[col] - np.mean(df[col]))/np.std(df[col])>z].index: 값이 z보다 큰 데이터의 인덱스를 추출합니다.

def outlier(df, col, z):
    return df[abs(df[col] - np.mean(df[col]))/np.std(df[col])>z].index
    
# Q. 무역수지 값을 기준으로 z=3일 때 이상치 데이터를 출력해보세요.
trade.loc[outlier(trade, '무역수지', 3)]

# Q. not_outlier() 함수를 구현하세요.
def not_outlier(df, col, z):
    return df[abs(df[col] - np.mean(df[col]))/np.std(df[col])<z].index

IQR 방법

  • IQR=Q3−Q1
  • 즉, IQR은 제 3사분위수에서 제 1사분위 값을 뺀 값으로 데이터의 중간 50%의 범위라고 생각하시면 됩니다.
  • 𝑄1−1.5∗𝐼𝑄𝑅Q1−1.5∗IQR 보다 왼쪽에 있거나,
  • 𝑄3+1.5∗𝐼𝑄𝑅Q3+1.5∗IQR 보다 오른쪽에 있는 경우 우리는 이상치라고 판단합니다.
# 이상치가 포함된 데이터 생성
np.random.seed(2020)
data = np.random.randn(100)  # 평균 0, 표준편차 1의 분포에서 100개의 숫자를 샘플링한 데이터 생성
data = np.concatenate((data, np.array([8, 10, -3, -5])))      # [8, 10, -3, -5])를 데이터 뒤에 추가함
data

# 박스플롯으로 이상치 확인하기
fig, ax = plt.subplots()
ax.boxplot(data)
plt.show()

# IQR을 먼저 구하기
Q3, Q1 = np.percentile(data, [75 ,25])
IQR = Q3 - Q1
IQR

# IQR을 통해 이상치 구하기 
data[(Q1-1.5*IQR > data)|(Q3+1.5*IQR < data)]

# Q. 사분위 범위수를 이용해서 이상치를 찾는 outlier2() 함수를 구현해보세요.
def outlier2(df, col):
    Q1, Q3 = np.percentile(df[col], [25, 75])
    IQR = Q3 - Q1

    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    outliers = df[(df[col] < lower_bound) | (df[col] > upper_bound)]
    return outliers

이상치를 판단한 뒤

  1. 가장 간단한 방법으로 이상치를 삭제할 수 있습니다.
    • 이상치를 원래 데이터에서 삭제하고, 이상치끼리 따로 분석하는 방안도 있습니다.
  2. 이상치를 다른 값으로 대체할 수 있습니다.
    • 데이터가 적으면 이상치를 삭제하기보다 다른 값으로 대체하는 것이 나을 수 있습니다.
      예를 들어 최댓값, 최솟값을 설정해 데이터의 범위를 제한할 수 있습니다.
  3. 혹은 결측치와 마찬가지로 다른 데이터를 활용하여 예측 모델을 만들어 예측값을 활용할 수도 있습니다.
  4. 아니면 binning을 통해 수치형 데이터를 범주형으로 바꿀 수도 있습니다.

이상치에 관심을 두는 이유

  1. 데이터 품질 개선
    • 데이터 오류 탐지 : 이상치는 종종 데이터 입력 오류, 시스템 오류, 잘못된 측정 등의 문제를 나타내기에 이상치를 감지하고 제거함으로 데이터의 정확성과 신뢰성을 높일 수 있습니다.
    • 결측값 대체 : 이상치가 있는 경우 이를 결측값으로 처리하거나 적절한 값으로 대체할 수 있습니다. 이를 통해 데이터의 유용성과 완성도를 개선할 수 있습니다.
  2. 모델 성능 향상
    • 예측 모델 정확도 향상 : 이상치가 예측 모델에 영향을 미치는 경우가 있습니다. 이상치를 처리하지 않으면 모델의 정확도가 저하될 수 있습니다. 이상치를 제거하거나 대체하여 모델의 성능을 향상시킬 수 있습니다.
    • 머신러닝 모델 학습 : 이상치가 있는 경우 학습 알고리즘이 잘못된 패턴을 학습할 수 있습니다. 이를 방지하기 위해 이상치를 제거하거나 처리하여 모델의 학습을 개선할 수 있습니다.
  3. 통찰력 제공
    • 데이터 이해 : 이상치는 데이터의 특이한 패턴이나 중요한 정보를 나타낼 수 있습니다. 이상치를 분석하여 데이터에 대한 추가적인 통찰력을 얻을 수 있습니다.
    • 사회적, 경제적 의미 : 특정 도메인에서의 이상치는 중요한 의미를 가질 수 있습니다. 예를 들어, 금융 데이터에서의 이상치는 사기 거래를 나타낼 수 있습니다.
  4. 모델 해석 가능성
    • 모델 해석 가능성 : 일부 모델은 이상치에 민감할 수 있습니다. 이상치를 처리하고 모델을 해석 가능하게 만들면 모델의 결과를 더 잘 이해하고 해석할 수 있습니다.

정규화

칼럼마다 스케일이 차이가 나게 되면 머신러닝 모델 학습에 문제가 발생할 수 있습니다.

예를 들어 키와 몸무게가 있다고 가정하면, 키의 범위가 몸무게의 범위보다 크기 때문에 데이터 간의 거리를 잴 때, 범위가 큰 키의 영향을 크게 받게 됩니다.

Standardization

  • Standardization은 보통 평균이 0이고 표준편차가 1일 때 사용합니다.
  • 그렇기에 데이터가 가우시안 분포를 따를 경우 유용합니다.
  • 아래 이미지를 보시면 Standardization 기법으로 정규화를 하면 어떻게 바뀌는 지 보여줍니다.

Min-Max Scaling

  • 데이터의 최솟값은 0, 최댓값은 1로 변환합니다.
  • Min-Max Scaling은 피처의 범위가 다를 때 주로 사용하며 확률 분포를 모를 때 유용합니다.
  • 아래 이미지를 보시면 Min-Max Scaling 기법으로 정규화를 하면 어떻게 바뀌는 지 보여줍니다.

💡 train 데이터와 test 데이터가나눠져있는 경우, train 데이터를 정규화시켰던 기준 그대로 test 데이터도 정규화 시켜줘야합니다.
👏train 데이터에는 fit_transform / test 데이터에는 transform을 하는 이유입니다.👏

One-Hot Encoding

머신러닝이나 딥러닝 프레임워크에서 범주형을 지원하지 않는 경우 원-핫 인코딩을 해야 합니다.

원-핫 인코딩이란 카테고리별 이진 특성을 만들어 해당하는 특성만 1, 나머지는 0으로 만드는 방법입니다.
pandas에서 get_dummies 함수를 통해 손쉽게 구현할 수 있습니다.

#trade 데이터의 국가명 컬럼 원본
print(trade['국가명'].head())  

# get_dummies를 통해 국가명 원-핫 인코딩
country = pd.get_dummies(trade['국가명'])
country.head()

# pd.concat 함수로 데이터프레임 trade와 country를 합쳐줍니다.
trade = pd.concat([trade, country], axis=1)
trade.head()

# 이제는 필요 없어진 국가명 컬럼을 삭제해 주고 나면 trade는 우리가 원하는 데이터프레임이 됩니다.
trade.drop(['국가명'], axis=1, inplace=True)
trade.head()

구간화

데이터를 구간별로 나누고자할 때 사용하는 기법으로 Data Binning, bucketing 이라 부릅니다.
히스토그램과 같이 연속적인 데이터를 구간을 나눠 분석할 때 사용하는 방법입니다.

salary = pd.Series([4300, 8370, 1750, 3830, 1840, 4220, 3020, 2290, 4740, 4600, 
                    2860, 3400, 4800, 4470, 2440, 4530, 4850, 4850, 4760, 4500, 
                    4640, 3000, 1880, 4880, 2240, 4750, 2750, 2810, 3100, 4290, 
                    1540, 2870, 1780, 4670, 4150, 2010, 3580, 1610, 2930, 4300, 
                    2740, 1680, 3490, 4350, 1680, 6420, 8740, 8980, 9080, 3990, 
                    4960, 3700, 9600, 9330, 5600, 4100, 1770, 8280, 3120, 1950, 
                    4210, 2020, 3820, 3170, 6330, 2570, 6940, 8610, 5060, 6370,
                    9080, 3760, 8060, 2500, 4660, 1770, 9220, 3380, 2490, 3450, 
                    1960, 7210, 5810, 9450, 8910, 3470, 7350, 8410, 7520, 9610, 
                    5150, 2630, 5610, 2750, 7050, 3350, 9450, 7140, 4170, 3090])
salary.hist()

업로드중..

# cut을 사용하기 위해 우선 구간 지정해주기
bins = [0, 2000, 4000, 6000, 8000, 10000]

# cut 함수에 데이터와 구간을 입력하면 데이터를 구간별로 나눠줍니다.
ctg = pd.cut(salary, bins=bins)

# 구간별로 값이 몇개가 속해 있는 지 value_counts()로 확인할 수 있습니다.
ctg.value_counts().sort_index()

# qcut은 구간을 일정하게 나누는 것이 아니라 데이터의 분포를 비슷한 크기의 그룹으로 나눠줍니다.
ctg = pd.qcut(salary, q=5)

💯 본 글에 작성된 전처리 방법을 모든 데이터에 일괄적으로 적용해야하는 것이 아닌 데이터 전처리는 데이터의 특성을 파악해서 해야합니다.

profile
Victoria Concordia Crescit

0개의 댓글