본문 바로가기
Python/ML&DL

[파이썬, Python] 머신러닝 - 2️⃣ 의사결정나무(Decision Tree)

by coding-choonsik 2023. 6. 14.
728x90
반응형
SMALL

📄 예제에 사용한 파일

bike (1).csv
2.42MB

1. bike 데이터셋 살펴보기

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

bike_df = pd.read_csv('/content/drive/MyDrive/KDT/Python/4.머신러닝 딥러닝/bike.csv')
bike_df

bike_df.info()

 

✅ bike_df의 컬럼 정보 알아보기

 

수치형 데이터의 정보 알아보기

bike_df.describe()

 

✅ bike_df의 대여개수(Count)의 데이터 수를 시각화하여 알아보기

sns.displot(x=bike_df['count'])

📍 대여수가 0 쪽으로 데이터가 몰려있음

 

 

✅ boxplot을 통해 count에 대한 데이터 분포 알아보기

sns.boxplot(y=bike_df['count'])

▲ 이상치라고 하기엔 데이터가 일렬로 촘촘하게 분포함

 

 

✅ 체감온도(feels_like)에 대한 자전거 대여 개수 분포를 scatterplot을 통해 알아보기

sns.scatterplot(x='feels_like', y='count', data=bike_df, alpha=0.3)

▲ 체감온도가 영하로 떨어지면 자전거 대여 개수가 줄어듦

 

 

✅ 기압(pressure)에 따른 자전거대여 개수의 분포를 scatterplot으로 알아보기

sns.scatterplot(x='pressure', y='count', data=bike_df, alpha=0.3)

▲ 기압이 너무 낮거나 높은경우에는 대여 수가 줄어듦

 

 

✅ 바람속도(wind_speed)에 따른 자전거대여 개수의 분포를 scatterplot으로 알아보기

sns.scatterplot(x='wind_speed', y='count', data=bike_df, alpha=0.3)

▲ 풍속이 10이상일 경우 자전거 대여수가 급격히 줄어듦

 

sns.scatterplot(x='wind_deg', y='count', data=bike_df, alpha=0.3)

✅ 풍향(wind_deg) 따른 자전거대여 개수의 분포를 scatterplot으로 알아보기

▲ 바람의 방향은 자전거 대여수에 큰 영향이 없는 것 같음

 

✅ 결측치 알아보기

bike_df.isna().sum()

 

bike_df.isna().mean()  # 결측치에 대한 비율

▲  결측치에 대한 비율이 높음, 데이터가 거의 없음

 

✅ 결측지에 대한 비율이 높고 1시간당 비와 눈의 여부이기 때문에 오지않은 경우로 판단 ➡ 0으로 결측지 채움

bike_df = bike_df.fillna(0)
bike_df.isna().mean()

 

 

 

✅ 날짜를 계절과 같은 범주형 데이터로 변환

(학습 시 계절별로 영향을 미치는가에 대한 특징을 잡을 수 있음)

bike_df['datetime'] = pd.to_datetime(bike_df['datetime'])
bike_df.info()

 

 

✅ 날짜데이터를 연도, 월, 시간, 날짜별로 데이터를 알아보기 위해 새로운 파생변수로 저장

bike_df['year'] = bike_df['datetime'].dt.year   # 해
bike_df['month'] = bike_df['datetime'].dt.month  # 월, 계절
bike_df['hour'] =  bike_df['datetime'].dt.hour  # 시간별
bike_df['date'] = bike_df['datetime'].dt.date

bike_df.head()

 

 

✅ 시계열 데이터(날짜)를 lineplot을 통해 알아보기

plt.figure(figsize=(14,4))  # 그래프 크기 조정
sns.lineplot(x='date', y='count', data=bike_df)  
plt.xticks(rotation=45)  # x축 데이터를 45도 회전시켜서 보여줌
plt.show()

▲ 그래프에서 이상한 부분

 

✅ 해당 데이터의 상태를 비교해보기 위해 2019년도 데이터를 월별로 그룹하여 자전거 대여수 평균을 구하기

bike_df[bike_df['year'] == 2019].groupby('month')['count'].mean()

 

 

 

2020년도 데이터를 월별로 그룹하여 자전거 대여수 평균 구하기

bike_df[bike_df['year'] == 2020].groupby('month')['count'].mean()

▲ 4월 데이터가 없음

 

 

