인공지능

서울시 자전거 수요 데이터(Seoul Bike Sharing) 분석 & 시각화+상관관계 분석

존카터 2025. 9. 3. 10:31

 

 

데이터셋: UCI - Seoul Bike Sharing Demand · 파일: SeoulBikeData.csv (약 0.58MB)

목표: 기본 EDA(결측/중복/범주 분포)와 간단한 시각화(막대/원형/히스토그램/산점도)를 통해 날씨·계절 등 요소와 대여량의 관계를 직관적으로 파악한다.

1) 데이터 로드

import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("SeoulBikeData.csv", encoding="cp949")  # 파일 읽기 (한글 인코딩)
print(df.shape)      # (행, 열) → 예: (8760, 14)
print(df.columns)    # 컬럼명
print(df.head())     # 상위 5행 미리보기

2) 결측치 · 중복값 확인

print(df.isnull().sum())
# 예시 출력
# Date                        0
# Rented Bike Count           0
# Hour                        0
# Temperature(캜)              0
# Humidity(%)                 0
# Wind speed (m/s)            0
# Visibility (10m)            0
# Dew point temperature(캜)    0
# Solar Radiation (MJ/m2)     0
# Rainfall(mm)                0
# Snowfall (cm)               0
# Seasons                     0
# Holiday                     0
# Functioning Day             0

df.duplicated().sum()     # 중복 행 개수 (예: 0)
# 같은 행을 지울 때: df = df.drop_duplicates()

3) 범주형 변수 확인

왜 필요할까?

  • 데이터 품질: 대소문자/띄어쓰기/오타로 인해 값이 쪼개지는 문제 방지 (예: Winter vs winter)
  • 분포 불균형: 공휴일/계절별 샘플이 치우치면 모델 학습에 영향
  • 인코딩 준비: 원-핫 인코딩 전 값 분포/범주 수 파악
# 값 종류/분포
print(df["Seasons"].unique()); print(df["Seasons"].value_counts())
print(df["Holiday"].unique()); print(df["Holiday"].value_counts())
print(df["Functioning Day"].unique()); print(df["Functioning Day"].value_counts())

# 문자열 정리 예시
df["Seasons"] = df["Seasons"].str.strip().str.capitalize()
df["Holiday"] = df["Holiday"].str.strip().str.title()
df["Functioning Day"] = df["Functioning Day"].str.strip().str.title()

# 오타 교정 예시
df["Seasons"] = df["Seasons"].replace({"Wniter": "Winter", "Sprng": "Spring"})

4) 시각화

4-1. 계절 분포 (막대그래프)

df["Seasons"].value_counts().plot(kind="bar")
plt.title("Seasons Distribution")
plt.xlabel("Season"); plt.ylabel("Count")
plt.show()

4-2. 공휴일 분포 (원형 그래프)

df["Holiday"].value_counts().plot(kind="pie", autopct="%1.1f%%")
plt.title("Holiday Distribution"); plt.ylabel("")  # y축 라벨 제거
plt.show()

4-3. 습도 분포 (히스토그램 + KDE)

df["Humidity(%)"].plot(kind="hist", bins=30, density=True, alpha=0.6, edgecolor="black")
df["Humidity(%)"].plot(kind="kde")
plt.title("Humidity Distribution with Density")
plt.xlabel("Humidity (%)")
plt.show()

4-4. 습도 vs 대여량 (산점도)

plt.scatter(df["Humidity(%)"], df["Rented Bike Count"], alpha=0.3)
plt.title("Humidity vs Bike Rentals")
plt.xlabel("Humidity (%)"); plt.ylabel("Rented Bike Count")
plt.show()


컬럼간 상관관계 분석

 

 

 

 

 

 

공분산, 상관계수, p-value 완전 이해 가이드

상관분석을 할 때 많이 듣는 세 가지 키워드: 공분산, 상관계수, p-value. 이 글에서 개념·수식·파이썬 예제까지 한 번에 정리합니다.

핵심 요약
공분산은 “함께 변하는 정도(크기와 방향)”를 원 단위로 나타내고, 상관계수는 그 공분산을 두 변수의 표준편차로 표준화해 -1~+1로 만든 수치입니다. p-value는 “관측된 상관계수가 우연으로도 나올 수 있는가?”를 확률로 검정합니다.

1) 공분산(Covariance): 함께 변하는 방향과 크기

정의 (모집단/표본 버전):

\[ \mathrm{Cov}(X,Y)=\mathbb{E}\big[(X-\mu_X)(Y-\mu_Y)\big],\qquad \widehat{\mathrm{Cov}}(X,Y)=\frac{1}{n-1}\sum_{i=1}^n (x_i-\bar x)(y_i-\bar y) \]

  • 부호: 양수면 함께 증가(양의 관계), 음수면 한쪽↑ 다른쪽↓(음의 관계).
  • 크기: 단위의 영향을 받습니다(예: °C ↔ °F로 바꾸면 값이 달라짐).
  • 해석: 방향 파악에는 좋지만, 단위 의존성 때문에 크기 비교엔 부적합.

2) 상관계수(Correlation): 공분산의 표준화

피어슨 상관계수 \(r\)는 공분산을 표준편차로 나눠서 -1~+1 스케일로 만든 값입니다.

\[ r_{X,Y}=\frac{\mathrm{Cov}(X,Y)}{\sigma_X\,\sigma_Y},\qquad -1\le r\le 1 \]

  • \(r\approx +1\): 거의 직선적 양의 선형관계
  • \(r\approx -1\): 거의 직선적 음의 선형관계
  • \(r\approx 0\): 선형 관계가 거의 없음 (비선형은 있을 수 있음)

