머신러닝&딥러닝

머신러닝 & 딥러닝 기초 7편 | 데이터 정제(결측치, 이상치 처리 등)

SecLogs YJ 2026. 3. 12. 22:12
목차

1. 결측치 처리(제거, 대체)
  1-1 결측치란
  1-2 결측치 제거
  1-3 결측치 대체
2. 이상치 처리(IQR, Z-Score)
  2-1 이상치란
  2-2 IQR(Interquartile Range) 방법
  2-3 Z-Score 방법
3. 데이터 중복 확인 및 제거
  3-1 중복 데이터란
  3-2 중복 데이터 확인
  3-3 중복 데이터 제거

 

1. 결측치 처리(제거, 대체)

1-1 결측치란

결측치란 데이터셋에서 특정 값이 누락된 상태를 의미한다.

 

결측치는 pandas에서 NaN(Not a Number) 형태로 표시된다.
NaN은 실제 숫자가 아닌 결측값을 의미하는 특수한 값이다.

 

결측치는 데이터 분석 결과에 영향을 줄 수 있으므로 반드시 처리해야 한다.

  • 분석 왜곡 방지 
  • 모델 학습 방해 방지 
  • 데이터 품질 향상

결측치 처리시 결측치의 양, 데이터의 중요성, 분석 목표를 고려해야 한다.

간단한 데이터셋에서는 제거와 평균/중간값 대체가 유용하다.

 

하지만, 복잡한 데이터에서는 머신러닝 기반 대체를 활용한다.

 

결측치 처리는 2가지 방법이 있다.

  1. 결측치 제거
  2. 결측치 대체

1-2 결측치 제거

결측치 제거는 다음과 같은 상황에서 사용한다.

  • 결측치가 데이터셋에서 차지하는 비율이 매우 적을 때
  • 해당 열이나 행이 분석에 큰 영향을 주지 않을 때

결측치 제거는 2가지 방법이 있다.

  • 열 제거 : 특정 열에 결측값이 너무 많을 경우, 해당 열 자체를 삭제한다.
  • 형 제거 : 특정 행에 결측값이 있을 경우, 해당 행을 삭제한다.

행을 삭제할 경우 데이터의 전체 개수가 줄어들 수 있으므로 데이터 손실 가능성을 고려해야 한다.

 

 

예제 1) 결측치 data 생성

import pandas as pd

data = {
    'Name': ['Alice', 'Bob', None],
    'Age': [25, None, 30],
    'Score': [85, 90, None]

    }

df = pd.DataFrame(data)

 

df를 출력해보면 아래와 같은 표가 출력된다.

NaN로 된 결측치를 확인할 수 있다.

 

	Name	Age	Score
0	Alice	25.0	85.0
1	Bob	NaN	90.0
2	NaN	30.0	NaN

 

예제 2) 결측치 확인하기

isnull() 메서드를 사용하여 결측치가 있는지 확인한다.

결측치일 경우 True가 반환된다.

df.isnull()

 

아래와 같은 표가 출력된다.

	Name	Age	Score
0	False	False	False
1	False	True	False
2	True	False	True

 

예제 3) 결측치가 있는 삭제

dropna() 메서드를 사용하여 결측치가 있는 행을 삭제한다.

df_missing_rows = df.dropna()

print(df_missing_rows)

 

출력 결과 아래와 같은 표가 출력된다.

기존에 결측값이 있던 행 2개가 삭제된 것을 볼 수 있다.

    Name   Age  Score
0  Alice  25.0   85.0

 

예제 4) 결측치가 있는 삭제

dropna(axis=1) 메서드를 사용하여 결측치가 있는 열을 삭제한다.

axis는 축이라는 뜻으로, dropna의 axis의 기본값은 0이다.

axis=0이면 행을 기준으로, axis=1이면 열을 기준으로 데이터를 삭제한다.

