이미지 로딩 중...

이상치 탐지 및 처리 완벽 가이드 - 슬라이드 1/9
A

AI Generated

2025. 11. 25. · 7 Views

이상치 탐지 및 처리 완벽 가이드

데이터 속에 숨어있는 이상한 값들을 찾아내고 처리하는 방법을 배워봅니다. 실무에서 자주 마주치는 이상치 문제를 Python으로 해결하는 다양한 기법을 소개합니다.


목차

  1. 이상치란 무엇인가
  2. Z-Score 방법으로 이상치 탐지하기
  3. IQR 방법으로 이상치 탐지하기
  4. 이상치 처리 방법 - 제거와 대체
  5. 다변량 이상치 탐지 - Isolation Forest
  6. 시계열 데이터의 이상치 탐지
  7. 이상치 시각화 방법
  8. 도메인 지식 기반 이상치 탐지

1. 이상치란 무엇인가

시작하며

여러분이 쇼핑몰 데이터를 분석하고 있는데, 갑자기 한 고객의 구매 금액이 999억 원으로 나타났다면? 분명히 뭔가 잘못된 겁니다.

이렇게 다른 데이터들과 확연히 다른 값을 '이상치(Outlier)'라고 부릅니다. 이런 문제는 실제 개발 현장에서 정말 자주 발생합니다.

센서 오류, 입력 실수, 시스템 버그 등 다양한 원인으로 이상한 값들이 데이터에 섞여 들어옵니다. 이런 이상치를 그냥 두면 평균 계산이 왜곡되고, 머신러닝 모델의 성능이 떨어지며, 잘못된 비즈니스 의사결정으로 이어질 수 있습니다.

바로 이럴 때 필요한 것이 이상치 탐지입니다. 데이터 속에서 수상한 값들을 자동으로 찾아내고, 적절히 처리함으로써 데이터 품질을 높일 수 있습니다.

개요

간단히 말해서, 이상치는 전체 데이터의 패턴에서 크게 벗어난 값을 의미합니다. 마치 키 150cm~180cm 사이의 사람들 사이에 키 300cm인 사람이 있는 것과 같습니다.

왜 이상치를 찾아야 할까요? 첫째, 데이터 분석의 정확도를 높이기 위해서입니다.

둘째, 실제 오류나 사기 거래를 발견하기 위해서입니다. 예를 들어, 신용카드 사기 탐지 시스템에서는 평소와 다른 결제 패턴을 이상치로 감지하여 사기를 막습니다.

전통적인 방법으로는 데이터를 일일이 눈으로 확인했다면, 이제는 통계적 기법으로 자동화할 수 있습니다. Python의 pandas와 numpy를 사용하면 수백만 개의 데이터에서도 순식간에 이상치를 찾아낼 수 있습니다.

이상치 탐지의 핵심 특징은 세 가지입니다. 첫째, 객관적인 기준을 사용합니다(예: 평균에서 3표준편차 이상 벗어남).

둘째, 대용량 데이터에도 빠르게 적용할 수 있습니다. 셋째, 도메인 지식과 통계 기법을 결합하여 정확도를 높일 수 있습니다.

이러한 특징들이 실무에서 데이터 품질 관리를 가능하게 만듭니다.

코드 예제

import numpy as np
import pandas as pd

# 샘플 데이터 생성 (키 데이터)
heights = [160, 165, 170, 175, 168, 172, 169, 300, 155, 180]
df = pd.DataFrame({'height': heights})

# 기본 통계 확인
mean = df['height'].mean()  # 평균
std = df['height'].std()    # 표준편차

print(f"평균: {mean:.2f}cm")
print(f"표준편차: {std:.2f}cm")
print(f"최소값: {df['height'].min()}cm")
print(f"최대값: {df['height'].max()}cm")

설명

이것이 하는 일: 위 코드는 키 데이터에서 기본 통계량을 계산하여 이상치를 찾기 위한 첫 단계를 수행합니다. 첫 번째로, numpy와 pandas 라이브러리를 불러옵니다.

이 두 라이브러리는 데이터 분석의 필수 도구입니다. 그 다음 샘플 키 데이터를 리스트로 만들고 DataFrame으로 변환합니다.

여기서 300cm라는 명백한 이상치를 포함시켰습니다. 두 번째로, mean()과 std() 함수를 사용해 평균과 표준편차를 계산합니다.

평균은 모든 값을 더해서 개수로 나눈 것이고, 표준편차는 데이터가 평균으로부터 얼마나 퍼져있는지를 나타냅니다. 이 두 값이 이상치를 판단하는 기준이 됩니다.

세 번째로, min()과 max() 함수로 최소값과 최대값을 확인합니다. 최대값이 300cm로 나타나면 이것이 의심스러운 값임을 직감적으로 알 수 있습니다.

이렇게 기본 통계를 먼저 파악하는 것이 이상치 탐지의 시작입니다. 여러분이 이 코드를 사용하면 데이터의 전반적인 분포를 빠르게 파악하고, 어떤 값이 의심스러운지 감을 잡을 수 있습니다.

실무에서는 이런 기초 통계 분석을 항상 먼저 수행하여 데이터를 이해하고, 어떤 이상치 탐지 방법을 사용할지 결정합니다.

실전 팁

💡 describe() 메서드를 사용하면 평균, 표준편차, 최소/최대값을 한 번에 확인할 수 있어 편리합니다

💡 데이터를 처음 받았을 때는 항상 히스토그램이나 박스플롯으로 시각화해서 이상치를 눈으로 먼저 확인하세요

💡 이상치가 진짜 오류인지 아니면 의미 있는 극단값인지 도메인 전문가와 상의해야 합니다

💡 대용량 데이터에서는 샘플링해서 먼저 분석한 후 전체 데이터에 적용하면 시간을 절약할 수 있습니다

💡 결측치(NaN)가 있으면 통계 계산이 왜곡되므로 dropna()나 fillna()로 먼저 처리하세요


2. Z-Score 방법으로 이상치 탐지하기

시작하며

여러분이 온라인 쇼핑몰의 주문 금액 데이터를 분석하고 있는데, 1천만 개의 주문 중에서 이상한 금액을 어떻게 찾아낼 수 있을까요? 일일이 확인하는 것은 불가능합니다.

