서울시 공공자전거 데이터를 활용한 수요 분석 및 예측

2025. 5. 10. 17:37프로젝트

github link : https://github.com/Thingjae98/bike_data/tree/main

 

GitHub - Thingjae98/bike_data

Contribute to Thingjae98/bike_data development by creating an account on GitHub.

github.com

 

 

🔍 프로젝트 개요

서울시 공공자전거 '따릉이' 데이터를 활용해 자전거 이용 패턴을 분석하고, 날씨 및 시간 정보를 기반으로 수요를 예측하는 프로젝트를 진행했습니다.
이 글에서는 데이터 수집, 전처리, 시각화, 모델링 및 결과 분석까지의 전체 흐름을 Python 기반으로 소개합니다.

 

🛠 사용 기술 스택

구분도구 및 기술
언어 Python 3
데이터 처리 pandas, numpy
시각화 matplotlib, seaborn, folium
머신러닝 scikit-learn, XGBoost
환경 관리 requirements.txt
파일 관리 CSV 데이터 (자전거 이용 + 기상청 날씨), 병합 및 전처리

 

📁 데이터 소개

1. 자전거 이용 데이터

  • 대여일시(24년 12월), 대여소명, 성별, 연령대, 운동량, 탄소량, 이동거리, 이용시간 등 포함
  • 시간대, 요일, 계절 등의 파생변수 추가

2. 기상청 날씨 데이터 (서울)

  • 기온, 강수량, 풍속, 습도, 지면온도, 적설량 등
  • 기상청 기후통계 Open API를 통해 수집하거나 csv로 다운로드 가능

📝 두 데이터는 대여일시 기준으로 병합하여 df_merged에 저장

 

📊 주요 분석 내용

✅ 요일 및 시간대별 이용량 분석

📌 분석 인사이트:

  • 평일(특히 월요일·화요일)  7, 19시는 출퇴근 시간대에 이용량이 집중되었습니다.
  • 주말은 오후 13~17시 사이에 이용이 많았으며, 레저 목적의 사용이 많은 것으로 보입니다.
# 히트맵: 요일 vs 시간대
pivot = df_merged.groupby(['요일', '시간대']).size().unstack().fillna(0)
order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
pivot = pivot.reindex(order)
plt.figure(figsize=(12,6))
sns.heatmap(pivot, cmap='YlGnBu')
plt.title('요일/시간대별 이용량')

 

 

 

 

✅ 인기 대여소 분석 (Top 10)

📌 분석 인사이트:

  • 상위 대여소는 주요 지하철역에 집중되어 있었습니다.
  • 마곡나루역, 영등포구청역 등 유동 인구가 많은 지역이 대여 중심 허브 역할을 하고 있습니다.
# 대여 기준 인기 대여소 Top 10
top_stations = df_merged["대여소명"].value_counts().head(10)

print("🚲 대여 기준 Top 10 대여소:")
print(top_stations)

# 시각화
import matplotlib.pyplot as plt

top_stations.plot(kind="barh", figsize=(8, 5), color="teal")
plt.gca().invert_yaxis()
plt.title("Top 10 인기 대여소")
plt.xlabel("대여 횟수")
plt.tight_layout()

 

 

 

✅ 날씨와의 상관관계 분석

📌 분석 인사이트:

  • 기온이 올라갈수록 이용건수도 증가하는 경향이 있었으며,
  • 반대로 강수량과 풍속이 높을수록 이용이 감소하는 음의 상관관계를 보였습니다.
  • 이는 날씨 정보가 수요 예측에 중요한 피처임을 의미합니다.
# 대여일시에서 날짜별 대여건수 집계
df_bike['날짜'] = pd.to_datetime(df_bike['날짜시간']).dt.date  # 대여일시에서 날짜 추출
daily_bike = df_bike.groupby('날짜').size().reset_index(name='대여건수')

# 날씨 데이터의 날짜 형식 변환
df_weather['날짜시간'] = pd.to_datetime(df_weather['날짜시간']).dt.date

# 날별 이용량과 날씨 데이터 병합
merged = pd.merge(daily_bike, df_weather, left_on='날짜', right_on='날짜시간', how='inner')

# 상관관계 확인
correlation = merged.corr(numeric_only=True)
print(correlation)

 

 

✅ 성별 연령대별 이용건수

📌 분석 인사이트:

  • 나이대별 이용건수는 20~40대에 집중되어 있었습니다.
  • 성별의 경우 여성보다는 남성 사용자가 더 많은것으로 보입니다.
# 성별-연령대별 이용건수 합계
pivot = df_merged.groupby(['연령대코드', '성별'])['이용건수'].sum().unstack()

pivot.plot(kind='bar', stacked=False, figsize=(8, 5), title='성별/연령대별 이용건수')
plt.ylabel("이용건수")
plt.xticks(rotation=0)

 


✅ 대여 유형별 이용 비율, 시간

📌 분석 인사이트:

  • 대부분의 사용자는 정기권에 구독중에 있었습니다.
  • 오히려 사용시간은 정기권이 반복되는 사용으로 간결하게 사용하고, 일일권이나 비회원으로 사용하는 사람의 시간이 길었습니다.
# 대여구분코드별 이용건수 합계
usage_by_type = df_merged.groupby("대여구분코드")["이용건수"].sum().sort_values(ascending=False)

 

 

🤖 예측 모델링 (XGBoost 회귀)

# 특성 및 목표 변수 선택
X = df[['기온(°C)', '강수량(mm)', '시간대', '풍속(m/s)']]
y = df['이용건수']

# 훈련/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# XGBoost 모델 학습
model = xgb.XGBRegressor(random_state=42)
model.fit(X_train, y_train)

# 예측
y_pred = model.predict(X_test)

# 성능 평가 (MAE)
mae = mean_absolute_error(y_test, y_pred)
 

📌 결과 해석:

  • MAE가 약 0.1327로, 예측값과 실제값의 차이가 평균적으로 0.13건밖에 되지 않음 → 모델 성능 우수
  • 날씨와 시간대만으로도 상당히 정확한 수요 예측이 가능함을 확인

 

🤖 클러스터링

# 필요한 컬럼 선택
df_cluster = df[['대여소명', '시간대', '요일']]
df_cluster['요일'] = df_cluster['요일'].astype('category').cat.codes  # 요일을 숫자로 변환

# 군집화 (KMeans)
kmeans = KMeans(n_clusters=3, random_state=42)  # 출퇴근, 관광 등 3개의 군집
df_cluster['cluster'] = kmeans.fit_predict(df_cluster[['시간대', '요일']])

📌 결과 해석:

  • 오히려 요일, 시간대로 군집화에는 실패했는데 이용건수가 해당 사용자의 이용건수라서 군집화에 오류를 발생시킨것으로 보임.

 

📌 결론 및 다음 단계

  • 시간대, 날씨, 요일 등을 활용한 예측 모델은 수요 예측에 효과적
  • 대여소 위경도와 함께 분석 시 출퇴근/관광 패턴까지 파악 가능
  • 향후 Google Mobility 데이터, 교통량 등 외부 데이터와 결합도 고려
  • 군집화(클러스터링)와 추천시스템을 통한 서비스 최적화도 가능성 있음