df_missing_cols = df.dropna(axis=1)  # axis = 1(열단위), axis = 0(default, 행단위)

print(df_missing_cols)

 

출력 결과 아래와 같은 표가 출력된다.

기존에 결측값이 모든 열에 있었으므로, 모든 데이터가 삭제된 것을 볼 수 있다.

Empty DataFrame
Columns: []
Index: [0, 1, 2]

 

예제 5) 결측치가 있는 특정 열 삭제

Cabin 열에 결측값이 매우 많을 경우 해당 열 자체를 삭제할 수 있다.
inplace=True 옵션은 기존 DataFrame을 직접 수정하는 것을 의미한다.

df.drop('Cabin', axis=1, inplace=True)

 

1-3 결측치 대체

결측치 대체는 다음과 같은 상황에서 사용한다.

  • 데이터의 양이 적을 때
  • 결측치가 무작정 삭제되면 데이터 손실이 너무 클 때

결측치 대체는 데이터의 분포를 유지하면서 데이터 손실을 최소화할 수 있다는 장점이 있다.

 

결측치 대체는 3가지 방법이 있다.

  • 고정 값으로 대체 : 특정 값으로 결측치를 채운다. 예) 평균, 중앙값, 0
  • 통계 기반 대체
    • 평균값으로 대체하기 : 수치형 데이터에서 결측값을 해당 열의 평균으로 대체한다.
    • 중간값으로 대체하기 : 평균이 극단값에 영향을 받는 경우, 중간값을 사용한다.
    • 최빈값으로 대체하기 : 범주형 데이터에서 가장 많이 등장한 값으로 결측치를 대체한다.
  • 예측 기반 대체 : 머신러닝 모델을 사용하여, 결측치를 예측해 채운다.

평균값(mean)은 모든 값의 평균을 의미하며, 극단값(outlier)의 영향을 받을 수 있다.
중앙값(median)은 데이터를 정렬했을 때 중앙에 위치한 값으로,극단값의 영향을 덜 받는다.

 

예제 0) 고정 값으로 대체

Age 열에서 결측값이 있는 행을 찾아 0으로 대체한다.

df.loc[df['Age'].isnull(), 'Age'] = 0

 

예제 1) 결측치를 평균값으로 대체

.mean() 메서드와 .fillna() 메서드를 사용하여 결측치를 평균값으로 대체한다.

Pandas의 .fillna() 메서드는 NULL 값을 지정된 값으로 대체한다.

Pandas의 .mean() 메서드는 각 열의 평균값을 포함하는 Series를 반환한다.

df['Age'] = df["Age"].fillna(df['Age'].mean())

df

 

출력 결과는 다음과 같다.

	Name	Age	Score
0	Alice	25.0	85.0
1	Bob	27.5	90.0
2	NaN	30.0	NaN

 

1행의 Age 값이  25와 30의 평균값인 27.5의 값으로 채워진 것을 볼 수 있다.

 

예제 2) 결측치를 중간값으로 대체

.median() 메서드와 .fillna() 메서드를 사용하여 결측치를 중간값으로 대체한다.

Pandas의 .fillna() 메서드는 NULL 값을 지정된 값으로 대체한다.

Pandas의 .median() 메서드는 각 열의 중앙값을 포함하는 Series를 반환한다.

df['Score'] = df['Score'].fillna(df['Score'].median())

df

 

 

출력 결과는 다음과 같다.

	Name	Age	Score	
0	Alice	25.0	85.0
1	Bob	27.5	90.0
2	NaN	30.0	87.5

 

2행의 Score 값이 85와 90의 중앙값 87.5로 채워진 것을 볼 수 있다.

 

예제 3) 결측치를 최빈값으로 대체

.mode() 메서드와 .fillna() 메서드를 사용하여 결측치를 최빈값으로 대체한다.

Pandas의 .fillna() 메서드는 NULL 값을 지정된 값으로 대체한다.

