Python/데이터분석 실습

[Kaggle] 자전거 수요 예측 ① 데이터 확인 & EDA

dori_0 2022. 5. 3. 00:22

자전거 수요 예측

① 데이터 확인 & EDA

 

 

다음 데이터셋을 이용하였습니다.

www.kaggle.com/c/bike-sharing-demand/

 

Bike Sharing Demand

Forecast use of a city bikeshare system

www.kaggle.com

datetime - hourly date + timestamp

season - 1 = spring, 2 = summer, 3 = fall, 4 = winter

holiday - whether the day is considered a holiday

workingday - whether the day is neither a weekend nor holiday

weather

    1: Clear, Few clouds, Partly cloudy, Partly cloudy

    2: Mist + Cloudy, Mist + Broken clouds, Mist + Few clouds, Mist

    3: Light Snow, Light Rain + Thunderstorm + Scattered clouds, Light Rain Scattered clouds

    4: Heavy Rain + Ice Pallets + Thunderstorm + Mist, Snow + Fog

temp - temperature in Celsius

atemp - "feels like" temperature in Celsius

humidity - relative humidity

windspeed - wind speed

casual - number of non-registered user rentals initiated 

registered - number of registered user rentals initiated 

count - number of total rentals 

 


 

  1. 라이브러리 및 데이터 살펴보기   

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

from sklearn.metrics import make_scorer # RMSLE
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_validate
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import RandomForestRegressor

plt.rc('font', family='Malgun Gothic')
plt.rc('axes', unicode_minus=False)

import warnings
warnings.filterwarnings(action='ignore') 

train = pd.read_csv("C:/data/Bike/train.csv")
test = pd.read_csv("C:/data/Bike/test.csv")
submission = pd.read_csv("C:/data/Bike/sampleSubmission.csv")

 

먼저 데이터를 확인해보자

print(train.shape)
train.head()

print(test.shape)
test.head()

print(submission.shape)
submission.head()

train
test
submission

  • casual + registered = count
  • 우리가 예측할 변수는 count임을 확인할 수 있다.

 

 

train의 'datetime' 타입이 object 이므로 datetime 형태로 변경하였다 ( test도 함께 변경)

train.info()

# datetime 형태로 변경
train['datetime'] = pd.to_datetime(train['datetime'])
test['datetime'] = pd.to_datetime(test['datetime'])
train.info()

 

 

결측치와 기초통계량도 확인해보면 다음과 같다

train.isnull().sum()
train.describe()

  • 결측치는 존재하지 않는다

 

 


  2. EDA   

먼저 'datetime'을 연, 월, 일, 시, 분, 초 로 나누어 컬럼에 추가해주자 ( + 요일도 )

train['year'] = train['datetime'].dt.year
train['month'] = train['datetime'].dt.month
train['day'] = train['datetime'].dt.day
train['hour'] = train['datetime'].dt.hour
train['minute'] = train['datetime'].dt.minute
train['second'] = train['datetime'].dt.second
#월(0) 화(1) 수(2) 목(3) 금(4) 토(5) 일(6)
train['dayofweek'] = train['datetime'].dt.dayofweek

test['year'] = test['datetime'].dt.year
test['month'] = test['datetime'].dt.month
test['day'] = test['datetime'].dt.day
test['hour'] = test['datetime'].dt.hour
test['minute'] = test['datetime'].dt.minute
test['second'] = test['datetime'].dt.second
test['dayofweek'] = test['datetime'].dt.dayofweek

train.head()

 

2-1. 연도별

sns.barplot(data = train, x='year', y='count', palette="Set2")

  • 2011년보다 2012년의 대여량이 더 많다

 

연도-월 별로 더 세분화 해보기 위해 새로운 컬럼 year_month를 만들어주자

def con_year_month(datetime):
    return "{0}-{1}".format(datetime.year, datetime.month)

train["year_month"] = train["datetime"].apply(con_year_month)
test["year_month"] = test["datetime"].apply(con_year_month)

print(train.shape)
train[["datetime", "year_month"]].head()

 

plt.figure(figsize=(15, 5))
sns.barplot(x='year_month', y='count', data=train)

  • 전체적으로 2011년에서 2012년의 대여량은 증가하는 추세이다
  • 겨울보다 여름에 대여량이 많다

 

 

2-2. 월별

sns.barplot(data = train, x='month', y='count')

  • 1월에 가장 대여량이 적고 6~9월에 대여량이 가장 많다
  • 위에서 본 것과 같이 겨울보다는 여름에 대여량이 높음을 확인할 수 있다

 

 

2-3. 일별

