상세 컨텐츠

본문 제목

서울 자전거 공유 수요 예측 데이터셋

인공지능/데이터분석

by Ryuzy 2025. 1. 8. 02:05

본문

반응형

1. 서울 자전거 공유 수요 데이터셋

서울시의 공공자전거 대여 서비스인따릉이 대여 수요를 예측하는 문제에 사용되는 데이터셋입니다. 특정 시간대와 날씨, 요일, 공휴일 여부, 기온, 습도 다양한 데이터를 활용하여 자전거 대여 수요를 예측합니다.

 

 

2. 데이터셋 컬럼

  • Date : 연월일
  • Rented Bike count - 매 시간마다 대여한 자전거 수
  • Hour - 하루 중 시간
  • Temperature - 온도
  • Humidity - 습도 %
  • Windspeed - 풍속 m/s
  • Visibility - 가시거리 m
  • Dew point temperature - 이슬점 온도
  • Solar radiation - 태양 복사 MJ/m2
  • Rainfall - 강우량 mm
  • Snowfall - 적설량 cm
  • Seasons - 겨울, 봄, 여름, 가을
  • Holiday - 휴일/휴일 없음
  • Functional Day - 운영되지 않았던 , 정상적으로 운영된

 

 

3. 데이터 전처리 및 탐색적 데이터 분석 (EDA)

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

bike_df = pd.read_csv('/본인의 구글드라이브 경로/SeoulBikeData.csv', encoding='CP949')
bike_df

 

※ CP949

  • Microsoft Windows의 한국어 문자 인코딩입니다.
  • EUC-KR을 확장한 형태로, 더 많은 한국어 문자(한자, 확장 문자 등)를 지원합니다.
  • 주로 Windows 환경에서 저장된 한글 파일에서 사용됩니다.
bike_df.info()

 

bike_df.describe()

 

bike_df.columns

 

bike_df.columns = ['Date', 'Rented Bike Count', 'Hour', 'Temperature', 'Humidity',
       'Wind speed', 'Visibility', 'Dew point temperature',
       'Solar Radiation', 'Rainfall', 'Snowfall', 'Seasons',
       'Holiday', 'Functioning Day']

 

bike_df.head()

 

sns.scatterplot(x='Temperature', y='Rented Bike Count', data=bike_df, alpha=0.3)

 

sns.scatterplot(x='Wind speed', y='Rented Bike Count', data=bike_df, alpha=0.3)

 

sns.scatterplot(x='Visibility', y='Rented Bike Count', data=bike_df, alpha=0.3)

 

sns.scatterplot(x='Hour', y='Rented Bike Count', data=bike_df, alpha=0.3)

 

bike_df.isna().sum()

 

bike_df.info()

 

bike_df['Date'] = pd.to_datetime(bike_df['Date'], format='%d/%m/%Y')
bike_df.info()

 

bike_df['year'] = bike_df['Date'].dt.year
bike_df['month'] = bike_df['Date'].dt.month
bike_df['day'] = bike_df['Date'].dt.day
bike_df.head()

 

plt.figure(figsize=(14, 4))
sns.lineplot(x='Date', y='Rented Bike Count', data=bike_df)
plt.xticks(rotation=45)
plt.show()

 

bike_df[bike_df['year'] == 2017].groupby('month')['Rented Bike Count'].mean()

 

bike_df[bike_df['year'] == 2018].groupby('month')['Rented Bike Count'].mean()

 

bike_df['TimeOfDay'] = pd.cut(bike_df['Hour'], 
                            bins=[0, 5, 11, 17, 23], 
                            labels=['Dawn', 'Morning', 'Afternoon', 'Evening'],
                            include_lowest=True)

 

※ pd.cut()

pd.cut()은 숫자 데이터를 구간(bins)으로 나누어 범주형 데이터로 변환하는 데 사용됩니다. 주로 연속형 데이터를 특정 범주로 분류할 때 활용됩니다.

  • bins: 숫자 데이터를 나눌 경계값(구간)입니다.
0 ≤ Hour ≤ 5 Dawn (새벽)
5 < Hour ≤ 11 Morning (아침)
11 < Hour ≤ 17 Afternoon (오후)
17 < Hour ≤ 23 Evening (저녁)
  • 주의: bins의 경계값은 오른쪽 경계값을 포함합니다.
bike_df.head()

 

sns.barplot(x='Functioning Day', y='Rented Bike Count', data=bike_df)

 

Seaborn의 barplot은 범주형 데이터(x)에 대해 수치형 데이터(y)의 통계 요약값을 계산하여 막대로 시각화하는 그래프로, 기본적으로 각 범주별 평균(mean)을 사용하며 평균의 불확실성을 나타내는 신뢰구간(error bar)을 함께 표시합니다. 내부적으로는 groupby와 집계 과정을 자동으로 수행하여 원본 데이터의 개수와 상관없이 범주 수만큼의 막대가 생성되며, estimator 옵션을 통해 평균 대신 합계·중앙값·개수 등으로 변경할 수 있고, hue를 사용하면 추가 범주에 따른 비교도 가능합니다. 즉, seaborn barplot은 값 자체가 아니라 집단 간 통계적 대표값을 비교하기 위한 시각화 도구입니다.

 