Pandas의 .mode() 메서드는 각 열의 최빈값을 반환한다.

 

df['Name'] = df['Name'].fillna(df['Name'].mode()[0])

df

 

이때 mode()[0]최빈값이 여러 개일 경우 가장 처음 값을 가져온다는 의미이다.

 

출력 결과는 다음과 같다.

	Name	Age	Score
0	Alice	25.0	85.0
1	Bob	27.5	90.0
2	Alice	30.0	87.5

 

2행의 Name 값이 Alice로 채워진 것을 볼 수 있다.

최빈값 인 Alice, Bob 중 가장 처음 값을 가져온 것을 의미한다.

 

2. 이상치 처리(IQR, Z-Score)

2-1 이상치란

이상치란 데이터셋에서 다른 값들과 크게 동떨어져 있는 값을 의미한다.

 

이상치는 분석 결과를 곡하거나 모델의 성능을 저하시킬 수 있으므로 적절히 처리해야 한다.

  • 데이터 왜곡 방지
  • 모델 성능 향상
  • 데이터 신뢰성 확보 

이상치 처리는 2가지 방법이 있다.

  1. IQR(Interquartile Range) 방법
  2. Z-Score 방법

2-2 IQR(Interquartile Range) 방법

IQR은 데이터 중간 50%를 포함하는 범위를 이용해 이상치를 탐지하는 방법이다.

 

보통 비대칭 분포 데이터에 적합하다.

IQR 방법은 극단값(outlier)에 비교적 영향을 덜 받기 때문에, 비대칭 분포 데이터에서도 안정적으로 이상치를 탐지할 수 있다.

 

  • Q1 (1사분위수) : 하위 25%의 값
  • Q3 (3사분의 수) : 상위 75%의 값
  • IQR : Q3-Q1
  • 이상치 기준
    • 아래쪽 : Q1 - 1.5 * IQR
    • 위쪽 : Q3 + 1.5*IQR

 

예제 1) 이상치 데이터 생성

# 이상치 처리

data = {
    'score' : [70, 75, 80, 85, 90, 150]
}

df = pd.DataFrame(data)

df

 

예제 2) IQR 값 계산

quantile() 메드를 사용하면, 분위수를 계산할 수 있다.

# IQR 
Q1 = df['score'].quantile(0.25)
Q3 = df['score'].quantile(0.75)

IQR = Q3 - Q1

print(Q1, Q3, IQR)

 

예제 3) 이상치 범주 구하기

변수명 뒤에 _를 붙이는 것은 Python 예약어와의 충돌을 피하거나, 임시 변수임을 나타내기 위해 사용하기도 한다.

이상치 범주는 low_ 값보다 작은 값이거나(or) up_ 값보다 큰 값이다.

low_ = Q1 -1.5* IQR
up_ = Q3 + 1.5* IQR

print(f'이상치 범주: {low_}보다 작은 값, {up_}보다 큰 값')

 

예제 4) 이상치 값 찾기

이상치 값은 l ow_ 값보다 작은 값이거나(or) up_ 값보다 큰 값이다.

df_1 = df[(df['score'] < low_) | (df['score'] > up_)]

df_1

 

예제 5) 이상치 제거하기

이상치가 아닌 값은 low_값보다 같거나 크고(and) up_값보다 같거나 작은 값이다.

df_2 = df[(df['score'] >= low_) & (df['score'] <= up_)]
df_2

 

예제 6) 이상치 데이터 변경하기

이상치 데이터는 loc[]를 이용하여 변경할 수 있다.

df['score'] = df['score'].astype(float)
df.loc[df['score'] < low_, 'score'] = low_  
df.loc[df['score'] > up_, 'score'] = up_

df

 

2-3 Z-Score 방법

Z-Score는 데이터의 값이 평균에서 얼마나 떨어져 있는지를 표준 편차 단위로 나타내는 방법이다.

 

