머신러닝/캐글

[필사] Bike Sharing Demand

have a good time 2021. 11. 8. 16:29

# 코드 1

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

import missingno as msno
plt.style.use('seaborn')

import warnings
warnings.filterwarnings("ignore")

mpl.rcParams['axes.unicode_minus'] = False

%matplotlib inline

코드 설명

 

 

mpl.rcParams['axes.unicode_minus'] = False

-> 그래프에서 마이너스 폰트가 깨지는 문제 대처

 

import missingno as msno

-> 결측치 시각화

 

%matplotlib inline

-> 주피터 노트북을 실행한 브라우저에서 바로 그림 볼 수 있게 함

 

 

# 코드 2

df_train = pd.read_csv("Kaggle/Bike Sharing Demand/train.csv",parse_dates=["datetime"])
df_test = pd.read_csv("Kaggle/Bike Sharing Demand/test.csv",parse_dates=["datetime"])

 

parse_dates=["datetime"]

-> datetime 칼럼을 datetime 형태로 읽는다.

 

 

# 코드 3

df_train.head()

 

  datetime season holiday workingday weather temp atemp humidity windspeed casual registered count
0 2011-01-01 00:00:00 1 0 0 1 9.84 14.395 81 0.0 3 13 16
1 2011-01-01 01:00:00 1 0 0 1 9.02 13.635 80 0.0 8 32 40
2 2011-01-01 02:00:00 1 0 0 1 9.02 13.635 80 0.0 5 27 32
3 2011-01-01 03:00:00 1 0 0 1 9.84 14.395 75 0.0 3 10 13
4 2011-01-01 04:00:00 1 0 0 1 9.84 14.395 75 0.0 0 1 1

# 코드 4

df_train.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10886 entries, 0 to 10885
Data columns (total 12 columns):
 #   Column      Non-Null Count  Dtype         
---  ------      --------------  -----         
 0   datetime    10886 non-null  datetime64[ns]
 1   season      10886 non-null  int64         
 2   holiday     10886 non-null  int64         
 3   workingday  10886 non-null  int64         
 4   weather     10886 non-null  int64         
 5   temp        10886 non-null  float64       
 6   atemp       10886 non-null  float64       
 7   humidity    10886 non-null  int64         
 8   windspeed   10886 non-null  float64       
 9   casual      10886 non-null  int64         
 10  registered  10886 non-null  int64         
 11  count       10886 non-null  int64         
dtypes: datetime64[ns](1), float64(3), int64(8)
memory usage: 1020.7 KB

 

 

 

 

-> train 데이터 정보 확인

칼럼들이 datetime, 정수, 실수형으로 이루어짐

 

# 코드 5

for col in df_train.columns: 
    msperc = 'column: {:>10}\t Percent of NaN value: {:.2f}%'.format(col, 100 * (df_train[col].isnull().sum() / df_train[col].shape[0])) 
    print(msperc)

 

코드 설명

 

msperc = 'column: {:>10}\t Percent of NaN value: {:.2f}%'.format(col, 100 * (df_train[col].isnull().sum() / df_train[col].shape[0])) 

 

-> \t : 탭

 

-> .format(col, 100 * (df_train[col].isnull().sum() / df_train[col].shape[0])) 

이 값들이 각각 

{:>10}, .2f 형식에 맞춰서 출력됨

1) {:>10} : 10칸 공간, 오른쪽 정렬

만약 {K:>10} 이였다면, 빈 공간을 K로 채움. 여기서는 K가 빈칸이므로 빈 공간은 그냥 빈 공간으로 놔둠

결과값을 보면 registered은 글자수가 10개라서, 빈 공간 없이 10칸을 채우고 있고

datetime 은, 글자수가 8개라서, 2개의 빈 공간을 두고 오른쪽 정렬하고 있음

 

참고자료: https://jeongwookie.github.io/2018/09/10/180911-code-alignment-using-format-function/

 

2) 2f : f는 실수 표현(2f는 소수점 2자리까지 표현

 

-> 100 * (df_train[col].isnull().sum() / df_train[col].shape[0])) 

shape[0] : 전체 행의 개수

shape[1] : 전체 열의 개수

 

즉 각 칼럼(예를 들어 season 칼럼)에 대해

