5. 데이터 정제(결측치, 이상치)

🔗 'Do it! 쉽게 배우는 파이썬 데이터 분석' 책을 기반으로 JupyterLab 환경에서 학습한 내용입니다 _φ(๑╹╹๑)

 

📌 결측치(누락된 값) 정제


  • 결측치 찾기: pandas 패키지의 isna(데이터 프레임명) 함수 활용
    • isna() 결과 True: 결측치인 값
    • isna() 결과 False: 결측치가 아닌 값
import pandas as pd
import numpy as np # np.nan을 통해 임의로 결측치를 만들기 위함

df = pd.DataFrame({'sex': ['M', 'F', np.nan, 'M', 'F'],
'score': [5, 4, 3, 4, np.nan]})
df

# 전체 테이블의 결측치 여부 확인
pd.isna(df)

# 일부 변수 열의 결측치 여부 확인
# df[['sex']].isna() 데이터프레임 형태로 결측치 여부 확인
# df['sex'].isna() 시리즈 형태로 결측치 여부 확인

pd.isna(df).sum() # 각 변수 별 결측치 빈도 출력

 

  • 결측치 있는 행 제거: 데이터프레임의 dropna(subset = ['결측지 제거할 변수명1', '결측치 제거할 변수명2', ...]) 함수 활용
    • dropna() 괄호 안에 아무것도 작성하지 않는 경우: 결측치가 존재하는 모든 행을 제거하므로, 분석에 필요한 행까지 손실 가능
      • ex) 성별, 소득, 지역으로 구성된 데이터에서 지역에 결측치가 있고, 성별과 소득의 관계성을 분석 할 때
    • 참고 Tip: mean, sum, agg 함수는 자동으로 결측치를 제거하고 계산하지만, 명시적으로 제거하고 분석하는 것 추천
df_nomiss = df.dropna(subset = ['sex', 'score']) # 결측치 제거한 데이터 프레임 할당
df_nomiss

 

  • 결측치 대체: 데이터프레임의 fillna(대체할 값) 함수를 활용하며, 데이터가 작고 결측치가 많아 데이터 분석에 왜곡이 발생하는 것을 방지하기 위하여 상황에 따라 아래 값들로 대체
    • 대체 방법1: 평균값이나 최빈값
      • ex) 평균값 대체: 매출 총액, 시험 1번 결석 시 해당 시험 성적 기존 다른 시험들 평균으로 대체
      • ex) 최빈값 대체: 설문조사를 기반으로 각 품목별 생산 개수 예측
    • 대체 방법2: 통계 분석 기법으로 예측값 추청
      • ex) 중고차 판매 [차종, 연식, 주행거리, 사고 유무, 판매 가격]에서 판매 가격 누락 시, 연식과 주행거리를 기반으로 판매가격 예측
exam = pd.read_csv('exam.csv')
exam.loc[[2, 7, 14], ['math']] = np.nan # loc[행 위치, 열 위치]: 데이터 위치 지정

mean_math = exam['math'].mean()
exam['math'] = exam['math'].fillna(mean_math) # math 열의 결측치를 평균값으로 대체한 후, 저장
exam

 

 

 