보통 정규분포를 따르는 데이터에 적합하다.

계산이 간단하다.

 

 

  • Z = (x - μ) / σ
    • x : 개별 데이터 값
    • μ : 평균
    • σ : 표준 편차 이상치 기준
  • 일반적으로 |Z| > 3 이면 이상치로 간주한다.

예제 0) 모듈 설치하기

pip install scipy

 

 

예제 1) 예제 데이터 생성하기

import pandas as pd
from scipy.stats import zscore

data = {'Scores' : [70, 75, 80, 85, 90, 1000000]}
df = pd.DataFrame(data)

 

 

예제 2) Z-Score 계산하

zscore(arr, axis=- ddof=0)은 입력 데이터의 상대적 Z-점수를 표본 평균 및 표준 편차에 대한 상대값을 계산한다.

아래 코드는 'Scores'의 열 데이터에 대한 Z-Score을 계산하는 코드이다.

df['Z_Score'] = zscore(df['Scores'])
df

 

예제 2) 출력 결과

	Scores	Z_Score
0	70	-0.447240
1	75	-0.447227
2	80	-0.447214
3	85	-0.447200
4	90	-0.447187
5	1000000	2.236068

 

 

예제 3) 이상치 확인하기

아래 코드에서는 2를 이상치의 기준으로 설정하였다.

outliers=df[(df['Z_Score'] >2)]
print(outliers)

 

예제 3) 출력 결과

    Scores   Z_Score
5  1000000  2.236068

 

 

예제 4) 이상치 제거하기

df_deleted = df[df['Z_Score'] <= 2]

df_deleted

 

예제 4) 출력 결과

	Scores	Z_Score
0	70	-0.447240
1	75	-0.447227
2	80	-0.447214
3	85	-0.447200
4	90	-0.447187

 

 

예제 5) 이상치 중간값으로 대체하기

중앙값은 데이터 개수가 짝수일 경우 두 가운데 값의 평균으로 계산되므로 소수점 값이 나올 수 있다.

따라서 정수형(int64) 컬럼에 중앙값을 대입하려면 컬럼을 실수형(float)으로 변환해야 한다.

astype() 메서드를 이용하여 형 변환을 한다.

df['Scores'] = df['Scores'].astype(float)

median = df['Scores'].median()
df.loc[df['Z_Score'] > 2, 'Scores'] = median

print(df)

 

예제 5) 출력 결과

   Scores   Z_Score
0    70.0 -0.447240
1    75.0 -0.447227
2    80.0 -0.447214
3    85.0 -0.447200
4    90.0 -0.447187
5    82.5  2.236068

 

결측값 대체 후, Z-Score 계산 및 이상치 확인을 다시 해보면, 이상치가 없는 것을 확인할 수 있다.

 

예제 6) 이상치 재확인

#Z-Score계산
df['Z_Score'] = zscore(df['Scores'])

# 이상치 확인
outliers=df[(df['Z_Score'] >2)]
print(outliers)

 

예제 6) 출력 결과

Empty DataFrame
Columns: [Scores, Z_Score]
Index: []

 

3. 데이터 중복 확인 및 제거

3-1 중복 데이터란

중복 데이터는 데이터셋에서 동일한 행(row)이 반복적으로 나타나는 것을 의미한다.

 

중복 데이터가 있으면 분석 결과가 왜곡될 수 있고, 데이터의 크기가 큰 경우 처리 속도와 메모리 사용량에 영향을 줄 수 있다.

 

중복 데이터의 형태로는 다음과 같은 것이 있을 수 있다.

  • 완전 중복 : 모든 열의 값이 동일한 행
    • 보통 삭제를 한다.
  • 부분 중복 : 특정 열의 값만 동일한 행

 

예제 1) 중복 데이터 생성

data = {
    'Name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob'],
    'Age': [25, 30, 25, 35, 30],
    'Score': [85, 90, 85, 95, 90]
    }