이런 문제를 해결하는 가장 대표적인 방법이 Z-Score입니다. Z-Score는 각 데이터 포인트가 평균으로부터 표준편차의 몇 배만큼 떨어져 있는지를 나타내는 점수입니다.

보통 Z-Score가 3 이상이거나 -3 이하면 이상치로 판단합니다. 바로 이럴 때 필요한 것이 Z-Score 방법입니다.

간단한 수식 하나로 모든 데이터를 자동으로 검사하여 의심스러운 값들을 골라낼 수 있습니다.

개요

간단히 말해서, Z-Score는 "이 값이 평균에서 얼마나 멀리 떨어져 있는가?"를 숫자로 표현한 것입니다. 계산 공식은 (값 - 평균) / 표준편차입니다.

왜 Z-Score가 유용할까요? 첫째, 서로 다른 단위의 데이터를 비교할 수 있습니다(예: 키와 몸무게를 동시에 분석).

둘째, 명확한 임계값을 설정할 수 있습니다(보통 ±3). 예를 들어, 전자상거래 사이트에서 평균 구매액이 5만 원인데 갑자기 500만 원짜리 주문이 들어오면 Z-Score가 매우 높게 나타나 사기 의심 주문으로 표시됩니다.

전통적인 방법으로는 "최댓값보다 크면 이상치"라는 단순한 기준을 사용했다면, 이제는 데이터의 분포를 고려한 통계적 방법을 사용합니다. 이렇게 하면 데이터의 특성에 맞춰 더 정확하게 이상치를 찾을 수 있습니다.

Z-Score 방법의 핵심 특징은 세 가지입니다. 첫째, 정규분포를 가정합니다(종 모양의 분포).

둘째, 계산이 매우 빠릅니다. 셋째, 해석이 직관적입니다(Z=3이면 상위 0.3%).

이러한 특징들이 실무에서 빠른 의사결정을 가능하게 합니다.

코드 예제

import numpy as np
import pandas as pd

# 주문 금액 데이터 (원 단위)
orders = [45000, 52000, 48000, 50000, 5000000, 47000, 51000, 49000]
df = pd.DataFrame({'amount': orders})

# Z-Score 계산
mean = df['amount'].mean()
std = df['amount'].std()
df['z_score'] = (df['amount'] - mean) / std

# 이상치 판별 (|Z-Score| > 3)
df['is_outlier'] = abs(df['z_score']) > 3

# 결과 출력
print(df)
print(f"\n이상치 개수: {df['is_outlier'].sum()}개")

설명

이것이 하는 일: 위 코드는 주문 금액 데이터에서 Z-Score를 계산하고, 통계적으로 이상한 주문을 자동으로 찾아냅니다. 첫 번째로, 주문 금액 데이터를 만듭니다.

여기서 500만 원짜리 주문이 명백한 이상치입니다. 나머지는 모두 4만~5만 원 사이의 정상적인 주문입니다.

DataFrame으로 변환하여 분석을 준비합니다. 두 번째로, Z-Score를 계산하는 핵심 공식을 적용합니다.

(값 - 평균) / 표준편차를 계산하여 새로운 컬럼 'z_score'에 저장합니다. 이렇게 하면 각 주문이 평균에서 얼마나 멀리 떨어져 있는지를 표준화된 점수로 알 수 있습니다.

500만 원짜리 주문은 Z-Score가 3을 훨씬 넘을 것입니다. 세 번째로, abs() 함수로 Z-Score의 절댓값을 구하고, 3보다 큰지 확인합니다.

이는 양쪽 극단값을 모두 잡기 위함입니다(너무 큰 값과 너무 작은 값). 결과를 'is_outlier' 컬럼에 True/False로 저장합니다.

마지막으로, 전체 결과를 출력하고 이상치가 몇 개인지 sum()으로 세어줍니다. sum()이 작동하는 이유는 True가 1로, False가 0으로 계산되기 때문입니다.

여러분이 이 코드를 사용하면 수백만 개의 거래에서도 순식간에 의심스러운 거래를 찾아낼 수 있습니다. 실무에서는 이렇게 찾은 이상치를 별도로 분류하여 담당자가 검토하거나, 자동으로 알림을 보내는 시스템을 구축합니다.

실전 팁

💡 데이터가 정규분포가 아니면 Z-Score 방법이 잘 작동하지 않으므로 먼저 히스토그램으로 분포를 확인하세요

💡 임계값 3 대신 2.5나 2를 사용하면 더 민감하게 이상치를 탐지할 수 있지만 정상값도 많이 걸립니다

💡 scipy.stats.zscore() 함수를 사용하면 더 간단하게 Z-Score를 계산할 수 있습니다

💡 이상치를 제거하기 전에 원본 데이터를 백업하거나 별도 컬럼으로 표시만 하세요(나중에 복구 가능)

💡 Z-Score 방법은 평균과 표준편차에 민감하므로 이상치가 너무 많으면 제대로 작동하지 않을 수 있습니다


3. IQR 방법으로 이상치 탐지하기

시작하며

여러분이 월급 데이터를 분석하고 있는데, CEO의 연봉 때문에 평균이 왜곡되어 있다면? Z-Score 방법은 평균과 표준편차를 사용하기 때문에 극단값의 영향을 많이 받습니다.

이런 문제를 해결하기 위해 통계학자들이 개발한 것이 IQR(Inter-Quartile Range, 사분위수 범위) 방법입니다. 이 방법은 중앙값을 기반으로 하기 때문에 극단값의 영향을 덜 받아 더 안정적입니다.

바로 이럴 때 필요한 것이 IQR 방법입니다. 박스플롯에서 보는 그 수염 밖의 점들이 바로 IQR 방법으로 찾아낸 이상치입니다.

개요

간단히 말해서, IQR은 데이터를 4등분했을 때 가운데 50%(25%~75%)의 범위를 의미합니다. Q1(25번째 백분위수)과 Q3(75번째 백분위수)의 차이가 IQR입니다.

왜 IQR 방법이 필요한가요? 첫째, 평균이 아닌 중앙값 기반이라 극단값에 강합니다.