※ Seaborn의 barplot 을 읽는 법

1) 먼저 “막대가 뭘 뜻하는지”부터 확인합니다. Seaborn barplot의 막대 높이는 기본적으로 평균(mean) 입니다.

  • x축(또는 y축)에 있는 각 범주(category) 별로 y값(수치형)의 평균을 계산해서 그 평균을 막대 높이로 그립니다.

따라서 “막대가 높다” = 그 범주의 평균이 크다 입니다. (원본 데이터의 한 개 값이 아니라, “집단의 대표값”입니다.)

 

2) 축을 읽어 “무엇을 비교하는지”를 정합니다

  • x축: 비교 대상(범주) → 예: 과일 종류, 요일, 지역, 등급
  • y축: 비교 기준(수치) → 예: 판매량 평균, 점수 평균, 가격 평균

 

3) 막대끼리 비교할 때는 “차이의 크기”를 봅니다. 막대의 높이 차이를 보고 해석합니다.

  • A 막대가 B보다 높다 → A의 평균이 더 크다
  • 막대 차이가 크다 → 집단 간 평균 차이가 크다
  • 비슷하다 → 큰 차이가 없다(적어도 평균 관점에서)

“평균이 비슷해도 데이터가 들쑥날쑥할 수 있다” → 그걸 보는 게 에러바입니다.

 

4) 에러바(error bar)는 “그 평균이 얼마나 믿을 만한지/흔들리는지”입니다. Seaborn barplot은 보통 막대 위아래에 선(에러바)가 그려집니다. 이 에러바는 설정에 따라 의미가 달라질 수 있지만(기본은 신뢰구간), 읽는 법은 공통입니다.

  • 해석: 이 데이터를 기반으로, 유사한 100가지의 케이스에서 95개 이상은 해당 범위에서 결과를 얻음을 의미
  • 평균(mean), 중앙값(median), 최빈값(mode)등의 다른 값들을 함께 고려했기 때문에 신뢰성 있는 수치임

 

bike_df['Functioning Day'].value_counts()

 

bike_df[bike_df['Functioning Day'] == 'Yes']

 

bike_df[bike_df['Functioning Day'] == 'No']

 

bike_df.info()

 

bike_df = bike_df.drop('Date', axis=1)
bike_df.head()

 

bike_df.select_dtypes(exclude=['number']).columns.tolist()

 

for i in bike_df.select_dtypes(exclude=['number']).columns.tolist():
    print(i, bike_df[i].nunique())

 

bike_df = pd.get_dummies(bike_df, columns=bike_df.select_dtypes(exclude=['number']).columns.tolist(), drop_first=True)
bike_df.head()

 

# 모든 컬럼 간 상관관계 분석
correlation_matrix = bike_df.corr()

# 목표 변수와의 상관관계만 확인
target_corr = correlation_matrix['Rented Bike Count'].sort_values(ascending=False)
print(target_corr)

 

※ corr() 함수

corr() 함수는 데이터프레임의 숫자형 열 간의 상관관계를 계산하는 데 사용됩니다. 상관관계는 두 변수 간의 선형 관계를 나타내며, 주로 -1에서 1 사이의 값으로 표현됩니다.

  • corr()는 Pearson 상관계수를 기본으로 사용합니다.
  • 숫자형 열만 상관관계 분석에 포함됩니다.
  • 높은 상관관계(>|0.5|): 강한 관계
  • 낮은 상관관계(<|0.2|): 약한 관계

corr()를 사용하여 높은 상관관계를 가진 컬럼을 식별하고 제거할지 여부를 판단할 수 있습니다. 특히, 다중공선성(multicollinearity) 문제가 발생할 경우 머신러닝 모델의 성능이 저하될 수 있으므로, 상관관계가 높은 컬럼을 적절히 제거하는 것이 중요합니다.

 

※ 다중공선성

다중공선성(Multicollinearity)은 회귀 분석에서 독립 변수들(설명 변수) 간에 강한 상관관계가 존재하는 현상을 의미합니다. 이러한 상관관계가 높으면 각 독립 변수가 종속 변수에 미치는 개별적인 영향을 정확하게 추정하기 어려워지며, 회귀 계수의 추정치가 불안정해져 작은 데이터 변화에도 크게 변할 수 있습니다. 이는 모델의 예측 성능 저하와 해석의 신뢰성 감소로 이어질 수 있으므로, 다중공선성이 높은 변수를 식별하고 제거하거나 조정하는 것이 중요합니다.

plt.figure(figsize=(16, 12))
sns.heatmap(correlation_matrix, annot=True, fmt='.2f', cmap='coolwarm')
plt.title('Feature Correlation Heatmap')
plt.show()

 

bike_df = bike_df.drop(['Dew point temperature'], axis=1)
bike_df.head()

 

bike_df.info()

 

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(bike_df.drop('Rented Bike Count', axis=1), bike_df['Rented Bike Count'], test_size=0.3, random_state=2025)

 