df = pd.DataFrame(data)

df

 

예제 1) 출력 결과

    	Name	Age	Score
0	Alice	25	85
1	Bob	30	90
2	Alice	25	85
3	Charlie	35	95
4	Bob	30	90

 

3-2 중복데이터 확인

중복 데이터를 찾을 경우 pandas의 duplicated() 메서드를 사용한다.

duplicated() 메서드는 데이터에서 중복된 행을 빠르게 찾아, 중복된 행은 'True' 고유한 행은 'False'를 반환한다.

기본적으로 이전 행과 동일한 값을 가진 두 번째 행부터 True를 반환한다.

 

예제 2) 전체 행이 같은 중복 찾기

df.duplicated()

 

예제 2) 출력 결과

각 행마다 중복 데이터의 여부를 판별한다.

위에서부터 아래로 순서대로 검사하기 때문에, 중복 데이터의 두번째부터 True를 반환한다.

0    False
1    False
2     True
3    False
4     True
dtype: bool

 

예제 3) 중복된 행의 개수 구하기

중복된 행의 개수를 계산할 때는 sum() 메서드를 사용한다.

print(df.duplicated().sum())

 

예제 3) 출력 결과

2

 

예제 4) 특정 열의 값을 기준으로 중복 찾기

subset 옵션을 이용한다. 예제에서는 'Name' 열을 기준으로 중복을 찾는다.

df.duplicated(subset='Name')

 

예제 4) 출력 결과

0    False
1    False
2     True
3    False
4     True
dtype: bool

 

예제 5) 중복된 행 추출

duplicated()는 False, True의 Bool 값을 반환하기 때문에, 아래와 같은 코드로 추출이 가능하다.

df[df.duplicated()]

 

예제 5) 출력 결과

	Name	Age	Score
2	Alice	25	85
4	Bob	30	90

 

3-3 중복데이터 제거

 

예제 1) 중복된 행 삭제하기

중복된 행을 삭제할 때는 pandas의 drop_duplicates() 메서드를 사용한다.

drop_duplicates()는 열 또는 특정 열을 기준으로 DataFrame에서 중복 행을 제거한다.

df_cleaned = df.drop_duplicates()
print(df_cleaned)

 

예제 1) 출력 결과

중복 데이터인 2행과 4행이 삭제된 것을 볼 수 있다.

      Name  Age  Score
0    Alice   25     85
1      Bob   30     90
3  Charlie   35     95

 

예제 2) 중복 시 삭제 기준 정하기

drop_duplicates() 함수는 기본적으로 중복된 항목 중 첫 번째 항목만 유지한다.

마지막 항목까지 유지하려면 keep='last' 옵션을 사용하면 된다.

df_cleaned2 = df.drop_duplicates(keep='last')

print(df_cleaned2)

 

예제 2) 출력 결과

중복 데이터인 0행과 1행이 삭제된 것을 볼 수 있다.

      Name  Age  Score
2    Alice   25     85
3  Charlie   35     95
4      Bob   30     90

 

예제 3) 특정 열을 기준으로 삭제하기(?)

subset 옵션으로 중복을 확인할 열을 지정한다.

지정하지 않으면 모든 열이 고려된다.

df_cleaned3 = df.drop_duplicates(subset=['Name'])

 

예제 3) 출력 결과

Name을 기준으로 중복된 행이 삭제된 것을 볼 수 있다.

      Name  Age  Score
0    Alice   25     85
1      Bob   30     90
3  Charlie   35     95

 

예제 4) 중복된 항목 모두 제거하기

중복된 행을 모두 제거하려면 keep=False로 설정하면 된다.

df_cleaned4 = df.drop_duplicates(subset=['Name'], keep=False)

 

예제 4) 출력 결과

	Name	Age	Score
3	Charlie	35	95

 


👉 머신러닝 & 딥러닝 기초 8편 | 데이터 변환 및 스케일링