둘째, 데이터 분포의 형태를 가정하지 않습니다(비정규분포에도 사용 가능). 예를 들어, 부동산 가격 데이터처럼 한쪽으로 치우친 분포에서도 잘 작동합니다.

고가 주택 몇 채 때문에 평균이 왜곡되어도 IQR은 영향을 덜 받습니다. 전통적인 방법으로는 평균 중심의 Z-Score를 사용했다면, 이제는 중앙값 중심의 IQR을 상황에 따라 선택할 수 있습니다.

특히 금융, 부동산, 의료비 같은 한쪽으로 치우친 데이터에서 IQR이 더 정확합니다. IQR 방법의 핵심 특징은 세 가지입니다.

첫째, 극단값에 강건합니다(robust). 둘째, 계산이 직관적입니다(상위 25%와 하위 25%를 제외).

셋째, 시각화(박스플롯)와 연결이 쉽습니다. 이러한 특징들이 실무에서 데이터 클리닝을 더 안전하게 만듭니다.

코드 예제

import numpy as np
import pandas as pd

# 월급 데이터 (만원 단위)
salaries = [300, 320, 310, 330, 8000, 315, 325, 305, 335, 318]
df = pd.DataFrame({'salary': salaries})

# Q1, Q3, IQR 계산
Q1 = df['salary'].quantile(0.25)  # 25번째 백분위수
Q3 = df['salary'].quantile(0.75)  # 75번째 백분위수
IQR = Q3 - Q1

# 이상치 경계값 계산
lower_bound = Q1 - 1.5 * IQR  # 하한선
upper_bound = Q3 + 1.5 * IQR  # 상한선

# 이상치 판별
df['is_outlier'] = (df['salary'] < lower_bound) | (df['salary'] > upper_bound)

print(f"Q1: {Q1}, Q3: {Q3}, IQR: {IQR}")
print(f"정상 범위: {lower_bound:.2f} ~ {upper_bound:.2f}")
print(df)

설명

이것이 하는 일: 위 코드는 월급 데이터에서 IQR을 계산하고, 통계적으로 정상 범위를 벗어난 급여를 찾아냅니다. 첫 번째로, 월급 데이터를 만듭니다.

대부분 300~335만 원 사이인데, 8000만 원이라는 CEO급 연봉이 하나 섞여 있습니다. 이런 극단값이 있어도 IQR 방법은 잘 작동합니다.

두 번째로, quantile() 함수로 Q1(25번째 백분위수)과 Q3(75번째 백분위수)를 구합니다. 0.25를 넣으면 데이터를 정렬했을 때 25% 위치의 값을 반환합니다.

Q3 - Q1이 바로 IQR입니다. 이 IQR은 데이터의 중간 50%가 얼마나 퍼져있는지를 나타냅니다.

세 번째로, 이상치 경계를 계산합니다. 통계학적으로 Q1 - 1.5×IQR보다 작거나 Q3 + 1.5×IQR보다 크면 이상치로 봅니다.

여기서 1.5는 경험적으로 정해진 상수로, 정규분포에서 약 99.3%의 데이터를 포함하도록 설계되었습니다. 이 경계값을 벗어나면 정말 극단적인 값입니다.

네 번째로, 각 급여가 경계 밖에 있는지 확인합니다. | 연산자는 OR을 의미해서, 하한선보다 작거나 상한선보다 크면 True가 됩니다.

8000만 원은 상한선을 훨씬 넘어서므로 이상치로 표시됩니다. 여러분이 이 코드를 사용하면 극단값이 많은 데이터에서도 안정적으로 이상치를 찾을 수 있습니다.

실무에서는 급여, 부동산 가격, 의료비, 보험금 같은 한쪽으로 치우친 데이터에 IQR 방법을 많이 사용합니다.

실전 팁

💡 1.5 대신 3을 사용하면 더 극단적인 이상치만 잡을 수 있고(덜 민감), 1을 사용하면 더 많은 이상치를 잡습니다(더 민감)

💡 IQR 방법은 Z-Score와 달리 데이터 분포를 가정하지 않으므로 어떤 데이터에도 안전하게 적용할 수 있습니다

💡 박스플롯을 그리면 시각적으로 이상치를 확인할 수 있어서 결과를 보고서에 포함하기 좋습니다

💡 여러 컬럼에 동일한 방법을 적용할 때는 함수로 만들어서 재사용하세요

💡 이상치를 제거하는 대신 윈저화(winsorization)를 고려해보세요 - 이상치를 경계값으로 대체하는 방법입니다


4. 이상치 처리 방법 - 제거와 대체

시작하며

여러분이 이상치를 찾았다면 이제 어떻게 해야 할까요? 그냥 삭제해버리면 될까요?

아니면 다른 값으로 바꿔야 할까요? 이상치를 어떻게 처리하느냐에 따라 분석 결과가 완전히 달라질 수 있습니다.

잘못 처리하면 중요한 정보를 잃거나 데이터를 왜곡할 수 있습니다. 예를 들어, 사기 거래 탐지 시스템에서 이상치를 모두 삭제하면 정작 사기 거래를 놓칠 수 있습니다.

바로 이럴 때 필요한 것이 상황에 맞는 이상치 처리 전략입니다. 제거, 대체, 캡핑 등 여러 방법 중에서 비즈니스 목적에 맞는 방법을 선택해야 합니다.

개요

간단히 말해서, 이상치 처리는 크게 세 가지 방법이 있습니다. 제거(deletion), 대체(imputation), 캡핑(capping)입니다.

왜 이상치 처리 방법이 중요한가요? 첫째, 데이터의 특성과 분석 목적에 따라 적합한 방법이 다릅니다.

둘째, 잘못 처리하면 편향(bias)이 생깁니다. 예를 들어, 머신러닝 모델을 학습시킬 때는 이상치를 제거하는 것이 좋지만, 이상 거래 탐지가 목적이라면 이상치가 바로 찾고자 하는 대상입니다.

전통적인 방법으로는 무조건 이상치를 삭제했다면, 이제는 도메인 지식을 활용하여 상황별로 다른 전략을 사용합니다. 때로는 이상치를 그대로 두는 것이 최선일 수도 있습니다.