언제 피어슨을 쓰나?

  • 두 연속형 변수의 선형 관계를 보고 싶을 때
  • 이상치 영향이 크지 않을 때
  • (가정) 대략적인 정규성/등분산성/독립성

대안(견고/단조)

  • 스피어만(ρ): 값 → 순위로 바꿔 상관. 단조 관계/이상치에 강함.
  • 켄달(τ): 순서쌍 일치/불일치 기반. 표본 작을 때 더 견고.

3) p-value: “이 상관이 우연일 확률은?”

가설: \(H_0:\rho=0\) (모집단 상관계수=0). 표본 상관 \(r\)로 다음 통계를 계산합니다.

\[ t=\frac{r\sqrt{n-2}}{\sqrt{1-r^2}}\ \sim\ t\text{-분포(df=}n-2\text{)} \quad (피어슨, 근사 가정 하) \]

이때 양쪽 검정 p-value는 \(2\cdot P(T\_{n-2}\ge |t|)\).
p-value가 작을수록 “\(r\neq 0\)”을 뒷받침하는 증거가 강합니다. 관례상 p < 0.05면 유의미로 간주.

주의 · p-value는 “효과의 크기”가 아니라 “우연일 가능성”. 표본이 매우 크면 아주 작은 \(r\)도 유의해질 수 있습니다. 항상 효과크기(상관계수)실무적 중요도를 함께 보세요.

4) 파이썬으로 실무 적용 (pandas / SciPy)

4.1 기본 상관행렬 (피어슨)

corr = df.corr(numeric_only=True, method="pearson")
print(corr)  # 숫자형 컬럼들 간 상관계수 표

4.2 타깃과의 상관만 보기

target = "Rented Bike Count"  # 또는 리네임한 "RentedBikeCount"
corr_to_target = corr[target].drop(labels=[target]).sort_values(ascending=False)
print(corr_to_target.head(5))   # 양의 상관 TOP5
print(corr_to_target.tail(5))   # 음의 상관 TOP5

4.3 상관 + 유의확률(p-value)

from scipy.stats import pearsonr, spearmanr

x = df["Humidity(%)"]
y = df["Rented Bike Count"]  # or "RentedBikeCount"

# 피어슨 (선형)
r, p = pearsonr(x, y)
print(f"Pearson r = {r:.3f}, p-value = {p:.3g}")

# 스피어만 (순위/단조, 이상치에 강함)
rho, p2 = spearmanr(x, y)
print(f"Spearman rho = {rho:.3f}, p-value = {p2:.3g}")

4.4 히트맵(순수 matplotlib)

import numpy as np
import matplotlib.pyplot as plt

labels = corr.columns.tolist()
fig, ax = plt.subplots(figsize=(10,8))
im = ax.imshow(corr.values, vmin=-1, vmax=1)
ax.set_xticks(np.arange(len(labels))); ax.set_xticklabels(labels, rotation=45, ha="right")
ax.set_yticks(np.arange(len(labels))); ax.set_yticklabels(labels)
plt.colorbar(im, ax=ax)
for i in range(len(labels)):
    for j in range(len(labels)):
        ax.text(j, i, f"{corr.values[i, j]:.2f}", ha="center", va="center", fontsize=7)
plt.title("Correlation Heatmap (Pearson)")
plt.tight_layout(); plt.show()

5) 공분산 vs 상관계수 한눈 비교

지표 의미 범위/단위 장점 주의점
공분산 Cov(X,Y) 함께 변하는 정도(방향·크기) 제한 없음 / 단위 의존 방향 파악 쉬움 스케일 바뀌면 값도 변해 크기 비교 어려움
피어슨 상관 r 공분산의 표준화(선형 관계) [-1, +1] / 무단위 스케일 무관, 해석 직관적 이상치에 민감, 비선형 관계는 놓침

6) 해석 & 실무 팁

  • 상관 ≠ 인과: 제3의 변수(교란) 가능성 항상 고려.
  • 이상치: 몇 점이 r을 크게 왜곡할 수 있음 → 산점도/박스플롯으로 진단, 스피어만 병행.
  • 비선형: r≈0이라도 곡선 관계가 있을 수 있음 → 산점도/변환(로그·제곱)/비선형 모델 고려.
  • 다중공선성: 설명변수끼리 높은 상관이 모델 안정성을 해칠 수 있음 → 변수 선택/PCA/정규화 고려.
  • 다중검정: 많은 변수쌍을 동시에 테스트하면 우연히 유의한 결과가 늘어남 → FDR/보정 고려.

여기서 다중 공선성이란 회귀 분석이나 머신러닝에서 강한 상관관계를 가진 변수들로 인해 모델이 둘다 필요하지 않다고 착각하게 되는 것을 말한다

예를들어 습도와 이슬점 온도는 매우 강한 상관관계를 가지고 있다(둘다 공기준 수분과 온도간의 관계이기에) 

이때 VIF(분산팽창성지수)를 통해 다중공선성을 파악해야한다.

VIF 값 해석

1 ~ 5 문제 없음 / 약한 다중공선성
5 ~ 10 다중공선성 의심, 주의 필요
10 ↑ 심각한 다중공선성 → 변수 제거/변환 고려

즉 각 컬럼별로 분산팽창성지수(VIF)를 찾고, 다중공선성이 확인되면 해당 요소를 없애주는 작업이 필요하다.