묭동이 개발 블로그
  • [seaborn] boxplot 그래프를 통해 이상치 확인하고 제거하기 (2)
    2024년 01월 12일 22시 44분 41초에 업로드 된 글입니다.
    작성자: 묭동이

    본 글은 Evan Jung님의 Udemy "데이터분석 필수, Python Streamlit을 활용한 대시보드 만들기" 강의를 수강 후 정리해 작성하였습니다.


    안녕하세요 [seaborn] boxplot 그래프를 통해 이상치 확인하고 제거하기 (1)에 이어서

    이번 글에서는 사용자 정의 함수를 통해 이상치를 처리하는 방법과 seaborn 라이브러리를 활용하여 두 개의 boxplot 그래프를 그리는 것을 알아보겠습니다 !

     

    이상치 제거 함수 만들기

    먼저 필요한 라이브러리를 불러오도록 하겠습니다.

    import pandas as pd

     

    이후 remove_outliers_iqr 이름의 이상치 제거 함수를 정의하였습니다.

    [seaborn] boxplot 그래프를 통해 이상치 확인하고 제거하기 (1)에서 다뤘던 이상치 제거 알고리즘을 함수로 정의한 형태입니다.

    remove_outliers_iqr 함수는dataframe 객체와 column을 인자로 받습니다.

     

    (1) df의 column에 해당하는 Q1, Q3, IQR 값 구하기

    함수의 인자로 넘겨진 df(DataFrame 객체)와 column(열)을 통해 Q1, Q3, IQR 값을 구합니다.


    (2) 이상치 임계값 설정

    이상치의 임계값으로 하한가 및 상한가를 설정합니다.

    # 하한가 설정
    lower_bound = Q1 - 1.5 * IQR
    # 상한가 설정
    upper_bound = Q3 + 1.5 * IQR

    (3) 이상치 확인

    loc 메소드를 통해 행조건을 만족하는 모든 열의 데이터를 추출합니다.

    하한가(lower_bound) 보다 작고 상한가(upper_bound) 보다 큰 값이 이상치이므로

    아래 코드와 같이 행조건을 주고 모든 열의 데이터를 추출하여 이상치를 의미하는 outliers 변수에 저장합니다.

    df.loc[(df[column] < lower_bound) | (df[column] > upper_bound), :]

    (4) 이상치 제거

    이후 loc 메소드를 통해 행조건을 만족하는 모든 열의 데이터를 추출합니다.

    아래 코드와 같이 이상치를 확인하는 것의 반대개념을 이용해 행조건을 주어 이상치가 제거된 데이터를 df_clean 변수에 저장합니다.

    df_clean = df.loc[(df[column] >= lower_bound) & (df[column] <= upper_bound), :].reset_index(drop=True)

    remove_outliers_iqr 이상치 제거 함수 정의


    (5) 함수 테스트

    sales 데이터의 'year' 열 중 2007~2010년에 해당하는 데이터를 이용하여 작성한 함수를 테스트해보도록 하겠습니다.

    코드의 출력 결과 'price' 열에 있는 이상치 값들이 잘 나온 것을 확인할 수 있습니다

     

    (6) seaborn 라이브러리를 활용해 그래프로 그려보기

    아래 코드를 실행했을 때 그래프를 통해 알 수 있듯이 1e6과 같이 과학적 표기법으로 y축이 표현되었습니다.

    과학적 표기법을 해제하는 방법은 아래에서 다뤄보겠습니다 !

    그래프 그리기

    ScalarFormatter Module

    • ScalarFormatter는 Matplotlib 라이브러리에서 축의 눈금 레이블을 스칼라 형식으로 지정하기 위한 모듈이며 축 눈금 레이블을 보다 읽기 쉬운 형식으로 표시할 수 있습니다.
    • formatter.set_scientific(False)
      • 과학적 표기법(예: 1e6) 대신 일반 숫자 형식으로 축 눈금 레이블을 표시하도록 설정합니다.
      • False를 인자로 주어 과학적 표기법을 사용하지 않도록 합니다.

     

    (1) 도화지 그리기

    그래프를 그리기 위해 먼저 도화지를 그려줍니다.

    • plt.subplots를 사용하여 두 개의 그래프(subplot)가 포함된 그림(figure)을 생성합니다.
    •  nrows=2는 두 개의 행이 있는 서브플롯을 의미하며, figsize=(10, 10)는 그림의 크기를 10x10 인치로 설정합니다.
    fig, ax = plt.subplots(nrows=2, figsize=(10, 10))

    (2) 첫 번째(이상치가 포함된) boxplot 그리기

    sns.boxplot(data=df, x='year', y='price', hue='propertyType', ax=ax[0])
    ax[0].set_title('with outliers')
    ax[0].yaxis.set_major_formatter(formatter)
    • sns.boxplot은 Seaborn의 boxplot을 그리는 함수입니다.
    • data=df는 사용할 데이터셋을, x='year', y='price'는 각각 x축과 y축에 표시할 변수를 지정합니다.
    • 범례 만들기
      • hue='propertyType'은 각 연도별로 다른 부동산 유형의 가격 분포를 색상으로 구분합니다.
    • 첫 번째 서브플롯(ax[0])에 제목을 설정하고, y축의 숫자 형식을 일반 숫자 형식으로 설정합니다.

    (3)  두 번째(이상치가 제거된) boxplot 그리기

    sns.boxplot(data=df_clean, x='year', y='price', hue='propertyType', ax=ax[1])
    ax[1].set_title('without outliers')
    • 두 번째 서브플롯(ax[1])에 대해 df_clean 데이터셋을 사용하여 boxplot을 그립니다. 
    • 즉, 이상치가 제거된 데이터만 포함되어 있는 그래프를 그립니다.

    (4)  그래프 화면에 표시하기

    plt.tight_layout()
    plt.show()
    • plt.tight_layout() : 서브플롯들이 겹치지 않도록 적절하게 레이아웃을 조정합니다.
    • plt.show() : 최종적으로 생성된 그래프를 화면에 표시합니다.

    그래프 그리는 코드

     

    이상치가 포함되어 있는 그래프와 이상치가 제거된 그래프를 비교했을 때 확실히 많은 이상치들이 제거된 것을 알 수 있습니다.

    그러나 모든 이상치가 제거되지는 않았는데요 "모든" 이상치를 제거하려면, 데이터에서 이상치를 먼저 식별하고 제거하는 과정을 거쳐야 합니다. 모든 이상치를 제거하는 것이 반드시 최선은 아닐 수도 있으며 때로는 이상치가 중요한 정보를 포함하고 있을 수 있으므로, 이상치를 제거하기 전에 왜 이상치가 발생했는지를 이해하는 것이 중요하다고 생각합니다.

    seaborn 라이브러리를 통해 그린 boxplot 그래프

     

    틀린 부분 댓글로 작성해주시면 감사하겠습니다 😊

     

     

    Reference

     

    matplotlib.ticker — Matplotlib 3.8.2 documentation

    Sequence of acceptable tick multiples, starting with 1 and ending with 10. For example, if steps=[1, 2, 4, 5, 10], 20, 40, 60 or 0.4, 0.6, 0.8 would be possible sets of ticks because they are multiples of 2. 30, 60, 90 would not be generated because 3 does

    matplotlib.org

     

    seaborn.boxplot — seaborn 0.13.1 documentation

    seaborn.boxplot seaborn.boxplot(data=None, *, x=None, y=None, hue=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=0.75, fill=True, dodge='auto', width=0.8, gap=0, whis=1.5, linecolor='auto', linewidth=None, fliersize=Non

    seaborn.pydata.org

     

    댓글