본문 바로가기
파이썬 - 분석 라이브러리

[파이썬] 데이터 이상치 확인 | Matplotlib 박스플롯 시각화, subplots

by myoongs 2025. 2. 15.

< 데이터 이상치/이상값  >

이상치는 특정 범위를 벗어난 튀는 데이터이며 데이터 분석에 영향을 줄 수 있어 보통 제거하는 편이다.

하지만 경우에 따라 보류하고 별도 분석을 진행할 수도 있다. 

데이터 전처리를 어떻게 할 것이냐는 분석 목적, 상황 등에 따라 다르다.

 

이상치는 데이터 입력 오류, 측정 오류, 실험 오류, 고의적인 이상값, 표본추출 에러 등에서 발생할 수 있으며, 소비자 조사 같은 경우 간혹 정말 특이한 특성을 가진 고객이라 일반적 범위에서 많이 벗어난 경우도 있다. 

예를 들어, 대부분이 연봉 4000 - 6000만원인데 2억 이상인 경우 등.

 

이상치를 알아내기 가장 쉬운 방법은 시각화를 통해서 한 눈에 보는 방법인 것 같다.

개인적으로 그 중 제일 편한 방법은 박스플롯 (상자수염그림) 사용인 것 같다. 

박스플롯은 최솟값, 3개의 사분위수 (25%, 50%, 75%), 최댓값으로 데이터를 요약하는 그래프로, 데이터가 어떤 방향으로 더 많이 늘어져 있는지 한 눈에 파악 가능하며 이상치 또한 함께 표현이 된다.

 

 

 

파이썬을 사용하여 박스플롯을 만들려면 우선 시각화 모듈을 불러와야한다. 

Matplotlib, Seaborn 등을 불러와야 하는데, 가장 기본이 되는 Matplotlib 을 사용하여 박스플롯을 만들어보겠다.

 

사용할 데이터는 지난번 데이터 전처리에서 결측값 처리하는 방법을 다룬 글에서 사용한 Kaggle 타이타닉 데이터 세트를 사용하겠다. 

 


< 파이썬 describe 메서드로 데이터 분포 확인 >

Matplotlib을 그리기 전에 우선 describe() 메서드로 데이터 분포부터 확인해보자

 

data3['Fare'].describe() # data3 데이터프레임의 Fare 열에 대한 데이터 정보 확인

 

결과값으로 Fare 열에는 총 418개의 데이터 인풋이 있다.

그리고 통계량은 아래와 같다:

  • 평균: 35.57
  • 표준편차: 55.85
  • 최솟값: 0
  • 25% 구간: 7.90
  • 50% 구간: 14.45
  • 75% 구간: 31.57
  • 최댓값: 512.33 

평균, 표준편차, 50% 구간 (중간값), 75% 구간과 최댓값을 보면 알 수 있듯이 범위에서 심각하게 벗어난 이상치가 있다는 것을 머리속에 그릴 수 있다.

 

이제 머리속으로 그리고 있는 데이터 분포를 Matplotlib 의 박스플롯을 사용하여 그려보자

 


< 파이썬 Matplotlib 으로 박스플롯 그리기 >

Maplotlib 으로 박스플롯을 그리는 방법은 아주 간단하다.

1. Matplotlib 임포트하기

2. Matplotlib 코드 넣고 돌리기

 

import matplotlib.pyplot as plt # maplotlib의 pyplot 모듈을 plt 라는 별칭으로 불러오고

