티스토리 뷰
*머신러닝 대회 기준
1. pandas로 데이터 둘러보기
import numpy as np
import pandas as pd # 판다스 임포트
# 데이터 경로
data_path = '/kaggle/input/bike-sharing-demand/'
train = pd.read_csv(data_path + 'train.csv') # 훈련 데이터
test = pd.read_csv(data_path + 'test.csv') # 테스트 데이터
submission = pd.read_csv(data_path + 'sampleSubmission.csv') # 제출 샘플 데이터
train.shape, test.shape
train.head()
test.head()
submission.head()
train.info()
test.info()
데이터가 어떤 형태이고, 정형인지 비정형인지, 결측값이 있는지 없는지 관측
2. 피처 분석(데이터 시각화)를 위해 피처 엔지니어링
판다스에서 object 타입은 문자열 타입.
split()을 쓰면 쉽게 나눌 수 있다.
print(train['datetime'][100]) # datetime 100번째 원소
print(train['datetime'][100].split()) # 공백 기준으로 문자열 나누기
print(train['datetime'][100].split()[0]) # 날짜
print(train['datetime'][100].split()[1]) # 시간
2011-01-05 09:00:00
['2011-01-05', '09:00:00']
2011-01-05
09:00:00
날짜를 연도, 월, 일로 나누기도 가능
print(train['datetime'][100].split()[0]) # 날짜
print(train['datetime'][100].split()[0].split("-")) # "-" 기준으로 문자열 나누기
print(train['datetime'][100].split()[0].split("-")[0]) # 연도
print(train['datetime'][100].split()[0].split("-")[1]) # 월
print(train['datetime'][100].split()[0].split("-")[2]) # 일
2011-01-05
['2011', '01', '05']
2011
01
05
apply() 함수로 DataFrame의 데이터를 일괄 가공
train['date'] = train['datetime'].apply(lambda x: x.split()[0]) # 날짜 피처 생성
# 연도, 월, 일, 시, 분, 초 피처를 차례로 생성
train['year'] = train['datetime'].apply(lambda x: x.split()[0].split('-')[0])
train['month'] = train['datetime'].apply(lambda x: x.split()[0].split('-')[1])
train['day'] = train['datetime'].apply(lambda x: x.split()[0].split('-')[2])
train['hour'] = train['datetime'].apply(lambda x: x.split()[1].split(':')[0])
train['minute'] = train['datetime'].apply(lambda x: x.split()[1].split(':')[1])
train['second'] = train['datetime'].apply(lambda x: x.split()[1].split(':')[2])
apply(lambda x: x.split()[0]
람다 함수를 적용하여 x.split()을 하면 datefame 전체를 공백으로 나눈 후 0번째 요소를 취한다.
그 후에 .split('-')[0] 적용
즉, datetime 함수에서 date는 train['date'] = train['datetime'].apply(lambda x: x.split()[0]) 이고,
그 함수를 -으로 나눈 것의 0번째 인덱스를 구한다고 생각하면 된다.
train['year']을 하면 year 피처로 추가된다.
요일 피처
from datetime import datetime # datetime 라이브러리 임포트
import calendar
print(train['date'][100]) # 날짜
print(datetime.strptime(train['date'][100], '%Y-%m-%d')) # datetime 타입으로 변경
# 정수로 요일 반환
print(datetime.strptime(train['date'][100], '%Y-%m-%d').weekday())
# 문자열로 요일 반환
print(calendar.day_name[datetime.strptime(train['date'][100], '%Y-%m-%d').
weekday()])
2011-01-05
2011-01-05 00:00:00
2
Wednesday
.weekday() 함수 추가 후, calender.day_name 메소드로 문자열로 요일을 반환한다.
그래프로 나타냈을 때 쉽게 알아볼 수 있다는 장점이 있다.
train['weekday'] = train['date'].apply(
lambda dateString:
calendar.day_name[datetime.strptime(dateString,"%Y-%m-%d").weekday()])
위의 함수를 weekday 피처로 추가
다음은 Season과 weather 피처 차례
map 함수로 문자열로 바꾼다.
train['season'] = train['season'].map({ 1: 'Spring', 2: 'Summer', 3: 'Fall', 4: 'Winter' })
train['weather'] = train['weather'].map({1: 'Clear', 2: 'Mist, Few clouds',
3: 'Light Snow, Rain, Thunderstorm',
4: 'Heavy Rain, Thunderstorm, Snow, Fog'})
map 함수 안에 set으로 넣어서 지정
여기까지 하고 나면 이렇게 변한다.
train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10886 entries, 0 to 10885
Data columns (total 20 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 datetime 10886 non-null object
1 season 10886 non-null object
2 holiday 10886 non-null int64
3 workingday 10886 non-null int64
4 weather 10886 non-null object
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
12 date 10886 non-null object
13 year 10886 non-null object
14 month 10886 non-null object
15 day 10886 non-null object
16 hour 10886 non-null object
17 minute 10886 non-null object
18 second 10886 non-null object
19 weekday 10886 non-null object
13개이던 피처가 19개로 늘어난 것을 볼 수 있다.
season과 weather 피처는 숫자에서 문자로 바뀐다.
세 달 씩 월을 묶으면 계절이 된다.
3. 데이터 시각화
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
씨본하고 맷플롯라이브러리.
코드 %matplotlib inline을 추가하면 matplotlib이 그린 그래프를 주피터 노트북에서 바로 출력해준다.
분포도
가장 먼저 살펴볼 것은 타깃값.
분포도는 수치형 데이터의 집계 값을 나타내는 그래프
타깃값인 count의 분포도를 보면 훈련 시 타깃값을 그대로 사용할지 변환해 사용할 지 파악할 수 있다.
mpl.rc('font', size=15) # 폰트 크기를 15로 설정
sns.displot(train['count']); # 분포도 출력
대부분은 정규분포를 따르지 않는다.
따라서 정규분포에 가깝게 만들기 위해 사용하는 방법은 로그변환이다.
로그변환은 데이터가 왼쪽으로 편향되어 있을 때 사용한다.
sns.displot(np.log(train['count']));
타깃값 분포가 정규분포에 가까울수록 회귀 모델 성능이 좋아진다.
다시 말해, 피처를 바로 활용해 count를 예측하는 것보다 log(count)를 예측하는 편이 더 정확하다.
다만, 마지막에 지수변환을 하여 실제 타깃값인 count로 복원해야 한다.
log(y)를 지수변환하면 y가 된다.
y=e^(log(y))
막대 그래프
연도, 월, 일, 시 ,분 , 초별로 총 여섯가지의 평균 대여 수량
이 피처들은 범주형 데이터다. 각 범주형 데이터에 따라 평균 대여 수량이 어떻게 다른지 파악한다.
seaborn의 barplot() 함수로 그린다.
# 스텝 1 : m행 n열 Figure 준비
mpl.rc('font', size=14) # 폰트 크기 설정
mpl.rc('axes', titlesize=15) # 각 축의 제목 크기 설정
figure, axes = plt.subplots(nrows=3, ncols=2) # 3행 2열 Figure 생성
plt.tight_layout() # 그래프 사이에 여백 확보
figure.set_size_inches(10, 9) # 전체 Figure 크기를 10x9인치로 설정
# 스텝 2 : 각 축에 서브플롯 할당
# 각 축에 연도, 월, 일, 시간, 분, 초별 평균 대여 수량 막대 그래프 할당
sns.barplot(x='year', y='count', data=train, ax=axes[0, 0])
sns.barplot(x='month', y='count', data=train, ax=axes[0, 1])
sns.barplot(x='day', y='count', data=train, ax=axes[1, 0])
sns.barplot(x='hour', y='count', data=train, ax=axes[1, 1])
sns.barplot(x='minute', y='count', data=train, ax=axes[2, 0])
sns.barplot(x='second', y='count', data=train, ax=axes[2, 1])
# 스텝 3 : 세부 설정
# 3-1 : 서브플롯에 제목 달기
axes[0, 0].set(title='Rental amounts by year')
axes[0, 1].set(title='Rental amounts by month')
axes[1, 0].set(title='Rental amounts by day')
axes[1, 1].set(title='Rental amounts by hour')
axes[2, 0].set(title='Rental amounts by minute')
axes[2, 1].set(title='Rental amounts by second')
# 3-2 : 1행에 위치한 서브플롯들의 x축 라벨 90도 회전
axes[1, 0].tick_params(axis='x', labelrotation=90)
axes[1, 1].tick_params(axis='x', labelrotation=90)
barplot 함수에서 ax는 그래프를 여러 개 그릴 때 어디 부분에 그리는지를 지정하는 함수이다.
이렇게 barplot을 그리면 각각의 특징을 볼 수 있다.
1. 2011년보다 2012년에 대여가 많다. 년별
2. 월별 평균 대여 수량의 추세 - 평균 대여 수량은 6월에 가장 많고 1월에 가장 적다.
3. 일별 평균 대여 수량 - 일별은 차이가 없다. 1일부터 19일까지의 데이터만 있으므로, 일자는 피처로 사용하지 못한다.
피처로 사용하려면 훈련 데이터와 테스트 데이터에 공통된 값이 있어야 한다.
4. 시간별 평균 대여 수량 - 쌍봉형. 새벽 4시에 대여 수량이 가장 적다. 새벽 4시에 자전거를 타는 사람은 없다.
아침 8시와 저녁 5~6시에 대여가 가장 많다.
사람들이 등하교 혹은 출퇴근 길에 자전거를 많이 이용한다.
5, 6. 분별이나 초별 평균 대여 수량은 아무 정보도 없다. 분과 초는 모두 0으로 기록되어 있다.
따라서 분과 초 피처는 사용 X
위의 그래프 그리는 방법을 스텝 1~스텝 3로 나눠본다.
스텝 1 : m행 n열 Figure 준비하기
첫 번째로 총 6개의 그래프(서브플롯)를 품는 3행 2열짜리 Figure를 준비
mpl.rc('font', size=14) # 폰트 크기 설정
mpl.rc('axes', titlesize=15) # 각 축의 제목 크기 설정
figure, axes = plt.subplots(nrows=3, ncols=2) # 3행 2열 Figure 생성 ①
plt.tight_layout() # 그래프 사이에 여백 확보 ②
figure.set_size_inches(10, 9) # 전체 Figure 크기를 10x9인치로 설정 ③
axes 객체 (axes.shape = (3,2)
axes
array([[<AxesSubplot:>, <AxesSubplot:>],
[<AxesSubplot:>, <AxesSubplot:>],
[<AxesSubplot:>, <AxesSubplot:>]], dtype=object)
간격 넓히기
figure, axes = plt.subplots(nrows=3, ncols=2)
plt.tight_layout()
스텝 2: 각 축에 서브플롯 할당
sns.barplot(x='year', y='count', data=train, ax=axes[0, 0])
sns.barplot(x='month', y='count', data=train, ax=axes[0, 1])
sns.barplot(x='day', y='count', data=train, ax=axes[1, 0])
sns.barplot(x='hour', y='count', data=train, ax=axes[1, 1])
sns.barplot(x='minute', y='count', data=train, ax=axes[2, 0])
sns.barplot(x='second', y='count', data=train, ax=axes[2, 1])
스텝 3 : (선택) 세부 설정
axes[0, 0].set(title='Rental amounts by year') #각 서브플롯에 제목 추가
axes[0, 1].set(title='Rental amounts by month')
axes[1, 0].set(title='Rental amounts by day')
axes[1, 1].set(title='Rental amounts by hour')
axes[2, 0].set(title='Rental amounts by minute')
axes[2, 1].set(title='Rental amounts by second')
axes[1, 0].tick_params(axis='x', labelrotation=90)
axes[1, 1].tick_params(axis='x', labelrotation=90)
#1행의 서브플롯 x축 라벨 90도 회전
박스플롯
박스플롯은 범주형 데이터에 따른 수치형 데이터 정보를 나타내는 그래프(주식에서의 캔들바)
막대 그래프보다 더 많은 정보를 제공
계절,날씨,공휴일,근무일(범주형 데이터) 별 대여 수량(수치형 데이터)
# 스텝 1 : m행 n열 Figure 준비
figure, axes = plt.subplots(nrows=2, ncols=2) # 2행 2열
plt.tight_layout()
figure.set_size_inches(10, 10)
# 스텝 2 : 서브플롯 할당
# 계절, 날씨, 공휴일, 근무일별 대여 수량 박스플롯 ①
sns.boxplot(x='season', y='count', data=train, ax=axes[0, 0])
sns.boxplot(x='weather', y='count', data=train, ax=axes[0, 1])
sns.boxplot(x='holiday', y='count', data=train, ax=axes[1, 0])
sns.boxplot(x='workingday', y='count', data=train, ax=axes[1, 1])
# 스텝 3 : 세부 설정
# 3-1 : 서브플롯에 제목 달기
axes[0, 0].set(title='Box Plot On Count Across Season')
axes[0, 1].set(title='Box Plot On Count Across Weather')
axes[1, 0].set(title='Box Plot On Count Across Holiday')
axes[1, 1].set(title='Box Plot On Count Across Working Day')
# 3-2 : x축 라벨 겹침 해결
axes[0, 1].tick_params(axis='x', labelrotation=10) # 10도 회전
계절 - 자전거 대여수량은 봄에 가장 적고, 가을에 가장 많다.
날씨 - 맑을 때 가장 많고, 날씨가 궂어질수록 적어진다.
공휴일 - 공휴일일 때와 아닐 때 수량의 중앙값은 비슷. 그러나 공휴일이 아닐 때는 이상치가 많다.
평일 - 마찬가지로 근무일일 때 이상치가 많다.
포인트플롯
근무일, 공휴일, 요일, 계절, 날씨에 따른 시간대별 평균 대여 수량
포인트플롯 - 캔들바에서 포인트들을 선으로 연결한 것.
범주형 데이터에 따른 수형 데이터의 평균과 신뢰구간을 점과 선으로 표현 (캔들바의 확장형)
여러 그래프를 서로 비교하기에 적합
# 스텝 1 : m행 n열 Figure 준비
mpl.rc('font', size=11)
figure, axes = plt.subplots(nrows=5) # 5행 1열
figure.set_size_inches(12, 18)
# 스텝 2 : 서브플롯 할당
# 근무일, 공휴일, 요일, 계절, 날씨에 따른 시간대별 평균 대여 수량 포인트플롯
sns.pointplot(x='hour', y='count', data=train, hue='workingday', ax=axes[0])
sns.pointplot(x='hour', y='count', data=train, hue='holiday', ax=axes[1])
sns.pointplot(x='hour', y='count', data=train, hue='weekday', ax=axes[2])
sns.pointplot(x='hour', y='count', data=train, hue='season', ax=axes[3])
sns.pointplot(x='hour', y='count', data=train, hue='weather', ax=axes[4]);
hue 파라미터에 비교하고 싶은 피처를 전달
대여 수량은 가을에 가장 많고, 봄에 가장 적다.
근무일에는 출퇴근 시간에 대여 수량이 많고, 쉬는 날에는 오후 12~2시에 가장 많다.
공휴일 여부, 요일에 따른 포인트 플롯도 근무일 여부에 따른 포인트플롯(1번)과 비슷한 양상
폭우, 폭설이 내릴 때 18시(저녁 6시)에 대여 건수가 있다. 정말 급한 일이 있었나 보다 ㅋㅋㅋ
이런 이상치는 제거를 고려해보는 것도 괜찮은 방법이다.
출처 :머신러닝 딥러닝 문제해결 전략