ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ 보험료 예측 모델 생성(1) _ 간단한 EDA 및 전처리 ]
    About_Datascience/ML 2023. 3. 13. 15:48

    이번에는 보험사 고객 정보 데이터셋을 가지고 간단한 EDA 및 모델링 프로젝트를 진행할 예정이다. 

     

    데이터를 간단히 살펴보면

     

     

    <Columns>

    • age : 고객 나이
    • sex : 고객 성별
    • bmi : 고객 bmi 지수
    • children : 고객의 자녀 수 
    • smoker : 고객의 흡연 여부
    • region : 고객 거주 지역 정보
    • charges : 고객에게 부과되는 보험료 

     

     

    간단한 EDA 및 전처리를 하기 전 확인해야 할 체크리스트를 정리해보았다. 

     

    <Check List>

    • 어떤 질문을 풀거나 틀렸다고 증명하려 하는가 ?
    • 중복된 데이터가 있는가 ?
    • 어떤 종류의 데이터가 있으며 다른 데이터 타입들을 어떻게 다루려 하는가 ?
    • 데이터에 Null 값이 존재하는지 , 있다면 그것들을 어떻게 처리하려 하는가?
    • 이상치가 존재하는가 ? 그 이상치는 관심을 가져야 할 데이터인가?
    • 변수 간 상관성이 있는가 ? 

     

    Import Necessary Module

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    import missingno as msno
    
    from sklearn.preprocessing import MinMaxScaler
    from sklearn.preprocessing import StandardScaler
    from sklearn.preprocessing import OneHotEncoder
    from sklearn.preprocessing import LabelEncoder
    
    from sklearn.linear_model import LinearRegression
    from sklearn.preprocessing import PolynomialFeatures
    from sklearn.svm import SVR
    from sklearn.tree import DecisionTreeRegressor
    from sklearn.ensemble import RandomForestRegressor
    from sklearn.model_selection import train_test_split
    
    from sklearn.metrics import r2_score

     

     

    Q. 어떤 질문을 풀거나 틀렸다고 증명하려 하는가 ?

            A . 보험사 고객 정보를 통해 보험료 예측 모델을 생성하려 한다.

     

    shape , describe , info 를 통해 데이터를 간략히 살펴보자 . 

    1338 row x 7columns

     

     

    describe( ) 를 통해서는 기본적인 통계관련 내용과 이상치가 있는지의 여부 등을 알 수 있다. 

     

     

    info ( ) 를 통해 Null 값이 존재하는지 , column 별로 data type 은 무엇인지 알 수 있다. 

     

     

    import missingno

    Null 값을 시각화 하기 위해 missingno 모듈을 import 하여 활용하였다.

     

     

    Q. 중복된 데이터가 있는가 ?

            A . duplicated( )함수 사용

     

     

    중복된 항목 제거

     

     

    <duplicated 함수의 매개변수>

     

    - keep : { first / last } 중복제거를 할 때 남길 행. first 면 첫 번째 중복 값을 남기고 , last면 마지막 값을 남긴다.

    - ignore_index : False로 하게 되면 index 컬럼이 새로 생기게 된다. True 로 하면 이를 무시하겠다는 뜻이다. 

     

     

     

    Q. 어떤 종류의 데이터가 있으며 다른 데이터 타입들을 어떻게 다루려 하는가 ?

            A . 총 컬럼 수와 컬럼별 데이터 타입 확인

     

    위에서 확인했던 info( ) 함수를 이용하여 확인해보자. 

     

     

    Column Type 별로 groupby로 묶어준 후 , agg( ) 함수를 이용하여 count 해주었다.

    dtype_df = df.dtypes.reset_index()
    dtype_df.columns = ['Count','Column Type']
    dtype_df = dtype_df.groupby('Column Type').agg('count').reset_index()
    
    dtype_df

    여기서 또 확인해보아야 할 것들이 있다.

    • 숫자형 데이터 중 명백하게 포함 할 의미가 없는 것은 없는가?
    • 범주형 변수는 있는가 ? (object type)

     

     

    범주형 변수에 대해 살펴보자 . 

    이 데이터셋에서 범주형 변수 column 은 sex , smoker , region 이 있다. 

     

     

    범주형 변수별 개수 시각화

    # 범주형 변수별 개수 시각화
    for col in df.select_dtypes(include = ['object','category']).columns :
        fig = sns.catplot(x=col , kind='count', data=df, hue=None)
        fig.set_xticklabels(rotation=60)
        plt.show()

    성별 ( male , female ) 과 흡연여부(yes , no) 는 2개의 값만을 갖는 것을 알 수 있고, 

    region 은 southwest, southeast , northwest , northeast 4개의 값을 갖는 것을 알 수 있다. 

     

    이제 이러한 범주형 변수들을 categorical (text) 데이터를 numerical 데이터로(숫자 정보)

    나중에 머신러닝을 돌릴 때 컴퓨터가 인식할 수 있도록 Encoder 를 통해 숫자로 변환해줘야 한다. 

     

     

    < LabelEncoderOneHotEncoder >

     

    1. LabelEncoder 

    각 변수별 속성에 알파벳 순서에 따라 unique한 정수가 할당된다. 속성값을 그냥 정수로 바꿔주는 것이기 때문에

    dataframe 자체의 크기가 커지거나 줄어들지 않는다. shape도 유지된다. 

    one-hot encoding이 변수 안의 속성값의 종류만큼 열을 추가해서 늘린다는 걸 감안하면 비교적 dense하다.

    LabelEncoder를 사용할 때 주의할 점이 있는데 LabelEncoder는 1차원 array만 입력받는다는 것이다. 

    또한 LabelEncoder는 추후 학습과정에서 의도치 않은 오류를 만들어낼 수 있기 때문에 웬만해서는 잘 안쓴다고 한다.


    2. OneHotEncoder

    라벨 인코딩보다 더 자주 쓰는 것이 원-핫 인코딩 방식으로 범주형 변수를 0 또는 1 값을 가진 하나 이상의 새로운 특성으로 바꾼 것이다.

    내가 표현하고 싶은 값만 1로 두고 나머지는 0으로 두는 것이라 쉽게 이해해도 괜찮을 것 같다. 

    하지만 OneHotEncoding에도 문제점이 있다.

    바로 '다중 공선성' 인데 , 이는 독립 변수들 간에 서로 강한 상관관계를 가지면서 회귀계수 추정의 오류가 나타나는 문제이다. 
    즉 , 하나였던 독립변수가 다른 독립변수에 영향을 줄 수 있다는 것이다. 

    따라서 OneHotEncoding을 사용할 때 다중공선성을 해결하기 위해서는

    Dummy Column이 n 개면 n-1개만 사용하여 해결해줄 수 있다.

     

     

    이번 실습에서는 항목이 2개인 성별 , 흡연 여부는 LabelEncoder 를 , 지역은 OneHotEncoder 를 사용하여 변환해주기로 한다. 

     

     

     

    <LabelEncoder - sex , smoker>

    1. 우선 데이터셋에서 sex , smoker 값들만 따로 분리해준다 .

     

    sex = df.iloc[:,1:2].values
    smoker = df.iloc[:,4:5].values

     

    2. LabelEncoder( ) 적용 

    # 유일 값이 2개인 성별
    le = LabelEncoder()
    
    sex[:,0] = le.fit_transform(sex[:,0])
    # transform을 거치면 type이 numpy의 ndarray로 나오기 때문에 형변환 해줘야함.
    sex = pd.DataFrame(sex)
    
    sex.columns =['sex']
    
    # 딕셔너리 형태로 변환
    le_sex_mapping = dict(zip(le.classes_, le.transform(le.classes_)))
    
    print(le_sex_mapping)
    print(sex)
    # {'female': 0, 'male': 1} 인 것을 명시해주기 위해 딕셔너리 형태로 변환해준 것.

     

    2-1 . smoker 도 같은 방식으로 적용해준다. 

    # 유일 값이 2개인 흡연자 (성별과 같은 로직)
    le = LabelEncoder()
    
    smoker[:,0] = le.fit_transform(smoker[:,0])
    # transform을 거치면 type이 numpy의 ndarray로 나오기 때문에 형변환 해줘야함.
    smoker = pd.DataFrame(smoker)
    
    smoker.columns =['smoker']
    
    le_smoker_mapping = dict(zip(le.classes_, le.transform(le.classes_)))
    
    print(le_smoker_mapping)
    print(smoker)

     

    <OneHotEncoder - region >

    # region -> OneHotEncoder 사용 
    region = df.iloc[:,5:6].values
    
    ohe = OneHotEncoder()
    
    region = ohe.fit_transform(region).toarray()
    
    region = pd.DataFrame(region)
    region.columns=['northeast','northwest','southeast','southwest']
    print('지역에 따른 OneHotEncoder 결과 : ')
    print(region[:10])

    OneHotEncoder 를 이용한 결과를 확인해보면

    OneHotEncoder 사용 결과

     

    Q. Null 값 처리 

            A . NULL 값이 포함된 컬럼을 찾고 각 컬럼의 평균값으로 채우기 ( Imputation 사용 )

     

     

    1. 각 컬럼들에 몇 개의 NULL 값이 포함되어 있는지 확인 

    bmi 컬럼에 5개의 NULL값이 있었다.

     

    2. NULL 값 시각화 

     

    2-1. missingno module 사용 

    import missingno
    heatmap 사용 하여 시각화

     

    3. NULL 값을 해당 컬럼의 평균값으로 대체

    # null 값을 해당 컬럼의 평균값으로 대체하기 
    df['bmi'].fillna(df['bmi'].mean(), inplace=True)
    
    # null 값 다시 확인
    df.isnull().sum()

    null 값이 모두 0인 것을 확인할 수 있다.

     

     

    4. 이상치 및 데이터 경향성 확인

     

     우선 describe( ) 함수를 이용하여 대략적으로 값들을 확인해보자. 

     

    describe() 사용

    대략적으로 봤을 때 눈에 띄는 이상치는 없는 것 같아 보인다. 

     

    이번엔 숫자형 데이터 , 범주형 데이터를 나눠서 데이터의 경향성을 시각화 해보자 .

     

     

    4-1 . numeric data 시각화 (displot 사용)

    # 숫자형 데이터들만 가져와서 경향성 확인
    numeric_data = df.select_dtypes(include= np.number)
    l = numeric_data.columns.values
    number_of_columns = 4
    
    for i in range(0, len(l)):
        sns.displot(numeric_data[l[i]],kde=True)

     

    age

    bmi

    children

    charges

     

    4-1 . numeric data 시각화 (boxplot 사용)

    # 데이터 컬럼 타입이 np.number인 컬럼들만 가져오기
    columns = df.select_dtypes(include=np.number).columns
    figure = plt.figure(figsize=(20,10))
    
    figure.add_subplot(1, len(columns), 1)
    
    for index, col in enumerate(columns):
        if index > 0 :
            figure.add_subplot(1,len(columns), index+1)
        sns.boxplot(y=col , data = df , boxprops ={"facecolor":'None'})
    figure.tight_layout()
    plt.show()

    boxplot( )

     

    4-2 . violin plot을 이용하여 numeric data와 categorical data 간의 상관관계 시각화.

     

     

     sex - age 

     

    smoker - age 

     

    region - age

     

    sex - bmi

     

    smoker - bmi

     

    이런 식으로 violin plot 을 이용하여 상관관계를 시각화 해보았는데 , 큰 연관성을 찾지 못하였다. 

     

    그러나 , smoker - charges 와의 시각화는 조금 독특했다.

     

    확실히 비흡연자에 비해 흡연자의 charges(보험료) 가 높게 나타나는 것이 보였다. 

    우리가 하고자 하는 보험료 예측에 중요한 feature인 것 같다. 

     

     

    5. corr( )을 이용한 변수 간 상관관계 확인

    # Correlation 시각화
    plt.figure(figsize=(6,4))
    sns.heatmap(df.corr(),cmap='Blues',annot=False)

     

    # 보험료 Correlation Matrix 시각화 
    k=4 #Heatmap 에서 확인한 변수 개수
    
    cols = df.corr().nlargest(k,'charges')['charges'].index
    cm = df[cols].corr()
    plt.figure(figsize=(10,6))
    sns.heatmap(cm, annot=True, cmap='viridis')
    plt.show()

     

    두 히트맵을 보면 그나마 charges와 age 간에 상관관계가 조금 있다는 것을 제외하고는 크게 상관관계가 있는 변수는 없는 것 같다 . 

     

    pairplot 을 그려본 후 확인해봐도

    그나마 age 와 charges 에 약간의 선형적인 관계가 눈에 보이고 나머지로는 변수 간 관계가 어떤지 확인하기 힘들 것 같다. 

     

    아까 위에 violin plot 으로 확인해봤을 때  smoker가 보험료를 측정하는 데 있어 중요한 feature 중 하나라는 것을 알 수 있었다. 

    이를 pair plot 으로 hue = ' smoker ' 로 주고 확인해보았다. 

    선형적인 관계가 보이는 age와 charges 부분 그래프를 확인해보면 age와 charges 간에 관계도 관계지만 

     

    나이와 별개로 흡연여부가 yes인 파란점의 보험료 자체가 비흡연자에 비해 꽤나 높다는 결과가 도출되었다.

     


     

    간단한 EDA 및 전처리는 이 정도로 마무리 하고 다음 게시물에서는

    다양한 Regression 모델을 활용해서 모델링을 해본 뒤 모델들을 평가해보고자 한다. 

Designed by Tistory.