100 * null값이 있는 행의 개수/전체 행의 개수

계산

 

 

 

 

결과 :

 

column:   datetime  Percent of NaN value: 0.00%
column:     season  Percent of NaN value: 0.00%
column:    holiday  Percent of NaN value: 0.00%
column: workingday  Percent of NaN value: 0.00%
column:    weather  Percent of NaN value: 0.00%
column:       temp  Percent of NaN value: 0.00%
column:      atemp  Percent of NaN value: 0.00%
column:   humidity  Percent of NaN value: 0.00%
column:  windspeed  Percent of NaN value: 0.00%
column:     casual  Percent of NaN value: 0.00%
column: registered  Percent of NaN value: 0.00%
column:      count  Percent of NaN value: 0.00%

 

 

# 코드 6

msno.matrix(df_train, figsize=(12,5))

결측치 시각화

-> 모두 꽉 차 있음 : 결측치 없음

# 코드 7

f, ax = plt.subplots(1, 1, figsize = (10,6)) 
g = sns.distplot(df_train["count"], color = "b", label="Skewness: {:2f}".format(df_train["count"].skew()), ax=ax) 
g = g.legend(loc = "best") 

print("Skewness: %f" % df_train["count"].skew()) 
print("Kurtosis: %f" % df_train["count"].kurt())

 

count(타켓변수)의 Skewness(왜도), Kurtosis(첨도)

 

1)

Skewness(비대칭도) : 비대칭도. 분포가 얼마나 비대칭을 이루는가.

Skewness= 0이면 정규분포. Skewness<0 이면 오른쪽으로 치우침, Skewness>0 이면 왼쪽으로 치우침

 

2) 

Kurtosis(첨도) : 확률분포의 뾰족한 정도

관측값들이 어느 정도 집중적으로 중심에 몰려 있는가 측정

-> 이 자료에서는 0에 몰려 있음을 알 수 있음

 

 

코드 설명

 

f, ax = plt.subplots(1, 1, figsize = (10,6)) 

 

subplots() 함수로 그래프 그릴 자리를 만듦

 

 

예를 들어

f,ax = plt.subplots(ncols=2) 라면, 아래와 같은 형태

 

 

만약,

f, ax = plt.subplots(nrows=2, ncols=3) 이라면, 아래와 같음

 

 

 

 

참고 자료 :

https://mindscale.kr/course/python-visualization-basic/concept/

 

https://dailyheumsi.tistory.com/36

 

 

 

g = sns.distplot(df_train["count"], color = "b", label="Skewness: {:2f}".format(df_train["count"].skew()), ax=ax) 

-> sns.distplot(df_train["count"]) : df_train["count"] 에 대해 히스토그램(막대), 커널밀도곡선(곡선) 을 그림

 

참고자료: https://rfriend.tistory.com/409

 

g = g.legend(loc = "best") 

-> 범례 추가

 

 

 

 

# 코드 8

df_train["year"] = df_train["datetime"].dt.year 
df_train["month"] = df_train["datetime"].dt.month 
df_train["day"] = df_train["datetime"].dt.day 
df_train["hour"] = df_train["datetime"].dt.hour 
df_train["minute"] = df_train["datetime"].dt.minute 
df_train["second"] = df_train["datetime"].dt.second 

df_test["year"] = df_test["datetime"].dt.year 
df_test["month"] = df_test["datetime"].dt.month 
df_test["day"] = df_test["datetime"].dt.day 
df_test["hour"] = df_test["datetime"].dt.hour 
df_test["minute"] = df_test["datetime"].dt.minute 
df_test["second"] = df_test["datetime"].dt.second 

df_train.shape

dt.year, dt.month 등을 사용하여,

datetime 형태의 값을 연, 월 .. 로 추출

그 결과 칼럼의 수가 18개로 늘어남

 

결과 :

(10886, 18)

 

# 코드 9 

figure, ((ax1,ax2,ax3),(ax4,ax5,ax6)) = plt.subplots(nrows = 2, ncols = 3) 
figure.set_size_inches(18,10) 