plt.boxplot(data3['Fare') # data3의 'Fare' 열을 기준으로 boxplot 그리기
plt.title('Boxplot - Fare') # 그래프 제목 (불필요하면 스킵해도 됨)
plt.xlabel('') # x 축 라벨
plt.ylabel('USD') # y 축 라벨
plt.show() # 그래프 제시

 

위에서 확인한 최댓값이 512.33 이었는데 박스플롯에서도 이 값은 혼자 멀찍이 떨어져 찍혀 있는 게 보인다.

앞단에서 설명했듯 박스플롯에서 이상치는 IQR(25%~75% 구간) 에서 위, 아래로 1.5배씩 떨어진 곳에 있는 데이터다.

즉, 이상치는 제1사분위수 - (IQR*1.5) 과 제3사분위수 + (IQR*1.5) 이다.

이렇게 '일반적 범위'를 벗어난 이상치는 박스플롯의 수염(위스커) 밖에 o 표기로 표현된다.

 

박스플롯을 가로로 보고 싶다면 vert=False 라는 조건을 추가해주면 된다.

plt.boxplot(data3['Fare'], vert=False) # 박스플롯 가로로 그리기기
plt.title('Boxplot - Fare') 
plt.xlabel('USD') # vert=False 할 경우 x,y 축이 전환되기 때문에 이전 라벨과 반대로 넣어줘야함
plt.ylabel('') 
plt.show()

 

보통 지정값으로 박스플롯의 수염의 길이는 IQR의 1.5배이다.

하지만 수염 길이를 조절하여 이상치의 기준을 다르게 볼 수도 있다.

whis=수염길이 조건을 코드에 추가해주면 된다.

 

# 수염길이=5.0 으로 변경하여 박스플롯 그리기
plt.boxplot(data3['Fare'], vert=False, whis=5.0) # 박스플롯 가로로 그리기기
plt.title('Boxplot - Fare') 
plt.xlabel('USD') 
plt.ylabel('') 
plt.show()

 

whis 지정값이 1.5일 때와 5.0으로 변경된 후의 수염길이와 이상치 분포가 다른 것을 확인할 수 있다.

 

한 번에 여러 박스플롯을 그릴 수도 있다.

이 때, 동일한 단위를 기준으로 여러 그래프를 그릴 수도, 각 그래프 별로 적절한 단위에 맞춰 그려지도록 할 수도 있다.

전자의 경우 위에서 한 것과 비슷한 방식으로 추가하고자 하는 열만 코드에 넣어주면 된다.

후자의 경우 subplot 을 사용하여 그릴 수 있다.

 


< 동일한 단위에서 여러개의 박스플롯 그리기 > 

같은 단위를 가진 데이터의 분포를 조건에 따라 비교해보기 위해 성별에 따른 Age 분포를 박스플롯으로 보도록 하겠다.

스텝은 아래와 같다.

1. 조건에 맞춰 데이터 필터링

    성별에 맞춰 Age 데이터를 분리해야해서 male_age 와 female_age 로 변수를 만들고 조건에 맞게 필터링 해준다.

2. 필터링된 데이터를 박스플롯으로 그리기

    male_age, female_age를 박스플롯에 데이터로 넣어주고, 각 그래프에 맞게 xticks 를 사용하여 그래프 라벨을 넣어준다

# 동일한 단위로 여러개의 박스플롯 그리기
# 성별에 따른 티켓 값 구분

# 셩별에 따라 데이터 필터링
male_age=data3[data3['Sex']=='male']['Age']
female_age=data3[data3['Sex']=='female']['Age']

# 박스 플롯 그리기기
plt.boxplot([male_age, female_age]) # 필터링한 성별에 따른 age 로 박스플롯 만들기
plt.xticks([1,2],['Male', 'Female']) # 각 그래프의 라벨을 Male, Female 로 넣기

plt.title('Age Distribution by Sex') # 제목 
plt.ylabel('Age') # y 축 라벨
plt.show()

 

< 단위가 다른 여러개의 박스플롯 그리기 - subplots 사용>

 

한 번에 여러개의 그래프를 그리기 위해 subplots 함수를 사용할 수 있다.

subplots는 matplotlib의 axes 클래스의 객체를 말하며, 두 개 이상의 축을 포함한다.

figure 객체와 각 subplot을 나타내는 axes 객체의 배열을 반환하기 때문에

1. 사용 전에 figure, axes 에 대한 정보를 전달하고

2. 만들 각 그래프에 대한 내용을 순차적으로 넣어준다.

 

# 개별 스케일로 여러개의 박스플롯 그리기
# Fare, Age

fig, axes=plt.subplots(1,2, figsize=(16,8)) # (1,2) 형태의 그래프를 만들며, 사이즈는 16x8
                                            # (1,2) ==> 1개의 행에 2개의 그래프가 들어가는 모습

# 첫 번째 그래프 그리기 - Fare
axes[0].boxplot(data3['Fare']) # 박스플롯을 그릴 기준 데이터 
axes[0].set_title('Fare Distribution') # 제목
axes[0].set_ylabel('USD') # y축 라벨

# 두 번째 그래프 그리기 - Age
axes[1].boxplot(data3['Age']) # 박스플롯으로 그릴 기준 데이터
axes[1].set_title('Age Distribution') # 제목
axes[1].set_ylabel('Age') # y 축 라벨

 

참고로, subplots 으로는 그래프 종류도 모두 다르게 그릴 수 있다.

오늘 주제는 박스플롯이었기 때문에 박스플롯만 그려보았다.

반응형