sns.barplot(data = train, x='day', y='count')

  • 1~19일의 데이터만 있고 나머지는 test 데이터에 존재하기 때문에 이 컬럼은 나중에 제거할 것이다
  • 일별로 큰 차이가 있지는 않다

 

 

2-4. 요일별

sns.barplot(data = train, x='dayofweek', y='count')

월(0) 화(1) 수(2) 목(3) 금(4) 토(5) 일(6)

  • 일요일이 대여량이 가장 적기는 하지만, 전체적으로 대여량의 차이가 비슷하다

 

 

2-5. 계절별

sns.barplot(data = train, x='season', y='count', palette="Set2")

1 = spring, 2 = summer, 3 = fall, 4 = winter

  • 봄, 가을에 수요량이 가장 많을 것이라고 예상했는데 봄에 대여량이 가장 적었다
  • 여름과 가을에 자전거 대여량이 많다

 

 

겨울이 봄보다 대여량이 많은 것이 신기해서 season이 월별로 어떻게 나뉘었는지 확인해보았다.

pd.crosstab(train['season'], train['month'])

  • 봄 - 1, 2, 3월    / 여름 - 4, 5, 6월   / 가을 - 7, 8, 9월   / 겨울 - 10, 11, 12월
  • 온도가 낮은 1~3월이 spring으로 되어 있어서 대여량이 낮았던 것임을 확인하였다.

 

 

2-6. 근무 여부

sns.barplot(data = train, x='workingday', y='count', palette="Set2")

  • 근무를 하는 날과 휴일의 수요량 자전거 대여량 차이는 없는 것으로 보인다

 

 

2-7. 시간대별

plt.figure(figsize=(15, 5))
sns.pointplot(x='hour', y='count', data=train)

  • 출퇴근 시간인 8시, 17시~18시 경에 이용 횟수가 많다

 

 

시간대별로 근무 여부/요일/날씨/계절에 따라 어떤 차이가 있는지 확인해보자

fig, axes = plt.subplots(2, 1, figsize=(12, 8))

sns.pointplot(x='hour', y='count', hue='workingday', data=train, ax=axes[0], palette="Set2")
sns.pointplot(x='hour', y='count', hue='dayofweek', data=train, ax=axes[1], palette="Set2")

  • 근무일(월~금)에는 출퇴근 시간에 대여량이 많다
  • 휴일(토, 일)에는 12~17시 경에 대여량이 많다

 

 

fig, axes = plt.subplots(2, 1, figsize=(12, 8))

sns.pointplot(x='hour', y='count', hue='weather', data=train, ax=axes[0], palette="Set2")
sns.pointplot(x='hour', y='count', hue='season', data=train, ax=axes[1], palette="Set2")

Weather

    1: Clear, Few clouds, Partly cloudy, Partly cloudy

    2: Mist + Cloudy, Mist + Broken clouds, Mist + Few clouds, Mist

    3: Light Snow, Light Rain + Thunderstorm + Scattered clouds, Light Rain Scattered clouds

    4: Heavy Rain + Ice Pallets + Thunderstorm + Mist, Snow + Fog

Season

    1 = spring, 2 = summer, 3 = fall, 4 = winter

 

  • 날씨가 매우 안 좋을때는 대여량이 거의 없는 것으로 보인다.
  • 봄에 대여량이 가장 적고 나머지는 비슷하다

 

 

2-8. 상관계수

cor_train = train[["temp", "atemp", "humidity", "windspeed", "casual", "registered", "count"]]
cor_train.corr()

plt.figure(figsize=(15, 10))
sns.heatmap(cor_train.corr(), annot=True, cmap='Blues')

  • 온도, 습도, 풍속은 상관관계가 없는 것으로 보인다
  • temp와 atemp는 매우 높은 양의 상관관계를 보이는 것으로 보아 거의 같은 컬럼으로 생각해도 괜찮을 것 같다 
    • atemp 컬럼은 나중에 삭제하도록 하자

 

 

2-9. 온도, 바람세기, 습도 데이터 확인

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 5))

sns.regplot(data = train, x='temp', y='count', ax = ax1)
sns.regplot(data = train, x='windspeed', y='count', ax = ax2)
sns.regplot(data = train, x='humidity', y='count', ax = ax3)

  • windspeed(풍속) 이 0에 몰려있는 것으로 보아 결측치가 0으로 기록된 것이 아닐까 생각된다.

 

 

windspeed 컬럼만 확인해보면

# windspeed 확인
plt.figure(figsize=(15, 5))
sns.countplot(data = train, x='windspeed')

  • 많은 값이 0으로 존재한다. 이 값들은 Feature engineering 에서 고쳐주도록 하자