sns.barplot(data=df_train, x = "year", y = "count", ax = ax1) 
sns.barplot(data=df_train, x = "month", y = "count", ax = ax2) 
sns.barplot(data=df_train, x = "day", y = "count", ax = ax3) 
sns.barplot(data=df_train, x = "hour", y = "count", ax = ax4) 
sns.barplot(data=df_train, x = "minute", y = "count", ax = ax5) 
sns.barplot(data=df_train, x = "second", y = "count", ax = ax6) 

ax1.set(ylabel = "count", title = "Rental amount by year") 
ax2.set(ylabel = "count", title = "Rental amount by month") 
ax3.set(ylabel = "count", title = "Rental amount by day") 
ax4.set(ylabel = "count", title = "Rental amount by hour")

코드 설명

 

figure.set_size_inches(18,10) 

-> 인치 단위로 크기 조정

 

 

 

결과 : 

 

 

 

 

1. 연도별 대여량 : 11년도 대여량< 12년도 대여량

2. 월별 대여량 : 대체로 겨울 < 여름

3. 일별 대여량 : 1~19 일까지의 데이터만 포함됨. 나머지는 test데이터에 있음 (일단 보류)

4. 시간별 대여량 : 오전 8시, 오후 5시, 오후 6시에 높음 -> 출퇴근 시간 이용량이 높은 것으로 보임

 

# 코드 10

df_train["dayofweek"] = df_train["datetime"].dt.dayofweek 
df_test["dayofweek"] = df_test["datetime"].dt.dayofweek 
df_train.shape

요일 변수를 추가로 생성하여, 칼럼 수가 19개가 됨

 

 

결과 : 

(10886, 19)

 

# 코드 11

df_train["dayofweek"].value_counts()

 

 

결과 :

 

0 : 월요일 ...  6: 일요일

 

-> 5(토요일), 6(일요일) 대여량이 조금 더 많음 

 

# 코드 12

fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(nrows = 5) 
fig.set_size_inches(18,25) 

sns.pointplot(data = df_train, x = "hour", y = "count", ax = ax1) 
sns.pointplot(data = df_train, x = "hour", y = "count", hue = "workingday", ax = ax2) 
sns.pointplot(data = df_train, x = "hour", y = "count", hue = "dayofweek", ax = ax3) 
sns.pointplot(data = df_train, x = "hour", y = "count", hue = "weather", ax = ax4) 
sns.pointplot(data = df_train, x = "hour", y = "count", hue = "season", ax = ax5)

코드 설명

 

sns.pointplot(data = df_train, x = "hour", y = "count", hue = "workingday", ax = ax2) 

-> 두 번째 그림

x축 : hour , y축 : count , 

각 workingday 에 따라(즉, 0과1) 시각화

 

두번째 그림 :

근무일(1) - 출퇴근 시간에 집중

근무일 x (0) - 낮시간에 전체적으로 평이

 

 

세번째 그림 : 

토,일(5,6) - 낮시간에 전체적으로 평이

나머지 요일 - 출퇴근 시간에 집중

 

 

네번째 그림:

맑은날 - 가장 높은 대여량

그 다음으로 안개, 가벼운 눈비, 악천후 순

 

 

다섯번째 그림 :

봄 - 가장 적은 대여량

여름, 가을, 겨울 - 조금씩 차이가 남

 

 

# 코드 13

corr_data = df_train[["temp", "atemp", "casual", "registered", "humidity", "windspeed", "count"]] 

colormap = plt.cm.PuBu 

f , ax = plt.subplots(figsize = (12,10)) 
plt.title('Correlation of Numeric Features with Rental Count',y=1,size=18) 
sns.heatmap(corr_data.corr(), vmax=.8, linewidths=0.1,square=True,annot=True,cmap=colormap, 
            linecolor="white",annot_kws = {'size':14})

 

 

 

코드 설명

 

corr_data = df_train[["temp", "atemp", "casual", "registered", "humidity", "windspeed", "count"]] 

-> df_train 의 여러 컬럼 중에서, temp, atemp,..., count 를 이용해서 corr_data를 만듦

 

 

 

colormap = plt.cm.PuBu 

-> matplot 에는 여러 개의 색을 섞은 colormap이 있음

그 중에서 PuBu 를 사용

 

참고자료 : https://jrc-park.tistory.com/155

 

heatmap : 데이터들의 배열을 색상으로 표현해줌

매개변수 : 