X_train.shape, y_train.shape

 

X_test.shape, y_test.shape

 

 

4. 결정 트리

결정 트리(Decision Tree)는 데이터를 기반으로 의사결정을 수행하는 트리 구조의 예측 모델입니다. 루트 노드(root node)에서 시작해 각 노드는 특정 특성(feature)의 조건에 따라 가지(branch)로 분기되며, 최종적으로 리프 노드(leaf node)에 도달해 예측 결과(클래스나 값)를 도출합니다. 주로 분류(Classification)와 회귀(Regression) 문제에 사용되며, 데이터의 패턴을 직관적으로 시각화할 수 있어 해석이 용이합니다. 하지만 트리가 너무 깊어지면 과적합(overfitting) 문제가 발생할 수 있으므로 가지치기(pruning)나 최대 깊이 설정 등으로 제어해야 합니다.

  • 전체 데이터셋을 하나의 노드로 시작합니다.
  • 최적의 특성(feature)과 분할 기준(threshold)을 찾아 첫 번째 분할을 수행합니다.
  • 분류 (Classification):
    • Gini 불순도(Gini Impurity)
    • 엔트로피(Entropy)
  • 회귀 (Regression):
    • 평균 제곱 오차(Mean Squared Error, MSE)
    • 절대 평균 오차(Mean Absolute Error, MAE)
  • 각 하위 노드에 대해 위 단계를 반복합니다.
  • 이 과정을 통해 트리는 여러 깊이로 성장합니다.
  • 모든 노드가 더 이상 나눌 수 없거나 특정 조건(max_depth, min_samples_split)을 만족할 때까지 반복됩니다.
  • 더 이상 분할이 불가능할 때 리프 노드가 생성됩니다.
  • 분류 문제: 가장 많은 클래스가 있는 클래스를 예측값으로 사용
  • 회귀 문제: 평균값을 예측값으로 사용

※ Gini 불순도 (Gini Impurity)

  • Gini 불순도는 한 노드에 있는 데이터의 순수도(Purity)를 측정하는 지표입니다.
  • 한 노드에 있는 샘플들이 동일한 클래스에 속할 확률이 높을수록 Gini 불순도는 낮아집니다.
  • 즉, 노드가 "얼마나 섞여 있는지"를 나타냅니다.

엔트로피 (Entropy)

  • 엔트로피는 정보의 불확실성(혼란도, Uncertainty)을 측정합니다.
  • 엔트로피가 높을수록 해당 노드에 있는 데이터는 더 섞여 있으며, 예측하기 어렵습니다.
  • 엔트로피는 데이터가 균등하게 분포될 때 최대값을 가집니다.
from sklearn.tree import DecisionTreeRegressor

dtr = DecisionTreeRegressor(random_state=2025)
dtr.fit(X_train, y_train)
pred1 = dtr.predict(X_test)

sns.scatterplot(x=y_test, y=pred1)

 

※ DecisionTreeRegressor

DecisionTreeRegressor는 주어진 데이터를 반복적으로 분할하여 예측을 수행합니다. 각 분할은 데이터의 목표 변수 값을 예측하는 데 가장 적합한 값을 찾기 위해 이루어집니다. 이를 통해 데이터를 점차 더 작은 부분으로 나누고, 각 부분에서 평균값을 예측값으로 사용하는 방식입니다. DecisionTreeRegressor  데이터를 두 가지 그룹으로 분할합니다. 각 분할에서 목표는 두 그룹의 MSE가 가능한 한 낮도록 만드는 것입니다. 즉, 각 분할에서의 MSE가 최소화되도록 분할점을 찾습니다.

 

from sklearn.metrics import root_mean_squared_error

root_mean_squared_error(y_test, pred1)

 

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)

 

root_mean_squared_error(y_test, pred2)

 

# 하이퍼 파라미터 적용
dtr = DecisionTreeRegressor(random_state=2025, max_depth=50, min_samples_leaf=30)
dtr.fit(X_train, y_train)
pred3 = dtr.predict(X_test)

root_mean_squared_error(y_test, pred3)

 

from sklearn.tree import plot_tree

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

 

from sklearn.ensemble import RandomForestRegressor

rf = RandomForestRegressor(random_state=2025)
rf.fit(X_train, y_train)
pred4 = rf.predict(X_test)
root_mean_squared_error(y_test, pred4)

 

rf.feature_importances_

 

feature_imp = pd.DataFrame({
    'features': X_train.columns,
    'importances': rf.feature_importances_
})

 

feature_imp

 

top10 = feature_imp.sort_values('importances', ascending=False).head(10)
top10

 

plt.figure(figsize=(5, 10))
sns.barplot(x='importances', y='features', data=top10)
반응형

'인공지능 > 데이터분석' 카테고리의 다른 글

슈퍼스토어 마케팅 캠페인 데이터셋  (1) 2025.01.10
호텔 예약 수요 데이터셋  (1) 2025.01.09
주택 임대료 예측 데이터셋  (4) 2025.01.03
사이킷런  (1) 2024.12.31
머신러닝  (3) 2024.12.23

관련글 더보기