이상치 처리의 핵심 원칙은 세 가지입니다. 첫째, 이상치가 오류인지 실제 극단값인지 먼저 확인합니다.

둘째, 원본 데이터는 항상 보존합니다. 셋째, 처리 방법을 문서화합니다(재현 가능성).

이러한 원칙들이 실무에서 신뢰할 수 있는 분석을 가능하게 합니다.

코드 예제

import numpy as np
import pandas as pd

# 이상치가 포함된 데이터
data = [10, 12, 11, 13, 100, 12, 14, 11, 13, 12]
df = pd.DataFrame({'value': data})

# 방법 1: 제거 (Deletion)
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
df_removed = df[(df['value'] >= lower) & (df['value'] <= upper)]

# 방법 2: 평균으로 대체 (Mean Imputation)
df_mean = df.copy()
is_outlier = (df_mean['value'] < lower) | (df_mean['value'] > upper)
df_mean.loc[is_outlier, 'value'] = df_mean['value'].mean()

# 방법 3: 경계값으로 캡핑 (Capping)
df_capped = df.copy()
df_capped['value'] = df_capped['value'].clip(lower=lower, upper=upper)

print(f"원본: {len(df)}개, 제거 후: {len(df_removed)}개")
print(f"평균 대체: {df_mean['value'].tolist()}")
print(f"캡핑: {df_capped['value'].tolist()}")

설명

이것이 하는 일: 위 코드는 동일한 데이터에 세 가지 다른 이상치 처리 방법을 적용하여 결과를 비교합니다. 첫 번째 방법은 제거입니다.

IQR을 계산해서 경계값을 구하고, 그 범위 안의 데이터만 남깁니다. 불리언 인덱싱을 사용해서 조건에 맞는 행만 선택합니다.

&는 AND 연산자로, 양쪽 조건을 모두 만족해야 합니다. 이 방법은 가장 단순하지만 데이터를 잃는다는 단점이 있습니다.

두 번째 방법은 평균으로 대체하는 것입니다. copy()로 원본을 복사한 후, 이상치 위치를 찾아서 전체 평균값으로 바꿉니다.

loc를 사용하면 조건에 맞는 위치에만 값을 할당할 수 있습니다. 이 방법은 데이터 개수는 유지하지만, 평균이 실제보다 더 평균에 가까워지는 효과가 있습니다.

세 번째 방법은 캡핑입니다. clip() 함수는 값을 특정 범위로 제한합니다.

하한선보다 작으면 하한선으로, 상한선보다 크면 상한선으로 자동 변환됩니다. 100이라는 이상치가 상한선 값으로 바뀝니다.

이 방법은 극단값의 영향을 줄이면서도 어느 정도 정보를 유지합니다. 마지막으로, 세 가지 방법의 결과를 출력하여 비교합니다.

len()으로 개수를 확인하고, tolist()로 실제 값들을 보여줍니다. 각 방법이 데이터를 어떻게 변형시켰는지 명확히 알 수 있습니다.

여러분이 이 코드를 사용하면 상황에 맞는 최적의 이상치 처리 방법을 선택할 수 있습니다. 실무에서는 여러 방법을 시도해보고 모델 성능이나 분석 결과가 가장 좋은 방법을 선택합니다.

또한 A/B 테스트로 어떤 방법이 실제 비즈니스 지표를 개선하는지 확인하기도 합니다.

실전 팁

💡 제거 방법은 데이터가 충분히 많을 때만 사용하세요 - 작은 데이터셋에서는 정보 손실이 큽니다

💡 평균 대체보다는 중앙값 대체가 더 안전합니다 - median()을 사용하세요

💡 캡핑은 머신러닝 모델 학습 전에 많이 사용되는 방법입니다 - 이상치의 영향을 줄이면서 데이터를 보존합니다

💡 시계열 데이터에서는 이전/이후 값의 평균으로 대체하는 것이 더 합리적일 수 있습니다

💡 처리 전후의 분포를 히스토그램으로 비교하여 데이터가 너무 왜곡되지 않았는지 확인하세요


5. 다변량 이상치 탐지 - Isolation Forest

시작하며

여러분이 신용카드 사기를 탐지하려고 하는데, 거래 금액만 보면 정상인데 거래 시간, 위치, 패턴을 종합하면 수상한 경우가 있습니다. 이럴 때 어떻게 해야 할까요?

지금까지 배운 Z-Score나 IQR은 한 번에 하나의 변수만 봅니다. 하지만 실제 세상의 이상치는 여러 변수의 조합으로 나타나는 경우가 많습니다.

정상적인 금액에, 정상적인 시간대인데도 위치와 패턴이 이상하면 사기일 수 있습니다. 바로 이럴 때 필요한 것이 다변량 이상치 탐지입니다.

그 중에서도 Isolation Forest는 최근 가장 인기 있는 머신러닝 기반 이상치 탐지 알고리즘입니다.

개요

간단히 말해서, Isolation Forest는 "이상치는 고립시키기(isolate) 쉽다"는 아이디어에 기반합니다. 정상 데이터는 비슷한 값들이 모여있어서 분리하려면 많은 단계가 필요하지만, 이상치는 멀리 떨어져 있어서 몇 번만에 고립시킬 수 있습니다.

왜 Isolation Forest가 필요한가요? 첫째, 여러 변수를 동시에 고려합니다.

둘째, 정규분포를 가정하지 않아 어떤 데이터에도 사용 가능합니다. 셋째, 대용량 데이터에도 빠릅니다.

예를 들어, 네트워크 침입 탐지 시스템에서는 패킷 크기, 빈도, 출발지, 목적지 등 수십 개 변수를 동시에 분석하여 이상한 접속을 찾아냅니다. 전통적인 방법으로는 각 변수를 따로따로 분석했다면, 이제는 변수 간의 관계까지 고려하여 더 정교하게 이상치를 찾습니다.

2차원, 3차원, 심지어 수백 차원의 데이터에서도 작동합니다. Isolation Forest의 핵심 특징은 세 가지입니다.