✅ bike_df에 'covid'라는 새로운 파생변수를 만듦

  • 날짜를 계절과 같은 범주형 데이터로 바꿈(# 2020-04-01 이전: precovid
  • 2021-04-01 이전 : covid
  • 그 이후 : postcovid
def covid(date):
  if str(date) < '2020-04-01':
    return 'precovid'
  elif str(date) < '2021-04-01':
    return 'covid'
  else:
    return 'postcovid'
    
    
 # 데이터프레임에 함수 적용
bike_df['date'].apply(covid)

 

✅ 위 함수를 람다함수를 이용하여 데이터프레임에 적용하기

bike_df['covid'] = bike_df['date'].apply(lambda date: 'precovid' if str(date)< '2020-04-01' else 'covid' if str(date)< '2021-04-01' else 'postcovid')
bike_df.tail()

 

✅ 'month' 컬럼으로 'season'이라는 새로운 파생변수를 만들기

  •  3~5월 : spring
  •  6~8 : summer
  •  9~11 : fall
  •  12~2 : winter
bike_df['season'] = bike_df['month'].apply(lambda month: 'winter' if month == 12 else 'fall' if month >= 9 else 'summer' if month >=6 else 'spring' if month >=3 else 'winter')
bike_df.head()

 

✅ 'hour'컬럼으로 'day_night'라는 새로운 파생변수 만들기

bike_df['day_night'] = bike_df['hour'].apply(lambda x: 'night' if x>=21 else 'late evening' if x>=19 else 'early evening' if x>=17 else 'late afternoon' if x>=16 else 'early afternoon' if x>= 13 else 'late morning' if x>=11 else 'early morning' if x>=5 else 'night')
bike_df['day_night']

 

 

✅ 모델 학습 시 필요없는 컬럼 삭제하고 저장하기

bike_df.drop(['datetime', 'month', 'date', 'hour'], axis=1, inplace=True)
bike_df.head()

 

 

✅ dtype이 object형인 컬럼 알아보기

 

✅ dtype이 object형인 컬럼들의 중복값을 제외한 데이터 종류와 개수 알아보기

for i in ['weather_main', 'covid', 'season', 'day_night']:
  print(i, bike_df[i].nunique())

 

 

✅ weather_main의 종류를 알아보기

bike_df['weather_main'].unique()

>>> array(['Clouds', 'Clear', 'Snow', 'Mist', 'Rain', 'Fog', 'Drizzle',
       'Haze', 'Thunderstorm', 'Smoke', 'Squall'], dtype=object)

 

✅ weather_main의 종류와 데이터 수를 boxplot으로 확인하기

plt.figure(figsize=(10,5))
sns.boxplot(x='weather_main', y='count', data=bike_df)

▲ Squall의 데이터 수가 적음

 

 

dtype이 object형인 컬럼 원핫인코딩 하기

bike_df = pd.get_dummies(bike_df, columns=['weather_main','covid','season','day_night'])
bike_df.head()

▲ 원핫인코딩된 컬럼들의 일부

 

✅ train데이터와 test데이터 나누기

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(bike_df.drop('count', axis=1), bike_df['count'], test_size=0.2, random_state=10)

2. 의사 결정 나무(Decision Tree)

  • 데이터를 분석하여 그 사이에 존재하는 패턴을 예측 가능한 규칙들의 조합으로 나타내며 그 모양이 나무와 같다고 하여 의사결정나무 라고 부름
  • 회귀(Regression), 분류(Classification) 모두 가능
  • 지니계수(Gini Index): 0에 가까울수록 클래스에 속한 불순도가 낮음
  • 엔트로피(Entropy): 결정을 내릴만한 충분한 정보가 데이터에 없다고 보는 것. (0에 가까울수록 결정을 내릴만한 충분한 정보가 있다)
    📍 지니계수와 엔트로피는 반비례 관계
  •  오버피팅(과적합): 훈련데이터에서는 정확하나 테스트데이터에서는 성과가 나쁜 현상을 말함. 훈련 데이터가 적거나 노이즈가 있을 때 또는 알고리즘 자체가 나쁠 때 발생. 의사 결정 나무에서는 나무의 가지가 너무 많거나 크기가 클 때 발생
  •  의사 결정 나무에서 오버피팅을 피하는 방법
    • 사전 가지치기: 나무가 다 자라기 전에 알고리즘을 멈추는 방법
    • 사후 가지치기: 의사 결정 나무를 끝까지 돌린 후 밑에서부터 가지를 쳐나가는 방법
from sklearn.tree import DecisionTreeRegressor

# 하이퍼 파라미터: 모델에 옵션을 주는 파라미터
# random_state: 랜덤한 값을 유지
dt = DecisionTreeRegressor(random_state=10)


# 학습 
dt.fit(X_train, y_train)

# 예측
pred1 = dt.predict(X_test)

# 검증데이터와 예측값을 그래프로 표시
sns.scatterplot(x=y_test, y=pred1)  # 모델이 잘 맞출수록 선형으로 나타남

 

2-1. 평가지표 적용하기

from sklearn.metrics import mean_squared_error

mean_squared_error(y_test, pred1, squared=False)  # RMSE
>>> 228.42843328100884

3. 선형회귀 vs 의사결정나무

 

3-1. 선형회귀 모델로 예측하기

# 선형회귀
from sklearn.linear_model import LinearRegression

# 선형회귀 객체 생성
lr = LinearRegression()

# 학습
lr.fit(X_train, y_train)

# 테스트 데이터로 예측
pred2 = lr.predict(X_test)

# 검증데이터와 예측값 그래프로 시각화
sns.scatterplot(x=y_test, y=pred2)

# RMSE
mean_squared_error(y_test, pred2, squared=False)  # RMSE

 

선형회귀 RMSE: 228.26128192004947
의사결정나무 RMSE: 228.42843328100884

 


3-2. 하이퍼파라미터 튜닝

# max_depth: 트리의 최대 깊이를 제한
# min_samples_leaf: 데이터가 나뉘어 최소 30개로 분류되었을때 가지치기를 끝냄
dt = DecisionTreeRegressor(random_state=10, max_depth=5, min_samples_leaf=30)

# 학습
dt.fit(X_train, y_train)

# 예측
pred3 = dt.predict(X_test)

# RMSE 
mean_squared_error(y_test, pred3, squared=False)
>>> 187.3015148952268
선형회귀 RMSE: 228.26128192004947
의사결정나무 RMSE: 228.42843328100884
의사결정나무 파라미터 튜닝: 187.3015148952268  (성능이 정말 달라질 수 있음!!)

 


3-3. 의사결정나무 모델의 그래프

from sklearn.tree import plot_tree 

plt.figure(figsize=(24,12))
plot_tree(dt, max_depth=5, fontsize=12)
plt.show()

▲ 의사결정나무의 그래프

 

plt.figure(figsize=(24,12))
plot_tree(dt, max_depth=5, fontsize=12, feature_names=X_train.columns)
plt.show()

✅ 하이퍼파라미터로 피쳐의 이름이 나오도록 표시하기

▲ feature_names=X_train.columns 하이퍼파라미터 적용 후

 

 

 

728x90
반응형
LIST