교육 정보
- 교육 명: 경기미래기술학교 AI 교육
- 교육 기간: 2023.05.08 ~ 2023.10.31
- 오늘의 커리큘럼: 빅데이터 기초 활용 역량 강화 (5/10~6/9) - 데이터 분석
- 강사: 조미정 강사님 (빅데이터, 머신러닝, 인공지능)
- 강의 계획:
1. 파이썬 언어 기초 프로그래밍
2. 크롤링 - 데이터 분석을 위한 데이터 수집(파이썬으로 진행)
3. 탐색적 데이터 분석, 분석 실습
- 분석은 파이썬만으로는 할 수 없으므로 분석 라이브러리/시각화 라이브러리를 통해 분석
4. 통계기반 데이터 분석
5. 미니프로젝트
문제정의: 자전거 수요 및 혼잡도 예측이 어려워 적절한 응대가 되지않고 효과적인 운영이 어려움
가설검정: 자전거 수요 예측으로 운영인력 및 인건비 절감 효과, 효과적인 대여소 운영
해결방안: 자전거 대여 수요 분석 - 기준별 자전거 수요량 확인 (EDA)
효과검증: 모델 활용 전/후 운영비용 및 고객만족도 점수
데이터 로드 - 시간대별 공공자전거 데이터, 기상정보데이터를 불러오고 구조 확인
데이터 전처리 - 결측치, 이상치, 컬럼명 변경
데이터 시각화
분석정리
참고 - 데이터 전처리
- 데이터 정제 -이상값삭제, 잡음 완화, 결측(빈값) 추가 등 -> 정확성 높임
- 결측값
: 삭제, 대체 등
작성자의 주관이 가장 많이 들어가는 편
고려 사항이 많음 - 가장 있을법한 데이터로 대체해야 함
혹은 머신러닝으로 예측값을 넣을 수 있음- 이상값
: 동떨어진 값 (입력오류도 있음)
- 이상값 탐지
- 시각적으로 확인
- 통계적 기법으로 확인
- IQR 기준 (박스플롯)
- 이상값 처리
- 이상값 제외
- 가장 가까운 최대/최소값으로 대체
- 데이터 자체가 편향적일 경우 log 변환등을 통해 편향치를 조정하는 경우도 있음
- binning (구간으로 묶기-이상치가 제거되는 효과)
- 잡음
- 통합 - 여러 자료 통합
- 축소 - 필요한 정보만 가져와서 데이터 축소
- 변환 - 필요시 데이터 타입 등을 변환
#글꼴 설치
!sudo apt-get install -y fonts-nanum # 나눔폰트 설치
!sudo fc-cache -fv # 폰트가 캐시에 저장되므로 위에서 설치한 폰트가 적용되도록 폰트 캐시 재 조성
!rm ~/.cache/matplotlib -rf # matplotlib의 폰트 캐시를 삭제
#이거 한 다음에 런타임을 재시작 해야함 (런타임-다시시작)
# 라이브러러 불러오기
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings(action='ignore')
# font 설정
plt.rc('font', family='NanumBarunGothic')
# 한글 깨짐 테스트
plt.scatter([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5])
plt.title('산점도')
plt.xlabel('키')
plt.ylabel('몸무게')
plt.show()
from google.colab import drive
drive.mount('/content/drive')
데이터
from google.colab import drive
drive.mount('/content/drive')
#데이터를 구글 드라이브에서 넣어서 사용함 - 따릉이데이퍼 폴더
from glob import glob
#제시한 조건에 맞는 파일명을 리싀트로 반환
file_names = glob("/content/drive/MyDrive/Colab Notebooks/경기미래기술학교AI과정/빅데이터 분석을 위한 기획과 탐색/04_탐색적데이터분석/따릉이데이터/*.csv")
#따릉이 폴더의 모든 csv 파일 이름을 리스트에 담기
#for 문으로 concat하기
bike_df = pd.DataFrame()
for file_name in file_names:
temp = pd.read_csv(file_name, encoding = 'cp949')
bike_df = pd.concat([bike_df, temp], ignore_index=True)
print (file_name, temp.shape[0])
info를 확인하고 내가 처리할 방향에 맞는지, 수정이 필요한 데이터가 있는지 확인
bike_df.info()
#
#결과
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 29827682 entries, 0 to 29827681
Data columns (total 12 columns):
# Column Dtype
--- ------ -----
0 대여일자 object
1 대여시간 int64
2 대여소번호 int64
3 대여소명 object
4 대여구분코드 object
5 성별 object
6 연령대코드 object
7 이용건수 int64
8 운동량 object
9 탄소량 object
10 이동거리 float64
11 사용시간 int64
dtypes: float64(1), int64(4), object(7)
memory usage: 2.7+ GB
하고싶은것
사용할 데이터 정리
1. key (날씨와 key_on 할데이터): bike_df.대여일자, bike_df.대여시간 = weather_info.일시
2. 이용건수
file_name1 = '/content/drive/MyDrive/Colab Notebooks/경기미래기술학교AI과정/빅데이터 분석을 위한 기획과 탐색/04_탐색적데이터분석/따릉이데이터/기상데이터/weather_2020.csv'
file_name2 = '/content/drive/MyDrive/Colab Notebooks/경기미래기술학교AI과정/빅데이터 분석을 위한 기획과 탐색/04_탐색적데이터분석/따릉이데이터/기상데이터/weather_2021.csv'
weather1 = pd.read_csv(file_name1, encoding = 'cp949')
weather2 = pd.read_csv(file_name2, encoding = 'cp949')
# 데이터 두개 합치기
weather_info = pd.concat([weather1, weather2],ignore_index = True)
# weather_info.sample(10)
weather_info.info()
데이터에서 수정할 부분이 있는지 확인하고 처리함
bike_df.isna().sum()
# 사용할데이터에는 결측치 없음
#
#결과
대여일자 0
대여시간 0
대여소번호 0
대여소명 0
대여구분코드 0
성별 13251460
연령대코드 0
이용건수 0
운동량 0
탄소량 0
이동거리 0
사용시간 0
dtype: int64
사용할데이터에는 결측치 없음
날짜 정보와 merge에 사용할 대여일자, 대여시간으로 필요한 데이터(여기서는 이용건수)를 집계
bike_df2 = bike_df.groupby(['대여일자', '대여시간'])['이용건수'].sum()
bike_df2 = bike_df2.reset_index() #인덱스 재 정렬 , 기존 인덱스를 열로
bike_df2
#
#결과
대여일자 대여시간 이용건수
0 2020-06-01 0 790
1 2020-06-01 1 915
2 2020-06-01 2 688
3 2020-06-01 3 506
4 2020-06-01 4 352
... ... ... ...
10248 2021-12-31 19 1756
10249 2021-12-31 20 1365
10250 2021-12-31 21 1455
10251 2021-12-31 22 876
10252 2021-12-31 23 599
10253 rows × 3 columns
대여일자에서 년도, 월, 일, 요일, 공휴일 변수 생성
bike_df2['대여일자'] = pd.to_datetime(bike_df2['대여일자'])
#2. datatime을 가지고 세부적인 정보를 빼냄
bike_df2['년도'] = bike_df2['대여일자'].dt.year
bike_df2['월'] = bike_df2['대여일자'].dt.month
bike_df2['일'] = bike_df2['대여일자'].dt.day
bike_df2['요일(num)'] = bike_df2['대여일자'].dt.dayofweek
bike_df2['공휴일'] = 0 #0: 평일 1: 공휴일
# 이번에는 토요일 일요일만 공휴일로 변경하고자 함
bike_df2.loc[bike_df2['요일(num)'].isin([5,6]),['공휴일']] = 1
bike_df2.sample(10)
#날짜 및 시간 컬럼 생성
weather_info['날짜'] = weather_info['일시'].str[:10]
weather_info['시간'] = weather_info['일시'].str[11:13].astype(int)
# 컬럼 선택 사용할 컬럼을 순서대로 가져와서 새 데이터 프레임 생성(기존 데이터프레임에서 drop할수도 있음)
weather_df = weather_info[['날짜', '시간', '기온(°C)', '강수량(mm)', '풍속(m/s)', '풍향(16방위)', '습도(%)','일조(hr)',
'일사(MJ/m2)', '적설(cm)','전운량(10분위)', '지면온도(°C)']]
#칼럼명 변경이 필요하다면
weather_df.columns = ['날짜', '시간', '기온', '강수량(mm)', '풍속(m/s)', '풍향(16방위)', '습도(%)','일조',
'일사', '적설(cm)','전운량', '지면온도']
# []안을 순서대로 다 바꿀수 있음
# 혹은 dict 형태로 넣어서 바꾸고싶은것만 바꿀수 있음
weather_df.isnull().sum()
#
#결과
날짜 0
시간 0
기온 1
강수량(mm) 15534
풍속(m/s) 2
풍향(16방위) 2
습도(%) 0
일조 7951
일사 7951
적설(cm) 16957
전운량 20
지면온도 13
dtype: int64
강수량, 적설, 일조, 일사같이 NaN값이 0인 경우는 0으로 fill
전운량, 기온, 지면온도, 풍향, 풍속 는 같은 일자의 이전시간대의 데이터로 대체
📕 이런 기본적인 방법들은 이미 만들어져 있으므로 메소드 찾아서 적용!
# 1. 강수량과 적설이 0인 경우는 0으로 fill
weather_df['강수량(mm)'].fillna(0, inplace = True)
weather_df['적설(cm)'].fillna(0, inplace = True)
weather_df['일조'].fillna(0, inplace = True)
weather_df['일사'].fillna(0, inplace = True)
# 날짜 시간으로 정렬 (되어있더라도 확실하게 하기위해 한번 더 정렬)
weather_df = weather_df.sort_values(['날짜','시간'])
# 전 값으로 채우기 같은 메소드는 이미 만들어져 있음!! 찾아보기
weather_df['기온'].fillna(method='ffill',inplace = True)
weather_df['풍속(m/s)'].fillna(method='ffill',inplace = True)
weather_df['풍향(16방위)'].fillna(method='ffill',inplace = True)
weather_df['전운량'].fillna(method='ffill',inplace = True)
weather_df['지면온도'].fillna(method='ffill',inplace = True)
weather_df['날짜'] = pd.to_datetime(weather_df['날짜'])
#데이터 타입 맞추기
bike_mg = pd.merge (bike_df2,
weather_df,
left_on =['대여일자', '대여시간'],
right_on = ['날짜', '시간']) #default = inner
bike_mg.head()
중복 데이터 drop
bike_mg = bike_mg.drop(['대여일자', '날짜', '시간'], axis = 1)
📕 데이터는 처리가 끝난경우 가벼워지므로 추후 필요할 경우를 대비하여 파일로 저장해놓으면 좋음
#저장
bike_mg.to_csv('bike_mg.csv', index = False, encoding='utf-8-sig')
#불러오기
bike_mg = pd.read_csv('bike_mg.csv')
원본 데이터프레임을 보존하고 복사본을 생성해서 시각화 할 예정
data = bike_mg.copy()
데이터의 기술 통계를 보고 정상적인 값인지 확인 (강수량이 마이너스 값이 없는지 등)
desc_df = data.describe().T
desc_df
sns.histplot(data['이용건수'])
sns.boxplot(data['이용건수'])
이렇게 아웃라이너가 많으면 머신러닝등에서는 일반추세 예측에 방해가 될 수 있어 제거할 수도 있지만 본 포스트에서는 분석 목적이라서 따로 제거하지 않음
sns.lineplot(x=data['년도'].map(str) + data['월'].map(str), y=data['이용건수'])
참고 - 정규분포형태로 만들기 위해서(이상치 처리) log, sqrt(루트) 취할 수 있음
import numpy as np sns.histplot(np.log1p(data['이용건수'])) # 이 그래프에서는 크게 편향이 좋아지지 않으므로 사용 하지 않을것 #편향치 확인 data['이용건수'].skew()
- 위 그래프와 같이 분포 형태가 달라짐
con_cols = []
#원하는 컬럼을 지정할 수 있는 조건이 있다면 컬럼을 고르기위해 조건문을 사용할 수 있음
for col in data.columns:
if (data[col].dtype == 'float64') or (col == '습도(%)'):
con_cols.append(col)
print(con_cols)
#
# 결과
['기온', '강수량(mm)', '풍속(m/s)', '풍향(16방위)', '습도(%)', '일조', '일사', '적설(cm)', '전운량', '지면온도']
fig, axes = plt.subplots(2,5, figsize = (20,8))
ax = axes.flatten()
# axes = (n,n)형태 / ax = m형태
for i, col in enumerate(con_cols):
sns.histplot(data = data, x = col, ax = ax[i])
# 다양한 그래프 subplot으로 나타내기
# 연속성 데이터는 line, 범주형 데이터는 bar그래프로 그려보기
# figure 크기 설정
fig, axes = plt.subplots(2,3, figsize = (20,8))
# 그래프 그리기
sns.barplot(data = data, x = '년도', y= '이용건수', ax = axes[0,0])
sns.barplot(data = data, x = '월', y= '이용건수', ax = axes[0,1])
sns.barplot(data = data, x = '공휴일', y= '이용건수', ax = axes[0,2])
sns.lineplot(data = data, x = '기온', y= '이용건수', ax = axes[1,0])
sns.barplot(data = data, x = '대여시간', y= '이용건수', ax = axes[1,1])
sns.lineplot(data = data, x = '강수량(mm)', y= '이용건수', ax = axes[1,2])
#제목 넣기
axes[0,0].set_title('년도별 이용건수')
axes[0,1].set_title('월별 이용건수')
axes[0,2].set_title('공휴일여부에 따른 이용건수')
axes[1,0].set_title('기온별 이용건수')
axes[1,1].set_title('대여시간별 이용건수')
axes[1,2].set_title('강수량(mm)별 이용건수')
# 간격두기
fig.subplots_adjust(hspace = 0.4)
분석
2021 이용건수가 2020에 비해 증가
9월이용건수가 최대, 12월이 최소
공휴일 이용건수보다 평일 이용건수가 더 많음 (출퇴근?)
기온에 따라서는 15도정도, 30도 정도에서 한번씩 피크가 옴 , 왜 20도에서는 이용건수가 대여시간은 8시, 18시에 많은것을 봐서는 출퇴근때 가장 많이 이용함을 알 수 있음 -> 공휴일과 연관지어서 생각 가능
비는 안올때가 많은데 30, 60 일때는 뭔가 확인하고싶음
공휴일에 이용시간도 궁금
plt.figure(figsize = (15,3))
sns.pointplot(x='대여시간', y='이용건수',data = data, hue = '공휴일')
# 평일과 공휴일에는 완전히 다른 모습을 보임! 오후 4시를 피크로 완만한 그래프
plt.figure(figsize = (15,3))
sns.pointplot(x='대여시간', y='이용건수',data = data, hue = '요일(num)')
# 요일로 보면 토요일에 이용건수가 더 많고 토요일이 좀더 오후에 전반적으로 이용이 높음
sns.boxplot(x='요일(num)', y='이용건수',data = data)
dofw = list('월화수목금토일')
plt.xticks([0,1,2,3,4,5,6],dofw)
plt.show()
#휴일은 상대적으로 변동성이 적음. 평일은 변동성이 큰편->아마 업무시간 변화가 아닐까
fig, axes = plt.subplots(4,3, figsize = (20,20))
ax = axes.flatten()
for i, col in enumerate(con_cols):
#print(i, col)
sns.regplot(data = data, x = col, y='이용건수', ax = ax[i], line_kws={'color': 'red'})
ax[i].set_ylim(0, 30000)
plt.figure(figsize=(10,8))
sns.heatmap(data[con_cols].corr(), annot=True, cmap = 'coolwarm')
- 2021 이용건수가 2020에 비해 증가
- 9월 이용건수가 최대, 12월이 최소
- 공휴일 이용건수보다 평일 이용건수가 더 많음. 대여시간은 8시, 18시에 많은것을 봐서는 출퇴근때 가장 많이 이용함을 알 수 있음
- 출근을 하지 않는 공휴일에는 평일과 완전히 다른 양상을 가져 오후 4시를 피크로 완만한 그래프
- 요일로 보면 토요일에 이용건수가 더 많고 토요일이 좀더 오후에 전반적으로 이용이 높음 #휴일은 상대적으로 변동성이 적음. 평일은 변동성이 큰 편(아마 업무시간 변화로 추정중)
- 기온이 15, 30도씨 일때 이용이 가장 많으나 그 중간치에서는 또 많지 않음 → 선형적인 혹은 하나의 값에 의존하는 상관관계를 가지지 않음
- 우천시에는 사용량이 확실히 적으나 종종 이상치가 존재함
- 강한 상관관계를 보이는 경우는 없음