첫째, 비지도 학습입니다(라벨 데이터 불필요). 둘째, 이상치가 소수라고 가정합니다(contamination 파라미터).

셋째, 랜덤 포레스트와 비슷한 앙상블 방법입니다. 이러한 특징들이 실무에서 복잡한 이상치 패턴을 찾아내게 합니다.

코드 예제

from sklearn.ensemble import IsolationForest
import numpy as np
import pandas as pd

# 2차원 데이터 생성 (거래 금액과 거래 횟수)
np.random.seed(42)
normal_data = np.random.randn(100, 2) * 10 + [50, 5]  # 정상 거래
outlier_data = np.array([[100, 20], [90, 18], [95, 22]])  # 이상 거래
data = np.vstack([normal_data, outlier_data])
df = pd.DataFrame(data, columns=['amount', 'frequency'])

# Isolation Forest 모델 학습
model = IsolationForest(contamination=0.03, random_state=42)  # 3% 이상치 예상
df['prediction'] = model.fit_predict(df[['amount', 'frequency']])

# -1이 이상치, 1이 정상
df['is_outlier'] = df['prediction'] == -1

print(f"탐지된 이상치: {df['is_outlier'].sum()}개")
print(df[df['is_outlier']])

설명

이것이 하는 일: 위 코드는 거래 금액과 거래 횟수를 동시에 분석하여 수상한 거래 패턴을 머신러닝으로 찾아냅니다. 첫 번째로, 샘플 데이터를 생성합니다.

정규분포를 따르는 정상 거래 100개를 만들고(평균 금액 50만원, 횟수 5회), 명백히 다른 패턴의 이상 거래 3개를 추가합니다. vstack()으로 세로로 합칩니다.

random.seed()는 결과를 재현 가능하게 만듭니다. 두 번째로, IsolationForest 모델을 만듭니다.

contamination=0.03은 전체 데이터의 약 3%가 이상치라고 알려주는 것입니다. 이 값을 조정하면 민감도를 바꿀 수 있습니다.

random_state는 결과를 일정하게 유지하기 위한 시드값입니다. 세 번째로, fit_predict()로 학습과 예측을 동시에 수행합니다.

이 함수는 데이터의 패턴을 학습하고, 각 데이터 포인트가 이상치인지 아닌지를 판단합니다. 1은 정상, -1은 이상치를 의미합니다.

내부적으로는 랜덤하게 변수와 값을 선택해서 데이터를 분할하고, 몇 번 만에 고립되는지를 계산합니다. 네 번째로, 결과를 해석하기 쉽게 True/False로 변환합니다.

prediction이 -1인 행만 골라서 출력하면 어떤 거래가 이상하다고 판단되었는지 볼 수 있습니다. 대부분 우리가 일부러 만든 이상 거래 3개가 잡힐 것입니다.

여러분이 이 코드를 사용하면 복잡한 다변량 데이터에서도 쉽게 이상치를 찾을 수 있습니다. 실무에서는 수십 개의 변수를 동시에 넣어서 분석합니다.

신용카드 사기 탐지, 네트워크 침입 탐지, 제조 불량 탐지, 의료 이상 진단 등 다양한 분야에서 활용됩니다.

실전 팁

💡 contamination 값은 도메인 지식에 기반하여 설정하세요 - 보통 0.01~0.1 사이를 사용합니다

💡 변수의 스케일이 다르면 StandardScaler로 정규화한 후 사용하는 것이 좋습니다

💡 n_estimators(트리 개수)를 늘리면 더 안정적이지만 느려집니다 - 기본값 100이 대부분 충분합니다

💡 decision_function()을 사용하면 이상치 점수를 얻을 수 있어서 순위를 매길 수 있습니다

💡 DBSCAN, Local Outlier Factor 같은 다른 알고리즘과 비교해보고 데이터에 가장 잘 맞는 것을 선택하세요


6. 시계열 데이터의 이상치 탐지

시작하며

여러분이 서버 CPU 사용률을 모니터링하고 있는데, 갑자기 사용률이 튀는 순간이 있습니다. 이게 진짜 문제일까요, 아니면 정상적인 피크 시간일까요?

시계열 데이터의 이상치는 일반 데이터와 다릅니다. 시간의 흐름에 따라 데이터가 변하고, 트렌드와 계절성이 있으며, 이전 값들이 다음 값에 영향을 미칩니다.

따라서 단순히 값의 크기만 보면 안 되고, 시간적 맥락을 고려해야 합니다. 바로 이럴 때 필요한 것이 시계열 이상치 탐지입니다.

이동 평균, 표준편차, 그리고 시계열 분해를 활용하여 시간적 패턴을 고려한 이상치를 찾습니다.

개요

간단히 말해서, 시계열 이상치 탐지는 과거 패턴과 비교하여 "이 시점에서는 이상한" 값을 찾는 것입니다. 같은 값이라도 상황에 따라 정상일 수도, 이상일 수도 있습니다.

왜 시계열 이상치 탐지가 중요한가요? 첫째, IT 인프라 모니터링에서 장애를 빨리 발견할 수 있습니다.

둘째, 주식이나 매출 같은 비즈니스 지표의 이상 징후를 포착할 수 있습니다. 예를 들어, 웹사이트 트래픽이 평소 같은 시간대보다 50% 감소하면 문제가 있는 것일 수 있습니다.

절댓값은 높아도 평소보다 낮으면 이상입니다. 전통적인 방법으로는 고정된 임계값을 설정했다면(CPU 80% 이상), 이제는 동적 임계값을 사용합니다.

시간대별, 요일별로 다른 기준을 적용하여 더 정확하게 이상을 감지합니다. 시계열 이상치 탐지의 핵심 특징은 세 가지입니다.

첫째, 이동 평균과 이동 표준편차를 사용합니다. 둘째, 트렌드와 계절성을 제거합니다(seasonal decomposition).

셋째, 급격한 변화율(delta)도 함께 봅니다. 이러한 특징들이 실무에서 시스템 장애를 조기에 발견하게 합니다.

코드 예제

import pandas as pd
import numpy as np