sns.heatmap(

corr_data.corr(), # 사용할 데이터

vmax=.8, # 최대값

linewidths=0.1, # cell 사이에 선을 넣음

square=True,

annot=True, # 각 cell의 값 표기 유무

cmap=colormap, # 히트맵의 색
linecolor="white", # cell 사이 선의 색깔

annot_kws = {'size':14})

 

 

참고 자료 : https://dsbook.tistory.com/51

 

 

 

 

 

 

count 열에서 가장 진한 변수는 registered ( 그런데 이 변수는 test 데이터에 존재하지 않음)

 

그 다음으로 상관관계 높은 변수 casual

그 외 온도, 습도, 풍속은 count 와 거의 관계가 없음

 

단, temp(온도)와 atemp(체감온도)는 서로 상관계수가 매우 높음

-> 다중공선성(일부 예측 변수가 다른 예측 변수와 상관 정도가 높아서 부정적 영향을 미침)

참고 자료 : https://terms.naver.com/entry.naver?docId=3404410&cid=40942&categoryId=32211

 

 

# 코드 14

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

temp_scatter_plot = pd.concat([df_train['count'],df_train['temp']],axis = 1) 
sns.regplot(x='temp',y = 'count',data = temp_scatter_plot,scatter= True, fit_reg=True, ax=ax1) 
windspeed_scatter_plot = pd.concat([df_train['count'],df_train['windspeed']],axis = 1) 
sns.regplot(x='windspeed',y = 'count',data = windspeed_scatter_plot,scatter= True, fit_reg=True, ax=ax2) 
humidity_scatter_plot = pd.concat([df_train['count'],df_train['humidity']],axis = 1) 
sns.regplot(x='humidity',y = 'count',data = humidity_scatter_plot,scatter= True, fit_reg=True, ax=ax3)

코드 설명

 

 

temp_scatter_plot = pd.concat([df_train['count'],df_train['temp']],axis = 1) 

-> pd.concat : 데이터 프레임을 이어 붙여줌

df_train의 count 칼럼과 temp 칼럼을 이어 붙임

 

예를 들어서 

 

df1 :

 

     a    b    c

0  가1 가2 가3

1  가4 가5 가6

 

 

df2 :

 

    a    b     c    d

0  가1  가2  가3  가4

1  가5  가6  가7  가8

2  가9 가10 가11 가12

 

 

pd.concat([df1,df2])

 

    a     b      c     d

0  가1  가2   가3   NaN

1  가4  가5   가6   NaN

2  가1  가2   가3   가4

3  가5  가6   가7   가8

4  가9  가10  가11 가12

 

참고 자료 : https://yganalyst.github.io/data_handling/Pd_12/

 

 

sns.regplot(x='temp',y = 'count',data = temp_scatter_plot,scatter= True, fit_reg=True, ax=ax1) 

-> sns.regplot : scatter plot 과 line plot 을 함께 볼 수 있는 데이터 시각화 방법

 

참고자료 : https://rk1993.tistory.com/entry/Pythonseaborn-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%8B%9C%EA%B0%81%ED%99%94-regplot-lmplot-catplot-swarmplot

 

 

결과 : 

 

heatmap에서 유의하지 않다고 판단된 변수에 대해 그래프를 그려봄

windspeed : 0에 데이터가 많이 몰려 있음

일반적으로 풍속이 0 인 경우는 적으므로, Null 데이터를 0으로 대체한 것인지 생각해볼 수 있음

 

# 코드 15

fig, axes = plt.subplots(nrows = 2, figsize = (18,14)) 

plt.sca(axes[0]) 
plt.xticks(rotation = 30, ha = "right") 
axes[0].set(ylabel = "count", title = "train windspeed") 
sns.countplot(data = df_train, x = "windspeed", ax = axes[0]) 

plt.sca(axes[1]) 
plt.xticks(rotation = 30, ha = "right") 
axes[1].set(ylabel = "count", title = "test windspeed") 
sns.countplot(data = df_test, x = "windspeed", ax = axes[1])

 

코드 설명

plt.sca(axes[0]) 

-> current Axes를 axes[0] 으로 설정

 

 

plt.xticks(rotation = 30, ha = "right") 

-> plt.xticks 로 x축 눈금 위치, 레이블 속성 등을 가져오거나 설정

