저번 포스팅에선 [수치형 변수 및 범주형 변수와 대여량 간 관계]를 시각화해보았고, 시간과 날씨 데이터에 대해 알아보았다.
이번 포스팅에선 분기(season)와 workingday 및 holiday 변수들이 대여량과 어떤 관계가 있는지를 시각화해보았다.
✔Table of Contents
2) 범주형 변수와 대여량 간 관계(con't)
나머지 변수들인 season, workingday, holiday와 대여량 간 관계를 시각화한다.
2-3) season과 평균 대여량 간 관계
저번 포스팅에서도 말했지만, 캐글 데이터상으로는 season이 '계절' 로 나와 있으나, 사실상 '분기'로 봐야 한다고 했었다. barplot을 그려보면..
# 1,4사분기 포커스
fig, axs = plt.subplots(1,3, figsize=(18,5))
ax1, ax2, ax3 = axs.flatten()
col = ['orange', 'grey', 'grey', 'green']
axs1 = sns.barplot('season', 'casual', data=train, ax = ax1, palette = col)
axs2 = sns.barplot('season', 'registered', data=train, ax = ax2, palette = col)
axs3 = sns.barplot('season', 'count', data=train, ax = ax3, palette = col)
axs1.set_title('Causal')
axs2.set_title('Registered')
axs3.set_title('Count (ALL)')
plt.show()
저번 포스팅에서 month별 대여량을 시각화했을 때 5~10월에 가장 대여량이 많았다.
그래서 예상대로, 2분기와 3분기(회색)이 가장 평균 대여량이 많았다.
그런데 casual과 registered는 서로 다른 양상을 보인다.
오렌지색과 녹색을 보면, registered일 때는 1, 4분기 평균 예약건이 높아진 모습이다. → 회원의 경우, 비회원보다는 미리 예약하고 탈 가능성이 높으니 이미 예약했으니 춥더라도 타겠다는 의지(?) 가 보인다.
여기서는 2개의 주목할 점을 발견할 수 있다.
1. casual과 registered의 이용 패턴이 많이 다르게 나타난다면, 둘을 나누어 예측하면 어떨까? (즉, target을 count뿐만 아니라, casual / registered로 나누어 예측하고 최종 결과는 둘을 더해보는 것이다!)
2. season과 month는 비슷한 변수이다. month를 3개월마다 잘라놓은 것이 season이므로 당연하다. 비슷한 변수가 머신러닝 모델에 중복해서 들어가면 예측 성능이 안 좋아질 수 있으니 둘 중 하나를 빼는 것도 좋아보인다.
2-4) workingday 및 holiday와 평균 대여량 간 관계
쉬는 날과 일하는 날에는 직관적으로 보기에도 대여량이 큰 차이가 있어 보인다. 특히 hour별로 쪼개 보면 더더욱 그렇다.
쉬는 날엔 오후에, 평일엔 아침이나 저녁에 대여량이 높지 않을까?
여기서 주의할 점!
workingday와 holiday는 모두 1, 0 의 이진 변수로 되어 있는데,
workingday는 평일 / holiday는 주말이 아닌 '공휴일' 이다. 즉, 정리하면 이렇다.
- holiday: workingday가
- 1: 0 이면 공휴일
- 0: 1이면 평일
- 0: 0 이면 주말
그래서 3개의 값으로 나눠주기 위해 새로운 열 'day_type'을 만들어서 대여량을 살펴보았다.
# 새로운 컬럼 생성
train['day_type'] = 'weekend'
is_holi = (train['holiday'] == 1)
is_week = (train['workingday'] == 1)
train.loc[(is_holi), 'day_type'] = 'holiday'
train.loc[(is_week), 'day_type'] = 'workingday'
train.head()
이제 groupby를 활용하여, 주말/평일/공휴일의 대여량을 카운트했다.
단 그냥 카운트하면 당연히 평일의 수가 절대적으로 많으니 대여량이 많이 나올 거라서, 날짜 수로 나눠주었다.
work_holi =(train.groupby('day_type')[['casual', 'registered', 'count']].sum()/ train.groupby('day_type')[['casual', 'registered', 'count']].count())
#시각화
plt.plot(work_holi)
plt.legend(work_holi.columns)
plt.show()
전체적으로, registered > casual 이다.
casual의 경우엔 주말과 공휴일의 이용이 많은 편이고, registered의 경우엔 평일이 가장 많은 편이었다.
시간대로 쪼개어 살펴보면 경향이 더 확실할 것 같아, 시간대별로(hour별) 대여건을 나누어 살펴보았다.
# 데이터 나누기
h1 = train.loc[train['day_type'] == 'workingday']
h2 = train.loc[train['day_type'] == 'weekend']
h3 = train.loc[train['day_type'] == 'holiday']
# 평일에 대해 - 시간대별 평균대여량
h1_df = h1.groupby('hour')[['casual', 'registered', 'count']].mean()
plt.figure(figsize = (10,5))
plt.plot(h1_df)
plt.xticks(np.arange(0, 25))
plt.legend(h1_df.columns)
plt.title('Workingday')
plt.show()
먼저 평일이다.
평일은 보통 registered가 많았다. 또한 그래프 형태가 완전히 출퇴근 시간에 몰빵되어 있다.
보통 오전 6시부터 증가하기 시작하여 8시에 피크 & 오후 4시부터 증가하기 시작하여 오후 5-6시에 피크를 찍는 형태이다.
# 주말에 대해 - 시간대별 평균대여량
h2_df = h2.groupby('hour')[['casual', 'registered', 'count']].mean()
plt.figure(figsize = (10,5))
plt.plot(h2_df)
plt.xticks(np.arange(0, 25))
plt.legend(h2_df.columns)
plt.title('Weekend')
plt.show()
다음은 주말을 알아보았다. 주말은 평일에 비해선 casual 대여량이 많은 편이었다.
다들 예상하셨다시피, 낮 시간대 이용이 두드러진다. 오전 7시부터 증가하기 시작하여, 오후 12시~4시 사이가 가장 대여량이 많다.
그리고 casual이든 registered든 그래프의 양상 자체는 비슷했다. (평일 그래프와는 다른 점!)
# 공휴일에 대해 - 시간대별 평균대여량
h3_df = h3.groupby('hour')[['casual', 'registered', 'count']].mean()
plt.figure(figsize = (10,5))
plt.plot(h3_df)
plt.xticks(np.arange(0, 25))
plt.legend(h3_df.columns)
plt.title('Holiday')
plt.show()
마지막으로 공휴일이다. 공휴일 또한 casual이 꽤 있는 편이다.
재밌는 점은 registered와 casual에 따라 피크 시간이 좀 차이가 있었다는 점이다.
그래프를 'count' 대여량만 기준으로 평일/주말/공휴일을 그려보면 더 추이가 돋보인다.
# 평일 / 주말/ 공휴일 그래프를 한번에 그리면 더 돋보인다
h_all = train.pivot_table(index = 'hour', columns = 'day_type', values = 'count', aggfunc = 'mean')
plt.figure(figsize = (10, 5))
plt.plot(h_all)
plt.legend(h_all.columns)
plt.xticks(np.arange(0, 25))
plt.show()
[해석]
- 평일: 출퇴근 시간인 오전 8시 & 오후 5-6시 완전 몰림
- 주말과 공휴일: 낮 시간에 대다수 이용
- 주말이 좀 더 평균이용수 多, 오후 12시-4시 사이 피크
- 공휴일은 몰리는 시간대 범위가 더 넓은 편
이렇게 해서, workingday와 holiday 변수는 특히 hour 별로 봤을 때 그 차이가 아주 뚜렷하게 나타나는 것을 확인하였다.
hour 외에도 다른 변수와의 관계가 없을까 싶어 weather, season별 평균 대여량으로 평일/주말/공휴일을 나누어 살펴보았는데, 자잘한 차이는 있지만 확연하게 다른 점은 찾지 못하였다. (궁금하신 분들은 주피터 노트북을 참고하세용)
이렇게 하여 범주형 변수와 대여량 간 관계까지 시각화해보았다.
3) 3개 변수 간 관계 파악(month, hour와 대여량 간 관계를 중심으로)
마지막으로는 x축이 month, hour이고, y축이 평균 대여량(count)일 때,
다양한 변수들 간의 관계가 어떻게 나타나는지를 파악해보았다. (temp, season, weather, holiday/workingday 등등..)
그 이유는 지금까지는 여러 변수들과 평균 대여량 간의 관계만 살펴보았는데
특히 월별(month), 시간별(hour)로 쪼개어 봤을 때 양상이 다르게 나타나는 경향이 돋보였기 때문이다.
3-1) hour별 평균 대여량 (hue: 범주형 변수)
여기서는 새로운 변수인 '요일' 을 만들었다. 이 변수는 datetime 형태인 경우 dayofweek로 가져올 수 있다.
#datetime으로부터 요일 추출하기 (dayofweek 활용)
train['dayofweek'] = train['datetime2'].dt.dayofweek
train.head()
위와 같이 숫자 형태로 나타나는데, 0은 월요일부터 ~ 6은 일요일을 나타낸다.
train['dayofweek'].value_counts() #요일별 수는 큰 차이 없음
혹시 요일별로 개수가 크게 차이나면 편파적인 데이터가 될 수 있어 살펴보았다. 큰 차이가 없어 안전하게 사용해도 될 듯!
x축을 hour로 하고, y축을 평균 대여량으로 하였다. 그리고 hue를 다양한 변수들로 하여 그래프를 그려보았다.
# hour별 대여량 다양하게 해서 그려보기
fig, axs = plt.subplots(6, 1, figsize = (18,25))
ax1, ax2, ax3, ax4, ax5, ax6 = axs.flatten()
sns.pointplot(x = 'hour', y = 'count', data = train, ax = ax1)
sns.pointplot(x = 'hour', y = 'count', hue = 'workingday', data = train, ax = ax2)
sns.pointplot(x = 'hour', y = 'count', hue = 'holiday', data = train, ax = ax3)
sns.pointplot(x = 'hour', y = 'count', hue = 'dayofweek', data = train, ax = ax4)
sns.pointplot(x = 'hour', y = 'count', hue = 'season', data = train, ax = ax5)
sns.pointplot(x = 'hour', y = 'count', hue = 'weather', data = train, ax = ax6)
plt.show()
[해석]
- 첫번째 그래프: 요일별 데이터 수가 비슷하다 = 그러니 주말,공휴일보다 평일 데이터 수가 많다! 그래서 앞서 살펴본 평일의 hour별 양상과 비슷하게 나타난다.
- workingday별로 나누어 그리면, 1(평일)과 0(주말,공휴일)일 때로 잘 나뉘어 그려진다.
- holiday별로 나누어 그리면 앞의 2번 그래프와 색깔이 반대로 나타난다.
- 요일별로 나누어 그리면 더욱 경향이 두드러진다. 0~4(평일) 과 5,6(주말) 그래프 양상 두드러짐
- 계절별로 봤을 땐 크게 다른 양상은 안 보인다.
- 날씨별로 봤을 때도 크게 다른 양상이 없다. (참고로 빨간 점은 weather = 4일 때이다. 데이터가 딱 1개밖에 없어서 아웃라이어 처리해야 할 듯.)
이번에는 x축을 hour, y축을 평균 casual 대여량으로 하여 그려보았다.
# hour별 casual 대여량
fig, axs = plt.subplots(5, 1, figsize = (18,22))
ax1, ax2, ax3, ax4, ax5 = axs.flatten()
sns.pointplot(x = 'hour', y = 'casual', data = train, ax = ax1)
sns.pointplot(x = 'hour', y = 'casual', hue = 'workingday', data = train, ax = ax2)
sns.pointplot(x = 'hour', y = 'casual', hue = 'dayofweek', data = train, ax = ax3)
sns.pointplot(x = 'hour', y = 'casual', hue = 'season', data = train, ax = ax4)
sns.pointplot(x = 'hour', y = 'casual', hue = 'weather', data = train, ax = ax5)
plt.show()
[해석]
casual로 봤을 땐 count로 봤을 때와 양상이 살짝 다른 모습을 보인다. 주말에 가까운 분포를 보임!
그래서 주로 낮 시간대의 대여량이 많이 나타나고 있다.
3-2) hour별 평균 대여량 + 연속형 변수 이중축
3-1에서 본 게 범주형 변수들과의 관계였다면, 이번엔 연속형 변수들과의 관계를 알아보자.
먼저, 온도이다.
왼쪽 축은 평균 온도, 오른쪽 축은 평균 대여량(count)이다. 그래서 회색 바(bar)는 대여량이며, 선 그래프는 온도(파란색: 온도, 오렌지색: 체감온도)를 나타낸다.
hourtemp_mean = train.groupby(by = 'hour')[['temp', 'atemp']].mean()
# (시간대별) 평균기온과 이용량의 차이
newdf = train.groupby('hour')[['count']].mean().reset_index()
fig, ax1 = plt.subplots(figsize = (10, 5))
ax1.plot(hourtemp_mean, linewidth=5, alpha=0.7)
ax1.tick_params(axis='both', direction='in')
ax1.legend(hourtemp_mean.columns)
ax2 = ax1.twinx()
ax2.bar('hour', 'count', data=newdf, color = 'gray', alpha = 0.5)
ax2.tick_params(axis='y', direction='in')
ax2.set_title('Average Count & Temp.')
ax1.set_zorder(ax2.get_zorder() + 10)
ax1.patch.set_visible(False)
평균 대여량과 평균 온도 간 비슷한 관계가 있어 보인다. 시간대별로 온도가 높아지면 대여량도 늘고, 온도가 낮아지면 대여량도 낮아지는 양상이다.
같은 그래프를 count가 아닌 casual로만 바꿔서 그려보았다. (코드가 길어 밑 부분부터는 사진만 첨부하였다)
casual로 봤을 때는 더욱 온도와 대여량 간 정비례 관계가 두드러진다!
이번엔 습도를 보자.
위가 count, 아래는 casual이다.
시간대별로 습도와 대여량 간 추이를 보면, 반비례 관계가 잘 드러나는 것을 확인할 수 있다.
다음으로는 풍향을 살펴보자.
풍향 또한 시간대별로 대여량과 정비례 관계가 나타나고 있다!
hour 변수는 4가지 연속형 변수들(온도, 습도, 풍향)과 대여량 간 관계를 매우 잘 보여주는 아주 중요한 변수라고 할 수 있다.
3-3) month별 평균 대여량(hue: 범주형 변수)
위에서 했던 것처럼, 이번엔 x축을 hour가 아닌 month로 바꿔서 그려보았다.
[해석]
5월부터 10월까지의 대여량이 많으며, month로는 딱히 평일과 주말을 나누어 볼 필요가 없을 듯하다.
또한 weather별로 나누어 봐도 다른 양상을 보이지 않는다.
이번엔 y 축을 count가 아닌, casual로 바꿔본다면 평일과 주말의 양상이 확연하게 나뉜다.
왜냐하면 앞에서도 살펴봤듯, 주말에 더 casual 대여가 많기 때문!
3-4) month별 평균 대여량 + 연속형 변수 이중축
역시 앞에서 했던 것처럼 4개의 연속형 변수들로 이중축을 만들어 살펴보자. 과연 hour처럼 대여량과 연속형 변수들의 관계를 잘 설명해줄 수 있을까?
먼저 온도!
월별 이용량은 온도와 대여량 간의 높은 상관성을 보여준다.
이번에도 casual 대여량일 때 더욱 정비례 관계를 보여주고 있다.
그러나 습도에 대해선, 월별로 나눠봤을 때 이용량과 전혀 연관성이 없었다.
마지막으로 풍향도 역시 월별로 나눴을 때 어떠한 경향성도 없었다.
Summary
이렇게 EDA 1편, 2편에서는 다양한 그래프들로 현황 파악과 함께 변수들 간의 관계도 함께 살펴보았다.
항상 느끼는 거지만 EDA는 정말 시행착오의 반복인 것 같다. 지금은 완성본을 포스팅하는 거라 체계적으로 보이겠지만, 엄청 다양한 그래프를 그려봤다가 관계가 없어서 지우고,, 다시 그리고를 반복했다. ㅎㅎ..
EDA를 한 끝에 우리가 가져가야 할 변수들을 정리하자면 다음과 같다.
- 수치형 변수
- 온도: temp, atemp (정비례 관계 두드러짐)
- 습도: humidity (반비례 관계)
- 풍향: windspeed → 정비례 관계가 작게 나타나기 때문에, 제거 또는 0의 값 보완 필요
- 범주형 변수
- 시간 변수: year, month,
day, hour, dayofweek- 여기서 day 변수는 관련이 없어 제거
- hour 변수는 다른 변수들과 대여량 간 관계를 설명해주는 강력한 변수
- 날씨: weather → 값이 4인 것은 outlier(이상치) 이므로 제거
- 평일 및 공휴일: workingday, holiday
- 분기: season → month 변수와 역할이 겹치기 때문에, 좀 더 단순한 season을 남기고 month는 제거
- 시간 변수: year, month,
- 타겟(target)
- count: casual과 registered를 더한 값
- 그런데 casual과 registered의 양상이 좀 다르게 나타나기 때문에, count를 예측해보는 방법과 casual/registered를 따로 예측해보는 방법 2가지를 사용해볼 예정!
이렇게 정리한 것은 다음 포스팅인 머신러닝 편에서 적용해볼 예정이다. 다음 포스팅에서 이어집니다 😀
'Data Science > Kaggle' 카테고리의 다른 글
[kaggle] Bike Sharing Demand: ML 성능 개선 1편 (Ridge, Random Forest, LGBM) (0) | 2022.07.01 |
---|---|
[kaggle] Bike Sharing Demand: Baseline Model 2편 (pipeline, k-fold, scaling) (0) | 2022.06.30 |
[kaggle] Bike Sharing Demand: Baseline Model 1편 (데이터 전처리) (2) | 2022.06.30 |
[kaggle] Bike Sharing Demand: EDA 1편 (6) | 2022.06.24 |
Kaggle 프로젝트를 시작하며 (feat. 깃허브) (0) | 2022.06.24 |