# 시계열 데이터 생성 (서버 CPU 사용률)
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='h')
cpu_usage = 50 + np.random.randn(100) * 5  # 평균 50% ± 5%
cpu_usage[50] = 95  # 이상치 삽입
df = pd.DataFrame({'timestamp': dates, 'cpu': cpu_usage})

# 이동 평균과 이동 표준편차 계산 (window=10)
df['rolling_mean'] = df['cpu'].rolling(window=10).mean()
df['rolling_std'] = df['cpu'].rolling(window=10).std()

# 동적 임계값 계산 (mean ± 3 * std)
df['upper_bound'] = df['rolling_mean'] + 3 * df['rolling_std']
df['lower_bound'] = df['rolling_mean'] - 3 * df['rolling_std']

# 이상치 판별
df['is_outlier'] = (df['cpu'] > df['upper_bound']) | (df['cpu'] < df['lower_bound'])

print(df[df['is_outlier']][['timestamp', 'cpu', 'rolling_mean', 'upper_bound']])

설명

이것이 하는 일: 위 코드는 서버 CPU 사용률 시계열 데이터에서 과거 패턴을 고려하여 비정상적인 순간을 찾아냅니다. 첫 번째로, 시계열 데이터를 생성합니다.

date_range()로 100시간의 타임스탬프를 만들고, 평균 50%에 약간의 노이즈가 있는 정상 데이터를 생성합니다. 50번째 시점에 일부러 95%라는 이상치를 삽입하여 탐지 테스트를 준비합니다.

두 번째로, rolling() 함수로 이동 평균과 이동 표준편차를 계산합니다. window=10은 최근 10시간의 데이터를 기준으로 계산한다는 의미입니다.

이렇게 하면 고정된 값이 아니라 시간에 따라 변하는 기준을 만들 수 있습니다. 예를 들어, 낮 시간대는 사용률이 높고 밤에는 낮은 패턴을 자동으로 반영합니다.

세 번째로, 동적 임계값을 계산합니다. 각 시점의 이동 평균에 3배 표준편차를 더하고 빼서 상한선과 하한선을 만듭니다.

이 경계는 시간에 따라 계속 변합니다. 이것이 고정 임계값과의 큰 차이점입니다.

네 번째로, 실제 CPU 값이 동적 경계를 벗어나는지 확인합니다. 우리가 삽입한 95%는 그 시점의 이동 평균과 표준편차를 고려했을 때 상한선을 넘어서므로 이상치로 탐지됩니다.

결과를 출력하면 어느 시점에 얼마나 벗어났는지 볼 수 있습니다. 여러분이 이 코드를 사용하면 서버 모니터링, 주가 감시, 제조 센서 데이터 분석 등 시간 순서가 있는 모든 데이터에서 이상을 탐지할 수 있습니다.

실무에서는 이를 자동화하여 이상치가 발견되면 즉시 알림을 보내는 시스템을 구축합니다. AWS CloudWatch, Datadog, Grafana 같은 모니터링 도구들이 모두 이런 원리를 사용합니다.

실전 팁

💡 window 크기는 데이터의 특성에 맞게 조정하세요 - 너무 작으면 노이즈에 민감하고, 너무 크면 변화를 놓칩니다

💡 초기 window 크기만큼은 NaN이 나오므로 fillna()나 dropna()로 처리해야 합니다

💡 계절성이 있는 데이터(예: 주말/평일 패턴)는 seasonal_decompose()로 트렌드를 분리한 후 잔차(residual)에서 이상치를 찾으세요

💡 급격한 변화를 감지하려면 diff()로 변화율을 계산하고 그것의 이상치를 찾는 것도 효과적입니다

💡 실시간 모니터링에서는 ewm()(지수 가중 이동 평균)을 사용하면 최근 데이터에 더 높은 가중치를 줄 수 있습니다


7. 이상치 시각화 방법

시작하며

여러분이 이상치 분석 결과를 상사나 고객에게 보고해야 하는데, 숫자만 나열하면 이해하기 어렵습니다. 어떻게 하면 직관적으로 보여줄 수 있을까요?

데이터 분석에서 시각화는 단순히 예쁘게 보이기 위한 것이 아닙니다. 복잡한 패턴을 한눈에 파악하고, 이상치가 어디에 어떻게 분포하는지 명확히 보여줍니다.

특히 이해관계자들이 기술적 배경이 없을 때 시각화는 필수입니다. 바로 이럴 때 필요한 것이 이상치 시각화입니다.

박스플롯, 산점도, 히스토그램 등 다양한 차트를 활용하여 이상치를 효과적으로 표현합니다.

개요

간단히 말해서, 이상치 시각화는 데이터와 이상치를 그래프로 표현하여 패턴과 문제점을 직관적으로 이해하게 만드는 것입니다. 왜 이상치 시각화가 중요한가요?

첫째, 탐색적 데이터 분석(EDA)에서 데이터의 분포를 빠르게 파악할 수 있습니다. 둘째, 이상치 탐지 알고리즘의 결과를 검증할 수 있습니다.

셋째, 비기술자에게도 쉽게 설명할 수 있습니다. 예를 들어, 박스플롯 하나면 "이 점들이 수염 밖에 있어서 이상치입니다"라고 간단히 설명할 수 있습니다.

전통적인 방법으로는 엑셀 표로 숫자를 나열했다면, 이제는 matplotlib, seaborn, plotly 같은 라이브러리로 인터랙티브한 시각화를 만듭니다. 마우스를 올리면 상세 정보가 나오고, 확대/축소도 가능합니다.

이상치 시각화의 핵심 기법은 세 가지입니다. 첫째, 박스플롯으로 분포와 이상치를 동시에 표현합니다.

둘째, 산점도에 색상을 달리하여 정상/이상을 구분합니다. 셋째, 다차원 데이터는 차원 축소(PCA)로 2D 평면에 투영합니다.

이러한 기법들이 실무에서 데이터 인사이트를 효과적으로 전달하게 합니다.

코드 예제

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

# 데이터 생성
np.random.seed(42)
normal = np.random.randn(100) * 10 + 50
outliers = [0, 5, 95, 100]
data = np.concatenate([normal, outliers])
df = pd.DataFrame({'value': data})