rotation :  x축 레이블을 시계 반대 방향으로 회전시키는 각도

ha = "right" : 레이블의 오른쪽 끝을 눈금에 맞춤

 

 

sns.countplot : 항목별 갯수를 나타냄

 

 

windspeed에 대한 자세한 시각화를 했음

-> 0 값이 많음 : 고쳐줄 것

 

 

 

# 코드 16

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

df_train["year_month"] = df_train["datetime"].apply(concatenate_year_month) 
df_test["year_month"] = df_test["datetime"].apply(concatenate_year_month) 

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

 

코드 설명

 

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

-> datetime.year 값이 {0}

datetime.month 값이 {1} 로 들어감

즉, return datetime.year - datetime.month 가 됨

 

참고 자료 : https://blockdmask.tistory.com/424

 

df_train["year_month"] = df_train["datetime"].apply(concatenate_year_month) 

->

apply 함수 사용법

: apply(함수, axis=0 or 1)

첫번째 인자 : 적용하려는 함수 이름

두번째 인자 : 함수를 열(0)로 적용, 행(1)으로 적용 (기본값은 0)

 

 

참고자료 : https://zephyrus1111.tistory.com/90

 

 

결과 :

 

 

연도별 데이터의 대여 변화를 자세히 보기 위해 year_month 칼럼에 year와 month를 표시함

 

 

# 코드 17

fig, ax = plt.subplots(figsize = (18,4)) 
sns.barplot(data = df_train, y = "count", x = "year_month")

 

결과 : 

앞서 2011년에 비해 2012년의 대여량이 높았었음.

세분화해서 월별로 시각화했더니,

두 연도 모두 따뜻한 계절의 대여량이 더 많음, 전체적으로 갈수록 대여량 증가 추세

 

 

# 코드 18 

train 데이터 셋의 이상치 제거(IQR 사용)

from collections import Counter 

def detect_outliers(df, n, features): 
    outlier_indices = [] 
    for col in features: 
        Q1 = np.percentile(df[col], 25) 
        Q3 = np.percentile(df[col], 75) 
        IQR = Q3 - Q1 
        
        outlier_step = 1.5 * IQR 
        
        outlier_list_col = df[(df[col] < Q1 - outlier_step) | (df[col] > Q3 + outlier_step)].index 
        outlier_indices.extend(outlier_list_col) 
        
    outlier_indices = Counter(outlier_indices) 
    multiple_outliers = list(k for k, v in outlier_indices.items() if v > n) 
    
    return multiple_outliers 

Outliers_to_drop = detect_outliers(df_train, 2, ["temp", "atemp", "casual", "registered", "humidity", "windspeed", "count"])

코드 설명 

 

 

 outlier_list_col = df[(df[col] < Q1 - outlier_step) | (df[col] > Q3 + outlier_step)].index 

-> df의 칼럼중에, Q1-1.5*IQR 이거나 Q3+1.5*IQR 인 것들의 index

 

 outlier_indices.extend(outlier_list_col) 

-> 리스트  outlier_indices 에다가 outlier_list_col값 추가

 

 

 

** IQR 설명(Interquartile range)

Q3-Q1(사분위수 상위 75% 지점 - 하위 25% 지점의 값 차이)을 의미

 

박스 안이 노란줄 : median(중앙값)

박스의 왼쪽 : Q1

박스의 오른쪽 : Q3

 

IQR = Q3-Q1

최소 제한선 : Q1-1.5*IQR

최대 제한선 : Q3 + 1.5*IQR

 

이상치 : 최소 제한선 미만, 최대 제한선 초과값

 

즉 위의 코드에서 IQR을 이용해서 이상치 탐색

 

참고 자료 : https://hong-yp-ml-records.tistory.com/15

 

# 코드 19

 

df_train.loc[Outliers_to_drop]

결과 : (일부분만 출력)

 

 

 

참고 자료 :

https://hong-yp-ml-records.tistory.com/76?category=823206 

 

[Bike Sharing Demand] 캐글 자전거 수요예측 Part 3

https://www.kaggle.com/kongnyooong/bike-sharing-demand-for-korean-beginners [Bike Sharing Demand] for Korean Beginners (한글커널) Explore and run machine learning code with Kaggle Notebooks | Using..

hong-yp-ml-records.tistory.com