📌 이상치(정상 범위에서 크게 벗어난 값) 정제


  • 존재할 수 없는 값 제거하는 방법
    1. 이상치 확인: 데이터프레임의 value_counts() 활용
    2. 이상치 → 결측치로 대체: np.where(조건, '참인 경우', '거짓인 경우') 활용
      1. np.where() 함수에서 숫자와 NaN을 출력 결과로 활용하는 경우: np.where() 활용
      2. np.where() 함수에서 문자와 NaN을 출력 결과로 활용하는 경우: np.where() 함수에서 np.nan 대신 다른 문자열 활용 → 데이터프레임의 replace() 함수를 활용하여 np.nan으로 변경
      3. np.where() 함수에서 데이터프레임['변수명']의 값이 문자 형태인 것과 NaN을 출력 결과로 활용하는 경우: np.where() 바로 활용
    3. 결측치 제거: dropna(subset = ['제거할 변수명1', '제거할 변수명2',...]
# 임의의 데이터 프레임 생성

# 정상값: 성별 1, 2로 구성, 성적 1~5점
df = pd.DataFrame({'sex': [1, 2, 1, 3, 2, 1],
'score': [5, 4, 3, 4, 2, 6]}
df['sex'].value_counts().sort_index() # 성별 이상치 확인

# value_counts()만 할 경우, 빈도 많은 순서대로 출력
# sort_index(): 이름 순서대로

df['score'].value_counts.sort_index() # 성적 이상치 확인

# 이상치 → 결측치 대체

df['sex'] = np.where(df['sex'] == 3, np.nan, df['sex']) # 성별 값이 3인 경우 NaN
df['score'] = np.where(df['score'] == 6, np.nan, df['score']) # 성적이 6점인 경우 NaN
더보기
"""
np.where()에서 문자와 함께 np.nan을 활용하면, 결측치가 아닌 문자열로 'nan'이 들어감.
np.nan 말고 다른 문자열을 작성하고 해당 문자열을 nan으로 replace하는 방식 활용하기
"""

df = pd.DataFrame({'x1': [1, 1, 2, 2]})

df['x1'] = np.where(df['x1'] == 1, 'a', 'etc') # 1이 아닌 경우 NaN을 위해 임의의 문자열 'etc' 부여
df['x1'].replace('etc', np.nan) # 'etc' 문자열 NaN으로 변경

 

# 결측치 제거
df.dropna(subset = ['sex', 'score'])

 

 

  • 존재할 수 있지만 극단적인 값 제거하는 방법
    • 방법1: 논리적으로 판단
      • ex) 성인의 몸무게가 40~150kg을 벗어나는 경우는 매우 드물다고 판단하여 설정
    • 방법2: 통계적인 기준 설정 
      1. 상자 그림으로 극단치 기준 설정: seaborn 패키지의 boxplot(data = 데이터 프레임명, y = '변수명') 함수, 데이터프레임의 quantile(.숫자) 함수 활용
        • 상자 밖 가로선: 극단치 경계로 1.5IQR(IQR = Q3 - Q1의 값)에 해당 (1.5 IQR인 이유: https://ineed-coffee.github.io/posts/IQR-rule/)
        • 상자 아래 세로선: 극단치 경계 범위 내의 하위 0~25%에 해당하는 값
        • 상자 밑면: 하위 25% 위치의 값
        • 상자 안 가로선: 50% 위치의 값(중앙값)
        • 상자 윗면: 하위 75% 위치의 값
        • 상자 위 세로선: 극단치 경계 범위 내의 하위 75~100%에 해당하는 값
        • 상자 밖 표식: 1.5IQR을 벗어난 값
      2. 극단치 → 결측치로 대체: np.where((조건1 | 조건2), '참인 경우', '거짓인 경우') 함수 활용
      3. 결측치 제거: dropna(subset = ['제거할 변수명1', '제거할 변수명2', ...])
 

IQR Rule

:mag: Index

ineed-coffee.github.io

 

import pandas as pd
import seaborn as sns

mpg = pd.read_csv('mpg.csv')

sns.boxplot(data = mpg, y = 'hwy') # 상자 그림 생성

# IQR을 구하기 위한 Q1, Q3 값 계싼
pct25 = mpg['hwy'].quantile(.25) # 하위 25% 지점(Q1)의 값
pct75 = mpg['hwy'].quantile(.75) # 하위 75% 지점(Q3)의 값

# IQR 계산
IQR = pct75 - pct25

# 극단치 기준 생성
min_value = pct25 - 1.5 * IQR
max_value = pct75 + 1.5 * IQR
# 극단치 → 결측치로 대체
# np.where() 안에 조건을 여러가지 작성하는 경우, 각 조건을 괄호로 묶어야 함
mpg['hwy'] = np.where((mpg['hwy'] < min_value) | (mpg['hwy'] > max_value), np.nan, mpg['hwy'])

# 결측치 개수 확인
mpg['hwy'].isna().sum()

# 결측치 제거
mpg.dropna(subset = ['hwy'])

'데이터 분석 > Python' 카테고리의 다른 글

7-1. 한국복지패널 데이터 분석(1)  (0) 2026.07.23
6. 데이터 시각화(seaborn 패키지)  (0) 2026.07.20
4. 데이터 가공(전처리)  (0) 2026.07.16
3. 데이터 파악 및 수정  (0) 2026.07.15
2. DataFrame  (0) 2026.07.14