# IQR로 이상치 판별
Q1, Q3 = df['value'].quantile([0.25, 0.75])
IQR = Q3 - Q1
lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
df['is_outlier'] = (df['value'] < lower) | (df['value'] > upper)

# 시각화: 박스플롯과 산점도
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))

# 박스플롯
ax1.boxplot(df['value'], vert=False)
ax1.set_title('Box Plot')
ax1.set_xlabel('Value')

# 산점도
colors = ['red' if x else 'blue' for x in df['is_outlier']]
ax2.scatter(range(len(df)), df['value'], c=colors, alpha=0.6)
ax2.set_title('Scatter Plot (Red = Outlier)')
ax2.set_xlabel('Index')
ax2.set_ylabel('Value')

plt.tight_layout()
plt.savefig('outlier_visualization.png')
print("시각화 완료: outlier_visualization.png")

설명

이것이 하는 일: 위 코드는 동일한 데이터를 두 가지 방식으로 시각화하여 이상치를 다각도로 보여줍니다. 첫 번째로, 정규분포를 따르는 정상 데이터 100개와 명백한 이상치 4개를 만듭니다.

concatenate()로 합치고 DataFrame으로 변환합니다. 그 다음 IQR 방법으로 이상치를 판별하여 is_outlier 컬럼을 추가합니다.

두 번째로, matplotlib의 subplots()로 1행 2열의 서브플롯을 만듭니다. figsize로 전체 크기를 조정하고, 각 서브플롯에 다른 차트를 그립니다.

ax1, ax2는 각각 첫 번째, 두 번째 서브플롯을 가리킵니다. 세 번째로, 박스플롯을 그립니다.

boxplot() 함수는 자동으로 Q1, Q3, 중앙값, 수염, 이상치를 표시합니다. vert=False로 가로 방향으로 그립니다.

박스 밖의 점들이 바로 이상치입니다. 이 한 장의 그림으로 데이터 분포를 완벽히 파악할 수 있습니다.

네 번째로, 산점도를 그립니다. is_outlier 값에 따라 빨간색(이상치)과 파란색(정상)으로 색을 다르게 합니다.

리스트 컴프리헨션으로 색상 리스트를 만들고, c 파라미터로 전달합니다. alpha=0.6으로 투명도를 주면 겹치는 점들을 더 잘 볼 수 있습니다.

마지막으로, tight_layout()으로 서브플롯 간 간격을 자동 조정하고, savefig()로 파일로 저장합니다. 화면에 표시하려면 plt.show()를 추가하면 됩니다.

여러분이 이 코드를 사용하면 보고서나 프레젠테이션에 바로 사용할 수 있는 전문적인 시각화를 만들 수 있습니다. 실무에서는 seaborn을 사용하면 더 예쁜 차트를, plotly를 사용하면 인터랙티브한 차트를 만들 수 있습니다.

대시보드를 만들 때는 Streamlit이나 Dash 같은 프레임워크와 결합하여 실시간 모니터링 화면을 구축합니다.

실전 팁

💡 seaborn을 사용하면 sns.boxplot()으로 더 예쁜 박스플롯을 그릴 수 있고, 여러 그룹을 한 번에 비교할 수 있습니다

💡 2차원 산점도에서는 plt.axhline()과 plt.axvline()으로 임계선을 그어주면 이상치 경계가 명확해집니다

💡 대용량 데이터는 hexbin plot이나 2D 히스토그램을 사용하면 점이 겹치는 문제를 해결할 수 있습니다

💡 plotly를 사용하면 마우스 오버 시 상세 정보를 표시하고, 줌 인/아웃이 가능한 인터랙티브 차트를 만들 수 있습니다

💡 다변량 데이터는 pairplot()으로 모든 변수 쌍의 관계를 한눈에 볼 수 있어 유용합니다


8. 도메인 지식 기반 이상치 탐지

시작하며

여러분이 전자상거래 사이트에서 상품 리뷰 평점 데이터를 분석하고 있습니다. 통계적으로는 정상인데, 비즈니스 관점에서 이상한 경우가 있을까요?

예를 들어, 한 사용자가 100개의 5점 리뷰를 하루에 남겼다면? 통계적 이상치는 아니지만 명백히 조작입니다.

또는 마이너스 재고가 있다면? 데이터 타입은 맞지만 비즈니스 로직상 불가능합니다.

바로 이럴 때 필요한 것이 도메인 지식 기반 이상치 탐지입니다. 통계만으로는 부족하고, 해당 분야의 비즈니스 규칙과 상식을 코드로 구현해야 합니다.

개요

간단히 말해서, 도메인 지식 기반 이상치 탐지는 "비즈니스적으로 말이 안 되는" 데이터를 찾는 것입니다. 통계와 머신러닝을 넘어서 업계 지식이 필요합니다.

왜 도메인 지식이 중요한가요? 첫째, 통계 알고리즘은 맥락을 모릅니다.

둘째, 비즈니스 규칙 위반을 자동 탐지할 수 있습니다. 셋째, 데이터 품질 검증에 필수적입니다.

예를 들어, 의료 데이터에서 150kg 체중은 통계적으로는 이상치지만 가능한 값입니다. 하지만 신생아 체중이 150kg이면 명백한 오류입니다.

나이와 체중의 관계를 아는 도메인 지식이 필요합니다. 전통적인 방법으로는 수동으로 하나하나 검토했다면, 이제는 비즈니스 규칙을 코드로 작성하여 자동화합니다.

if-else 문이나 사용자 정의 함수로 복잡한 비즈니스 로직을 구현합니다. 도메인 지식 기반 탐지의 핵심 원칙은 세 가지입니다.

첫째, 비즈니스 전문가와 협업합니다. 둘째, 규칙을 명시적으로 문서화합니다.

셋째, 규칙은 계속 업데이트됩니다(진화). 이러한 원칙들이 실무에서 데이터 품질을 실질적으로 향상시킵니다.

코드 예제

import pandas as pd
import numpy as np

# 전자상거래 주문 데이터
orders = pd.DataFrame({
    'order_id': [1, 2, 3, 4, 5],
    'user_id': [101, 102, 103, 104, 105],
    'amount': [50000, -10000, 0, 1000000000, 75000],
    'quantity': [2, 3, 0, 999, 5],
    'age': [25, -5, 200, 30, 28]
})

