2020. 4. 2. 22:36ㆍ노트/Python : 프로그래밍
데이터 다운로드
https://www.kaggle.com/c/bike-sharing-demand/data
bike data 불러오기
이번 프로젝트는 Kaggle에 있는 공용 자전거 수요 데이터를 학습하여, 각 날짜마다 자전거 수요를 예측해보는 프로젝트에 대해 포스팅하려고 한다.
먼저 트레이닝 데이터와 테스트 데이터를 불러온다.
train=pd.read_csv("train.csv", parse_dates=['datetime'])
#pare_dates: 날짜 시간으로된 컬럼을 datetime으로 파싱
train.head()
test=pd.read_csv("test.csv",parse_dates=['datetime'])
test.head()
train.info()
test.info()
train.shape #(10886,12)
test.shape #(6493,9)
중복값 결측값 확인
- 훈련 데이터, 테스트 데이터 중복 값 있는지 확인
- 훈련 데이터, 테스트 데이터 null 값 (결측값) 확인
train[train.duplicated()] # 중복값 없음
test[test.duplicated()] # 중복값 없음
train.isnull().sum() #결측값 없음
test.isnull().sum() #결측값 없음
- 결측값 확인 다른방법 : missingno 라이브러리
#Anaconda prompt >> pip install missingno 설치
import missingno as msno
msno.matrix(train,figsize=(12,5))
#흰색선이 보이지 않으므로 결측값 없음
년,월,일,시 에 따른 평균 대여량 구하기
- datetime에서 년,월,일,시,분,초를 추출하여 각각의 column으로 구성
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 # 초 추출
train['dayofweek']=train['datetime'].dt.dayofweek #요일 추출 , 월요일:0, 일요일:6
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 #요일 추출 , 월요일:0, 일요일:6
탐색적 데이터 분석 (EDA)
- 기술통계 분석
train.describe() #기술통계
- 근무일 유무/요일/시즌/날씨에 따른 시간대별 자전거 대여량 구하기
import matplotlib.pyplot as plt
fig,((ax1,ax2,ax3),(ax4,ax5,ax6)) =plt.subplots(nrows=2, ncols=3)
fig.set_size_inches(20,8)
import seaborn as sns
sns.barplot(data=train, x="year", y="count", ax=ax1)
sns.barplot(data=train, x="month", y="count", ax=ax2)
sns.barplot(data=train, x="day", y="count", ax=ax3)
sns.barplot(data=train, x="hour", y="count", ax=ax4)
sns.barplot(data=train, x="minute", y="count", ax=ax5)
sns.barplot(data=train, x="second", y="count", ax=ax6)
2011년도 보다 2012년도가 자전거 수요량이 상승했다.
겨울보다 여름에 자전거 수요량이 상승한다.
연초보다 연말에 자전거 수요량이 소폭 상승한다.
새벽시간보다 출퇴근 시간에 자전거 수요량이 상승한다.
fig, axes = plt.subplots(nrows=2, ncols=2)
fig.set_size_inches(20,8)
sns.boxplot(data=train, x="season", y="count", ax=axes[0][0])
sns.boxplot(data=train, x="workingday", y="count", ax=axes[0][1])
sns.boxplot(data=train, orient="v", y="count", ax=axes[1][0])
#상자 그림 작성 (세로방향) orient“v” | “h”, optional
sns.boxplot(data=train, x="hour", y="count", ax=axes[1][1])
겨울 보다 여름 가을에 수요량이 더 많은 것을 확인할 수 있다.
겨울에는 극단치 데이터가 많은 편이다.
근무시간이 아닐 때 보다 근무시간에 극단치 데이터가 많은 편이다.
출퇴근 시간에 자전거 수요량이 높음을 확인할 수 있다.
train['dayofweek'].unique()
train['dayofweek'].value_counts()
fig, (ax1, ax2, ax3, ax4, ax5)=plt.subplots(nrows=5)
fig.set_size_inches(18,25)
sns.pointplot(data=train, x="hour", y="count", ax=ax1)
# x, y, hue names of variables in data or vector data, optional
sns.pointplot(data=train, x="hour", y="count",hue='workingday' ,ax=ax2)
sns.pointplot(data=train, x="hour", y="count",hue='dayofweek' ,ax=ax3)
sns.pointplot(data=train, x="hour", y="count",hue='weather' ,ax=ax4)
sns.pointplot(data=train, x="hour", y="count",hue='season' ,ax=ax5)
fig,(ax1,ax2,ax3)=plt.subplots(ncols=3)
fig.set_size_inches(12,5)
sns.regplot(x="temp",y="count",data=train, ax=ax1)
sns.regplot(x="windspeed",y="count",data=train, ax=ax2)
sns.regplot(x="humidity",y="count",data=train, ax=ax3)
자전거 수요량과 기후는 양의 상관관계
풍속은 적은 양의 상관관계
습도와는 음의 상관관계를 보인다.
-
연도별 월별 자전거 대여량 구하기
#2011년과 2012년 따로 구분하여 재출력 하고자함
def ym(mydt):
return"{0}-{1}".format(mydt.year,mydt.month)
train['year_month']=train['datetime'].apply(ym)
train[['datetime','year_month']]
fig,axes=plt.subplots(nrows=1, ncols=1)
fig.set_size_inches(18,4)
sns.barplot(data=train, x="year_month", y="count",ax=axes)
# 2012년에 자전거 대여량증가, 겨울보다 여름에 증가
아웃라이어 제거
정상범위 데이터 : count열값-count열값평균 < 3*(count.std)
#이상치(outliers) 제거 z<3
import numpy as np
trainWithoutOutliers=train[np.abs(train["count"]-train["count"].mean()) <= (train['count'].std()*3)]
print(train.shape)
print(trainWithoutOutliers.shape) # Outliers 제거 확인
데이터 보정
#풍속이 0인 것과 0이 아닌 것을 구분하여 저장
trainWind0=train.loc[train['windspeed']==0]
trainWindNot0=train.loc[train['windspeed']!=0]
print(trainWind0.shape)
print(trainWindNot0.shape)
-풍속 등 각 필드에 0으로 저장되어 있는 값을 근사값으로 보정
# 머신러닝 랜덤포레스트로 풍속 예측
from sklearn.ensemble import RandomForestClassifier
#data의 windspeed값이 0인 데이터를 랜덤 포레스트를 이용하여 예측한 값으로 대체
def predict_windspeed(data): #(출력확인용)
#풍속 예측에 사용되는 변수
wCol=['season','weather','humidity','temp','atemp']
#풍속을 0인 것과 0이 아닌 것으로 구분
dataWind0=data.loc[data['windspeed']==0]
dataWindNot0=data.loc[data['windspeed']!=0]
#랜덤포레스트 분류기 생성
rfModel=RandomForestClassifier()
dataWindNot0['windspeed']=dataWindNot0['windspeed'].astype("str")
# wCol > 풍속학습 > 모델완성
rfModel.fit(dataWindNot0[wCol], dataWindNot0['windspeed']) #(학습대상, 학습자료)
#학습한 모델로 풍속 0에 대한 데이터 예측
preValue=rfModel.predict(X=dataWind0[wCol])
print(preValue)
#풍속이 0인 것과 0이 아닌 것으로 분류
predictWind0=dataWind0
predictWindNot0=dataWindNot0
#예측값을 풍속이 0인 데이터에 대입
predictWind0['windspeed']=preValue
#풍속이 0이 아닌 데이터와 풍속이 0인 데이터 병합하여 data에 대입
data=predictWindNot0.append(predictWind0)
return data
print(predict_windspeed(train))
데이터를 보정할 출력값들을 확인해보았으니, 실제로 0인 풍속값들을 예측치들로 대체해보자.
함수 코드는 위에서 소개한 predict_windspeed와 동일하지만 맨 아래의 행이름 reset 부분만 재구성하여 추가해주었다.
def predict_windspeed2(data): #(실제데이터 보정용)
#풍속 예측에 사용되는 변수
wCol=['season','weather','humidity','temp','atemp']
#풍속을 0인 것과 0이 아닌 것으로 구분
dataWind0=data.loc[data['windspeed']==0]
dataWindNot0=data.loc[data['windspeed']!=0]
#랜덤포레스트 분류기 생성
rfModel=RandomForestClassifier()
dataWindNot0['windspeed']=dataWindNot0['windspeed'].astype("str")
# wCol > 풍속학습 > 모델완성
rfModel.fit(dataWindNot0[wCol], dataWindNot0['windspeed']) #(학습대상, 학습자료)
#학습한 모델로 풍속 0에 대한 데이터 예측
preValue=rfModel.predict(X=dataWind0[wCol])
print(preValue)
#풍속이 0인 것과 0이 아닌 것으로 분류
predictWind0=dataWind0
predictWindNot0=dataWindNot0
#예측값을 풍속이 0인 데이터에 대입
predictWind0['windspeed']=preValue
#풍속이 0이 아닌 데이터와 풍속이 0인 데이터 병합하여 data에 대입
data=predictWindNot0.append(predictWind0)
# 행이름 reset(data만 따로 가져오기 위함)
data.reset_index(inplace=True)
data.drop('index',inplace=True, axis=1)
return data
train=predict_windspeed2(train)
test=predict_windspeed2(test)
대체후 풍속값들의 분포를 시각화하여 확인해보면 train데이터 와 test데이터 모두 0 이 사라진 것을 확인할 수 있다.
fig, ax1 = plt. subplots()
plt.sca(ax1)
plt.xticks(rotation=30)
sns.countplot(data=train, x="windspeed", ax=ax1)
fig, ax1 = plt. subplots()
plt.sca(ax1)
plt.xticks(rotation=30)
sns.countplot(data=test, x="windspeed", ax=ax1)
자전거 대여수 예상 값 출력
먼저 종속변수들을 분석해야한다.
연속형변수는 dtype이 float64(실수형) 인 변수들을 의미한다.
#feature selection
# 연속형(temp, humi, wind, atemp), 범주형 변수는 타입을 category로 변경
train.columns
train.info()
범주형변수는 int64로 나타나있는 경우가 많은데, 이를 category로 변경해주어야 한다.
feature_names=['season','holiday','workingday','weather','temp','atemp',
'humidity','windspeed','year','hour','dayofweek']
#수리형을 범주형으로 바꿔주는 for 문
c_f_n =['season','holiday','workingday','weather',
'year','hour','dayofweek','month']
for v in c_f_n:
#dtype이 int64라서 category로 변경
train[v]=train[v].astype("category")
test[v]=train[v].astype("category")
train.info() # dtype 변경 확인
# 4개의 변수로 되어있음 확인
train['season'].dtypes
test['season'].dtypes
트레이닝 및 테스트 데이터 대입
xtrain=train[feature_names]
xtrain.shape #(10886, 11)
xtest=test[feature_names]
xtest.shape #(6493, 11)
ytrain=train['count'] #레이블(정답)
ytrain.shape
RMSLE 실제치와 예측치의 차이 함수 구현
모델의 성능을 평가하는 함수이다.
def rmsle(predicted_value, actual_value):
predicted_value=np.array(predicted_value)
actual_value=np.array(actual_value)
log_predict=np.log(predicted_value+1)
log_actual=np.log(actual_value+1)
diff=log_predict-log_actual
diff=np.square(diff)
mean_diff=diff.mean()
score=np.sqrt(mean_diff)
return score
모델 성능을 평가해본다.
from sklearn.metrics import make_scorer
rmsle_scorer=make_scorer(rmsle)
rmsle_scorer
# k-fold corss_validation # 핸즈온 머신러닝 p.127
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from pandas import DataFrame
kfold=KFold(n_splits=10, shuffle=True, random_state=42)
from sklearn.ensemble import RandomForestRegressor
model =RandomForestRegressor(n_estimators=100, n_jobs=-1,random_state=42)
%time score=cross_val_score(model, xtrain, ytrain,cv=kfold, scoring=rmsle_scorer )
print(score.mean())
0.33정도 나왔다. 성능이 안좋다 ;_;
model.fit(xtrain,ytrain)
x 트레이닝변수에 정답값인 y변수를 model에 학습시킨다.
prediction=model.predict(xtest)
prediction
test데이터에 예측 값을 출력하였다.
kaggle 제출
submission=pd.read_csv("sampleSubmission.csv")
submission['count']=prediction
submission.to_csv("result.csv",index=False)
상단의 Bike Sharing Demand Competition에 검색하여 들어간다.
Competition이 종료되어 Late Submission을 클릭하고, 준비된 csv 파일을 업로드하면
점수를 바로 확인할 수 있고 순위를 비교해볼 수 있다.
또이이잉 1.8139점이다. 점수가 낮으면 낮을 수록 좋다. 3169.5등 정도 하였다. ㅎ0ㅎ
아마 다른 데이터 전처리 작업도 해야되는 것 같다. &_& 3천등에서 계속 업데이트 해나가야지!
'노트 > Python : 프로그래밍' 카테고리의 다른 글
[파이썬기초] 한글 깨짐 방지코드 (0) | 2020.04.06 |
---|---|
[파이썬] 데이터 변형 | 원핫인코딩 (0) | 2020.04.04 |
[파이썬기초] 주민번호 뒷자리를 별표(*)로 변경하기 (0) | 2020.04.02 |
[파이썬 기초] 데이터 그룹화 (0) | 2020.04.02 |
[파이썬기초] 데이터 정보 확인 및 참조 (0) | 2020.03.31 |