| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
- 포아송분포
- Roberta
- 토픽 모델링
- 데이터리안
- 코사인 유사도
- BERTopic
- 데벨챌
- 블루 아카이브
- 조축회
- Tableu
- 블루아카이브 토픽모델링
- 피파온라인 API
- 옵티마이저
- NLP
- 다항분포
- 클래스 분류
- 문맥을 반영한 토픽모델링
- KeyBert
- 붕괴 스타레일
- geocoding
- 구글 스토어 리뷰
- 데이터넥스트레벨챌린지
- CTM
- LDA
- 원신
- 자연어 모델
- SBERT
- Optimizer
- 트위치
- 개체명 인식
- Today
- Total
분석하고싶은코코
데이터 분석(2) - 따릉이 수요량 예측 본문
데이콘에 있는 연습 문제를 통해서 데이터 분석을 진행합니다.
(https://dacon.io/competitions/open/235576/data)
쥬피터 노트북에서 데이터 분석한 과정을 과져왔습니다.
-------
따릉이 예측 - 데이터 분석 연습(2)¶
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import re
import warnings
#데이터 로드
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
submission = pd.read_csv('submission.csv')
#데이터 확인
train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1459 entries, 0 to 1458 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 1459 non-null int64 1 hour 1459 non-null int64 2 hour_bef_temperature 1457 non-null float64 3 hour_bef_precipitation 1457 non-null float64 4 hour_bef_windspeed 1450 non-null float64 5 hour_bef_humidity 1457 non-null float64 6 hour_bef_visibility 1457 non-null float64 7 hour_bef_ozone 1383 non-null float64 8 hour_bef_pm10 1369 non-null float64 9 hour_bef_pm2.5 1342 non-null float64 10 count 1459 non-null float64 dtypes: float64(9), int64(2) memory usage: 125.5 KB
train.sort_values('id')
| id | hour | hour_bef_temperature | hour_bef_precipitation | hour_bef_windspeed | hour_bef_humidity | hour_bef_visibility | hour_bef_ozone | hour_bef_pm10 | hour_bef_pm2.5 | count | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 3 | 20 | 16.3 | 1.0 | 1.5 | 89.0 | 576.0 | 0.027 | 76.0 | 33.0 | 49.0 |
| 1 | 6 | 13 | 20.1 | 0.0 | 1.4 | 48.0 | 916.0 | 0.042 | 73.0 | 40.0 | 159.0 |
| 2 | 7 | 6 | 13.9 | 0.0 | 0.7 | 79.0 | 1382.0 | 0.033 | 32.0 | 19.0 | 26.0 |
| 3 | 8 | 23 | 8.1 | 0.0 | 2.7 | 54.0 | 946.0 | 0.040 | 75.0 | 64.0 | 57.0 |
| 4 | 9 | 18 | 29.5 | 0.0 | 4.8 | 7.0 | 2000.0 | 0.057 | 27.0 | 11.0 | 431.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1454 | 2174 | 4 | 16.8 | 0.0 | 1.6 | 53.0 | 2000.0 | 0.031 | 37.0 | 27.0 | 21.0 |
| 1455 | 2175 | 3 | 10.8 | 0.0 | 3.8 | 45.0 | 2000.0 | 0.039 | 34.0 | 19.0 | 20.0 |
| 1456 | 2176 | 5 | 18.3 | 0.0 | 1.9 | 54.0 | 2000.0 | 0.009 | 30.0 | 21.0 | 22.0 |
| 1457 | 2178 | 21 | 20.7 | 0.0 | 3.7 | 37.0 | 1395.0 | 0.082 | 71.0 | 36.0 | 216.0 |
| 1458 | 2179 | 17 | 21.1 | 0.0 | 3.1 | 47.0 | 1973.0 | 0.046 | 38.0 | 17.0 | 170.0 |
1459 rows × 11 columns
train.isnull().sum()
id 0 hour 0 hour_bef_temperature 2 hour_bef_precipitation 2 hour_bef_windspeed 9 hour_bef_humidity 2 hour_bef_visibility 2 hour_bef_ozone 76 hour_bef_pm10 90 hour_bef_pm2.5 117 count 0 dtype: int64
#시간대별 우천시 대여 현황
train['count'].groupby([train['hour'], train['hour_bef_precipitation']]).mean().unstack()
| hour_bef_precipitation | 0.0 | 1.0 |
|---|---|---|
| hour | ||
| 0 | 73.275862 | 17.000000 |
| 1 | 48.844828 | 23.666667 |
| 2 | 32.431034 | 11.666667 |
| 3 | 21.813559 | 8.500000 |
| 4 | 13.716667 | 2.000000 |
| 5 | 13.283333 | 3.000000 |
| 6 | 24.916667 | 3.000000 |
| 7 | 62.783333 | 37.000000 |
| 8 | 137.830508 | 103.000000 |
| 9 | 93.250000 | 111.000000 |
| 10 | 79.616667 | 30.000000 |
| 11 | 88.327869 | NaN |
| 12 | 113.733333 | 2.000000 |
| 13 | 122.000000 | 5.000000 |
| 14 | 143.491228 | 7.750000 |
| 15 | 162.877193 | 11.750000 |
| 16 | 175.649123 | 44.666667 |
| 17 | 199.925926 | 72.000000 |
| 18 | 270.779661 | 15.000000 |
| 19 | 207.762712 | 20.000000 |
| 20 | 171.879310 | 29.333333 |
| 21 | 170.220339 | 86.000000 |
| 22 | 150.966102 | 68.000000 |
| 23 | 105.566667 | 1.000000 |
우천 여부에 따른 수요 변화량에 큰 의미가 있어 두 가지경우에 따른 수요량을 예측하기로 하였습니다.¶
#우천 여부에 따른 대여수량에 큰 차이가 있어 결과에도 반영할 필요가 있어 우천 여부에 따른 예상 대여 수량 예측으로 변경
submission['count_Rain'] = np.NaN
submission['count_nonRain'] = np.NaN
submission.drop('count',axis=1, inplace=True)
submission
| id | count_Rain | count_nonRain | |
|---|---|---|---|
| 0 | 0 | NaN | NaN |
| 1 | 1 | NaN | NaN |
| 2 | 2 | NaN | NaN |
| 3 | 4 | NaN | NaN |
| 4 | 5 | NaN | NaN |
| ... | ... | ... | ... |
| 710 | 2148 | NaN | NaN |
| 711 | 2149 | NaN | NaN |
| 712 | 2165 | NaN | NaN |
| 713 | 2166 | NaN | NaN |
| 714 | 2177 | NaN | NaN |
715 rows × 3 columns
#우천 여부 결측치 처리
train[train['hour_bef_precipitation'].isna()] # 0시, 18시 결측치
train['count'].groupby(train['hour']).mean() # 0시 평균 71.766 / 18시 평균 : 262.163
#결측치의 대여량은 0시 : 39대 / 18시 : 1대로 우천으로 판단하여 1(우천) 부여
train['hour_bef_precipitation'].fillna(1, inplace=True)
#우천 여부에 따른 예측을 위해 분리
train_Rain = train[train['hour_bef_precipitation']==1]
train_nonRain = train[train['hour_bef_precipitation']==0]
train_Rain.info()
train_nonRain.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 48 entries, 0 to 1443 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 48 non-null int64 1 hour 48 non-null int64 2 hour_bef_temperature 46 non-null float64 3 hour_bef_precipitation 48 non-null float64 4 hour_bef_windspeed 46 non-null float64 5 hour_bef_humidity 46 non-null float64 6 hour_bef_visibility 46 non-null float64 7 hour_bef_ozone 43 non-null float64 8 hour_bef_pm10 41 non-null float64 9 hour_bef_pm2.5 40 non-null float64 10 count 48 non-null float64 dtypes: float64(9), int64(2) memory usage: 4.5 KB <class 'pandas.core.frame.DataFrame'> Int64Index: 1411 entries, 1 to 1458 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 1411 non-null int64 1 hour 1411 non-null int64 2 hour_bef_temperature 1411 non-null float64 3 hour_bef_precipitation 1411 non-null float64 4 hour_bef_windspeed 1404 non-null float64 5 hour_bef_humidity 1411 non-null float64 6 hour_bef_visibility 1411 non-null float64 7 hour_bef_ozone 1340 non-null float64 8 hour_bef_pm10 1328 non-null float64 9 hour_bef_pm2.5 1302 non-null float64 10 count 1411 non-null float64 dtypes: float64(9), int64(2) memory usage: 132.3 KB
#우천시 시간대별 대여수 현황
train_Rain['count'].groupby([train_Rain['hour']]).mean().plot()
plt.axvline(8, c = 'red')
plt.axvline(9, c = 'red')
plt.axvline(17, c = 'red')
plt.axvline(21, c = 'red')
#특정 시간대에 높은 대여량을 보여주지만 공통점이 없음
#8, 9, 17, 21시에 높은 대여량을 보여주는데 우천시라 출,퇴근 용으로 대여를 한다고 보기에는 비정상적이라 판단
<matplotlib.lines.Line2D at 0x7fddacf4f370>
#비우천시 시간대별 대여수 현황
train_nonRain['count'].groupby([train_nonRain['hour']]).mean().plot()
plt.axvline(8, c = 'red')
plt.axvline(18, c = 'red')
plt.text(8.5,140, 'Go to Work')
plt.text(18.5,250, 'End Work')
Text(18.5, 250, 'End Work')
#상관계수 확인
train_Rain.corr()
train_nonRain.corr()
| id | hour | hour_bef_temperature | hour_bef_precipitation | hour_bef_windspeed | hour_bef_humidity | hour_bef_visibility | hour_bef_ozone | hour_bef_pm10 | hour_bef_pm2.5 | count | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| id | 1.000000 | -0.008835 | -0.005731 | NaN | -0.002075 | -0.007240 | -0.003851 | 0.052797 | -0.019598 | 0.007852 | -0.011242 |
| hour | -0.008835 | 1.000000 | 0.416285 | NaN | 0.474299 | -0.356118 | 0.184096 | 0.393164 | -0.032076 | -0.057489 | 0.648941 |
| hour_bef_temperature | -0.005731 | 0.416285 | 1.000000 | NaN | 0.394710 | -0.495191 | 0.183872 | 0.546061 | -0.012448 | -0.078857 | 0.618140 |
| hour_bef_precipitation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| hour_bef_windspeed | -0.002075 | 0.474299 | 0.394710 | NaN | 1.000000 | -0.468876 | 0.266186 | 0.521368 | 0.017473 | -0.195151 | 0.485488 |
| hour_bef_humidity | -0.007240 | -0.356118 | -0.495191 | NaN | -0.468876 | 1.000000 | -0.563993 | -0.425419 | -0.091953 | 0.182254 | -0.456960 |
| hour_bef_visibility | -0.003851 | 0.184096 | 0.183872 | NaN | 0.266186 | -0.563993 | 1.000000 | 0.084029 | -0.435255 | -0.672749 | 0.279097 |
| hour_bef_ozone | 0.052797 | 0.393164 | 0.546061 | NaN | 0.521368 | -0.425419 | 0.084029 | 1.000000 | 0.113076 | 0.026256 | 0.481460 |
| hour_bef_pm10 | -0.019598 | -0.032076 | -0.012448 | NaN | 0.017473 | -0.091953 | -0.435255 | 0.113076 | 1.000000 | 0.488428 | -0.124484 |
| hour_bef_pm2.5 | 0.007852 | -0.057489 | -0.078857 | NaN | -0.195151 | 0.182254 | -0.672749 | 0.026256 | 0.488428 | 1.000000 | -0.134993 |
| count | -0.011242 | 0.648941 | 0.618140 | NaN | 0.485488 | -0.456960 | 0.279097 | 0.481460 | -0.124484 | -0.134993 | 1.000000 |
#비우천시 상관관계 히트멥
plt.figure(figsize=(10,10)) # 크기 설정
sns.heatmap(train_nonRain.corr(),annot=True) # annot: 숫자 표시
#우천시 상관관계 히트멥
plt.figure(figsize=(10,10)) # 크기 설정
sns.heatmap(train_nonRain.corr(),annot=True) # annot: 숫자 표시
<AxesSubplot:>
#각 데이터프레임 결측치 확인
#hour_bef_temperature(기온) , hour_bef_windspeed(풍속), hour_bef_humidity(습도)를 요인으로 사용
train_Rain.isnull().sum() # 934, 1035 2개 항목에서 모든 요인 결측치
train_nonRain.isnull().sum() # 풍속만 7개의 결측치
0
#결측치 처리
#시간대별 평균 값 측정
train_Rain.groupby('hour').mean()['hour_bef_temperature']
train_Rain.groupby('hour').mean()['hour_bef_humidity']
train_Rain.groupby('hour').mean()['hour_bef_windspeed']
train_nonRain.groupby('hour').mean()['hour_bef_windspeed']
#결측치를 시간대별 평균 값으로 대체
train_Rain['hour_bef_temperature'].fillna({934:11.400000,1035:18.000000},inplace=True)
train_Rain['hour_bef_humidity'].fillna({934:84.000000,1035:82.000000},inplace=True)
train_Rain['hour_bef_windspeed'].fillna({934:2.800000 , 1035:1.900000 },inplace=True)
train_nonRain['hour_bef_windspeed'].fillna({18:3.289655, 244:1.798246, 260:1.618966, 376:1.950877, 780:3.326316, 1138:2.779661, 1229:1.643860},inplace=True)
/Users/seokholee/opt/anaconda3/lib/python3.9/site-packages/pandas/core/generic.py:6392: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy return self._update_inplace(result)
#TEST 데이터 결측치 처리
test.isnull().sum() # 653번 결측치 확인
test[test['hour_bef_temperature'].isna()] # 653 hour 19시
test[test['hour_bef_windspeed'].isna()] # 653
test[test['hour_bef_humidity'].isna()] # 653
test.groupby('hour').mean()['hour_bef_temperature'] # 26.110345
test.groupby('hour').mean()['hour_bef_windspeed'] # 3.541379
test.groupby('hour').mean()['hour_bef_humidity'] # 47.689655
test['hour_bef_temperature'].fillna({653:26.110345},inplace=True)
test['hour_bef_windspeed'].fillna({653:3.541379},inplace=True)
test['hour_bef_humidity'].fillna({653:47.689655},inplace=True)
#우천시 예측
use_columns = ['hour','hour_bef_temperature', 'hour_bef_windspeed', 'hour_bef_humidity']
X_train = train_Rain[use_columns]
Y_train = train_Rain['count']
X_test = test[use_columns]
from sklearn.ensemble import RandomForestRegressor
model=RandomForestRegressor(n_estimators=100,random_state=0)
#모델에 train 의 x,y를 넣어 학습한다.
model.fit(X_train,Y_train)
#학습한 모델에 test x를 넣어 결과를 뽑는다.
ypred1=model.predict(X_test)
submission['count_Rain']=ypred1
submission
| id | count_Rain | count_nonRain | |
|---|---|---|---|
| 0 | 0 | 9.346667 | NaN |
| 1 | 1 | 43.351667 | NaN |
| 2 | 2 | 82.604000 | NaN |
| 3 | 4 | 17.393667 | NaN |
| 4 | 5 | 94.530000 | NaN |
| ... | ... | ... | ... |
| 710 | 2148 | 23.557000 | NaN |
| 711 | 2149 | 17.855000 | NaN |
| 712 | 2165 | 12.053667 | NaN |
| 713 | 2166 | 18.128667 | NaN |
| 714 | 2177 | 13.506667 | NaN |
715 rows × 3 columns
#비우천시 예측
use_columns = ['hour','hour_bef_temperature', 'hour_bef_windspeed', 'hour_bef_humidity']
X_train = train_nonRain[use_columns]
Y_train = train_nonRain['count']
X_test = test[use_columns]
from sklearn.ensemble import RandomForestRegressor
model=RandomForestRegressor(n_estimators=100,random_state=0)
#모델에 train 의 x,y를 넣어 학습한다.
model.fit(X_train,Y_train)
#학습한 모델에 test x를 넣어 결과를 뽑는다.
ypred1=model.predict(X_test)
submission['count_nonRain']=ypred1
submission
| id | count_Rain | count_nonRain | |
|---|---|---|---|
| 0 | 0 | 9.346667 | 81.62 |
| 1 | 1 | 43.351667 | 239.04 |
| 2 | 2 | 82.604000 | 91.30 |
| 3 | 4 | 17.393667 | 29.59 |
| 4 | 5 | 94.530000 | 102.97 |
| ... | ... | ... | ... |
| 710 | 2148 | 23.557000 | 64.07 |
| 711 | 2149 | 17.855000 | 66.55 |
| 712 | 2165 | 12.053667 | 119.63 |
| 713 | 2166 | 18.128667 | 129.07 |
| 714 | 2177 | 13.506667 | 215.22 |
715 rows × 3 columns
----
학습이 필요한 부분
- 예측 모델에 대한 이해
: 가이드 라인에 따라가면서 추천 모델이 랜덤 포레스트 모델이라 진행하였지만 랜덤 포레스트 모델에 대한 정확한 지식이 없었다. 좀 더 공부해볼 필요가 있을 것 같다. 다음 글은 데이터 분석에 대한 글이 아니라 데이터 분석 모델에 대한 글을 작성해보려한다.
- 로지스틱회귀분석
: 통계학에 대해서 능통한건 아니지만 겉핥기식 지식으로 결과과 0과 1인 경우 로지스틱 회귀분석을 통한 분석이 가능하다고 배웠다. 그래서 이번 분석을 하면서 우천에 대한 결과가 0과 1이라는것을 보자마자 떠올렸는데 구하려고 하는 값은 결국 대여량수다 보니 어떻게 활용해야할지 고민하다가 답이 안나와서 활용하지 못했다. 결국 위에서 분석한것 처럼 그냥 데이터 자체를 나눠서 분석해서 사용량 예측을 진행함ㅠ
- 데이터 분석을 위한 구분과 결합 과정
: 우천 여부에 따른 데이터로 훈련을 시켰지만 우천 여부가 나와있는 테스트 데이터에 각각 전부 적용해버렸다. 사실 이 부분을 결정하면서 고민이 많았다. 어찌됐든 예측하는건데 비가 올경우의 대여수량 예측인거고 비가 안올때 대여수량 예측이니 그대로 해도 되겠지와 훈련시킨 데이터가 너무 다른데 이러면 Underfitting(과소적합) 현상이 나타날 것 같았다.(확인은 안해봤지만 실제로 비우천인데 우천으로 예상한 결과는 무조건 이 현상이 있을 듯...) 그럼 테스트 데이터도 훈련 데이터처럼 구분을 시키고 각각의 훈련데이터로 예측을 하고 합칠까 생각이 들었다. 그 과정이
1. 테스트 데이터 우천별 데이터 분리
2. 우천별 훈련데이터에 맞게 테스트 데이터 데이터 피팅시켜 대여량 예측
3. 예측된 데이터 결합
4. 서브미션 데이터를 우천여부에 따른 정렬 이후 값 대입
3번 과정과 4번 과정이 추가 된다. 이렇게 안하면 예측값들이 본인의 값과 다른 곳에 들어가버리게 된다. 사실 머릿속으로 생각한 과정이라 이게 더 맞는 과정인지는 잘 모르겠다.
이렇게 하지 않은 이유는 예측이라는 것을 하기 때문에 비가 올것이다 라는 결과가 있지만 올것이기니 안올수도 있는거 아닌가? 라는 생각이 들었고 그러면 비가 안올때의 예측수량 올떄의 예측해보고 싶다라는 생각이 들어서 나눠서 해봤다. 잘 못된 모델이라고 생각이 되긴했지만 그래도 연습하는 과정이니 알고도 해보자라는 생각으로 해봤다ㅎ...
그래서 해봄...ㅎㅎ 내 생각에는 밑에가 더 잘 맞는 데이터 예측같다.
(1~2번 과정 스샷은 생략! 코드를 일부분만 가져오는 법을 몰라서 스샷으로 올림ㅠ)

'데이터분석' 카테고리의 다른 글
| 이디야는 스타벅스 근처에 입점한다? (0) | 2022.11.25 |
|---|---|
| 제주도 교통량 예측(1)_EDA (0) | 2022.10.11 |
| 데이터분석(4) - 타이타닉 생존자 구하기 (0) | 2022.06.27 |
| 데이터분석(3) - 랜덤 포레스트 (0) | 2022.06.07 |
| 데이터 분석 체험하기 - 영화 관객수 예측 (0) | 2022.06.03 |