# 도메인 규칙 기반 이상치 탐지
def detect_business_anomalies(df):
    anomalies = []

    # 규칙 1: 음수 금액은 불가능
    if (df['amount'] < 0).any():
        anomalies.append("음수 금액 발견")

    # 규칙 2: 주문 수량이 0이면 이상
    if (df['quantity'] <= 0).any():
        anomalies.append("0 이하 수량 발견")

    # 규칙 3: 10억 초과 주문은 의심 (사기 가능성)
    if (df['amount'] > 1000000000).any():
        anomalies.append("비정상 고액 주문 발견")

    # 규칙 4: 나이는 0~120 범위여야 함
    if ((df['age'] < 0) | (df['age'] > 120)).any():
        anomalies.append("비정상 나이 발견")

    return anomalies

# 각 주문별 상세 체크
orders['is_valid'] = True
orders.loc[orders['amount'] < 0, 'is_valid'] = False
orders.loc[orders['quantity'] <= 0, 'is_valid'] = False
orders.loc[orders['age'] < 0, 'is_valid'] = False

anomalies = detect_business_anomalies(orders)
print("발견된 이상 패턴:", anomalies)
print("\n유효하지 않은 주문:\n", orders[~orders['is_valid']])

설명

이것이 하는 일: 위 코드는 전자상거래 주문 데이터에서 비즈니스 상식에 어긋나는 이상한 값들을 자동으로 찾아냅니다. 첫 번째로, 샘플 주문 데이터를 만듭니다.

일부러 여러 종류의 비즈니스 규칙 위반을 포함시켰습니다. 음수 금액, 0 수량, 10억 초과 금액, 음수 나이 등입니다.

이런 값들은 통계적으로는 단순히 극단값일 뿐이지만, 비즈니스적으로는 말이 안 됩니다. 두 번째로, detect_business_anomalies() 함수를 정의합니다.

이 함수는 여러 비즈니스 규칙을 체크하고, 위반 사항을 문자열 리스트로 반환합니다. any() 함수는 하나라도 True가 있으면 True를 반환하므로, 조건에 맞는 행이 하나라도 있으면 해당 이상 패턴이 발견된 것입니다.

세 번째로, 각 규칙을 구체적으로 구현합니다. 규칙 1은 음수 금액을 체크하고(환불은 별도 테이블에 있어야 함), 규칙 2는 수량이 0 이하인지 확인하며(주문이면 최소 1개), 규칙 3은 비정상 고액 결제를 잡고(사기 가능성), 규칙 4는 나이의 물리적 범위를 체크합니다.

이런 규칙들은 도메인 전문가와의 협의를 통해 정해집니다. 네 번째로, 주문별로 유효성을 표시합니다.

is_valid 컬럼을 만들어 기본값을 True로 하고, 각 규칙 위반 시 False로 바꿉니다. loc를 사용하면 조건에 맞는 행만 선택적으로 수정할 수 있습니다.

~ 연산자는 NOT이므로, ~orders['is_valid']는 유효하지 않은 주문만 선택합니다. 여러분이 이 코드를 사용하면 데이터 입력 시점에 실시간으로 검증하거나, 배치 처리로 전체 데이터를 정기적으로 점검할 수 있습니다.

실무에서는 이런 규칙을 데이터베이스의 제약조건(constraint)이나 API의 유효성 검사(validation)로 구현하여 애초에 잘못된 데이터가 들어오지 못하게 막습니다. Pydantic, Marshmallow 같은 라이브러리를 사용하면 더 체계적으로 관리할 수 있습니다.

실전 팁

💡 비즈니스 규칙을 별도의 설정 파일(JSON, YAML)로 관리하면 코드 수정 없이 규칙을 변경할 수 있습니다

💡 Great Expectations 라이브러리를 사용하면 데이터 품질 테스트를 체계적으로 작성하고 문서화할 수 있습니다

💡 규칙 위반 시 단순히 제거하지 말고 별도 테이블에 로깅하여 나중에 분석하고 규칙을 개선하세요

💡 도메인 전문가와 정기적으로 만나 새로운 규칙을 추가하고 오래된 규칙은 업데이트하세요

💡 A/B 테스트로 규칙의 엄격함을 조정하여 너무 많은 False Positive가 나오지 않도록 최적화하세요


#Python#Outlier Detection#IQR#Z-Score#Data Cleaning#Data Science

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

범주형 변수 시각화 완벽 가이드 Bar Chart와 Count Plot

데이터 분석에서 가장 기본이 되는 범주형 변수 시각화 방법을 알아봅니다. Matplotlib의 Bar Chart부터 Seaborn의 Count Plot까지, 실무에서 바로 활용할 수 있는 시각화 기법을 배워봅니다.

이변량 분석 완벽 가이드: 변수 간 관계 탐색

두 변수 사이의 관계를 분석하는 이변량 분석의 핵심 개념과 기법을 배웁니다. 상관관계, 산점도, 교차분석 등 데이터 분석의 필수 도구들을 실습과 함께 익혀봅시다.

단변량 분석 분포 시각화 완벽 가이드

데이터 분석의 첫걸음인 단변량 분석과 분포 시각화를 배웁니다. 히스토그램, 박스플롯, 밀도 그래프 등 다양한 시각화 방법을 초보자도 쉽게 이해할 수 있도록 설명합니다.

데이터 타입 변환 및 정규화 완벽 가이드

데이터 분석과 머신러닝에서 가장 기초가 되는 데이터 타입 변환과 정규화 기법을 배워봅니다. 실무에서 자주 마주치는 데이터 전처리 문제를 Python으로 쉽게 해결하는 방법을 알려드립니다.

결측치 처리 전략 완벽 가이드

데이터 분석에서 가장 먼저 만나는 문제, 결측치! 삭제부터 고급 대체 기법까지, 실무에서 바로 쓸 수 있는 결측치 처리 전략을 초급자도 이해하기 쉽게 알려드립니다. 데이터의 품질을 높이고 정확한 분석 결과를 얻어보세요.