dacon 식수 예측
Mid Project2로 팀원들과 수요예측 프로젝트를 진행했다.
Mar 10, 2023
Learnings
- EDA가 중요하다 → 코로나/법정 공휴일 외 휴가 등 반영
- 시계열 데이터의 특성을 잘 반영해야 예측력이 높아진다
- 메뉴 데이터를 적절하게 변형해야하는데 어렵다
👉🏻 PPT 보기
데이터 둘러보기(구조 탐색)
파일별 용도 파악
train, test, sample_submission
데이터 양(레코드 수, 피처 수, 전체 용량 등)
- train.shape (1205,12)
2016-02-01 ~ 2021-01-26
- test.shape (50,10)
2021-01-27 ~ 21-04-09
- sub.shape (50,3)
2021-01-27 ~ 21-04-09
피처 이해(이름, 의미, 데이터 타입, 결측값 개수, 고윳값 개수, 실제값, 데이터 종류 등)
Summary : 자체적인 결측값은 없음. 근데 특정일자의 행이 아예 없음!
누락값만 확인
2017,18년 40주
19년 25,26주가 null값
훈련 데이터와 테스트 데이터 차이
- target값 이진 분류 문제
- 훈련, 테스트 데이터셋 모두 동일한 feature가 들어있음
타깃값 : 제출(예측)해야 하는 값
- 회귀분석
- 일별 중식계, 석식계의 숫자를 맞춰야하는 문제
데이터 시각화
- '일자', '요일', '년', '월', '일', '주' + 공휴일
- '본사정원수', '본사휴가자수', '본사출장자수', '
본사시간외근무명령서승인건수', '현본사소속재택근무자수', + '본사근무자수'
- '조식메뉴', '중식메뉴', '석식메뉴' + 중식메뉴, 석식메뉴, 중식_카테고리, 석식_카테고리
- '중식계', '석식계'
타깃값의 분포
- 중식계와 석식계는 정규분포와 비슷한 분포를 갖고 있다
- 석식은 0값이 꽤 있다
- 자기계발의 날, 가정의날 등으로 석식메뉴가 없는 날들은 석식계가 0이다
- 석식메뉴가 있으나 석식계가 0으로 집계된 날이 있다 → 본사시간외 근무명령서 승인건수가 0인점을 고려할 때, 특별히 오후 일정이 있었던 날이 아닐지?.? → 다만 본사시간외근무명령서승인건수가 0이라고 해서 석식계가 0인 것은 아니다. 0이나 석식계가 0이 아닌날들도 많다,
- outlier분포를 IQR기준으로 살펴보자
- 중식 아웃라이어 :
- 연말에 전반적으로 중식계가 적었다
- 19년 2/11 max값을 찍었는데 석식계도 그 날 높았던 것으로 보아.. 일이 바빴거나, 외부 변수로 인해 식당이용이 높았던 것으로 추정된다 (해당 날짜 전후로 데이터를 살펴볼까?)
- 석식 아웃라이어 :
- 자기계발의 날, 가정의날등 석식메뉴가 없는 날은 석식 이용이 없다 (주로 수요일)
- 석식이용이 높았던 날은 본사시간외근무명령서승인건수가 압도적으로 많았다 (야근과의 상관성??)
범주형 시각화 (날짜)
월/주/일/요일
- 월 단위로 보면 큰 편차는 없다
- 주별로 봤을 때, 연말 마지막주의 중식, 석식은 최소값을 가지며, 40주차가 Max값인데, 오차막대가 긴 것은 40주차가 17,18년도가 누락값이라 다른 주차 대비 표본이 적어서 그런 것으로 보인다.
- 일간의 편차는 크지 않다
- 요일별로 봤을 때, 전반적으로 월>화>수>목>금 순으로 중식, 석식계가 줄어든다
타깃값에 결측치가 있다
- 아예 행 자체가 없다
수치형 시각화
- 본사근무자수 = 본사정원수 - 본사휴가자수 - 본사출장자수 - 현본사소속재택근무자수
텍스트 시각화
- 중식/석식 국 간의 차이는 없음
- 중식/석식 밥도 비슷함
- 타깃값에 따른 분포도
데이터 관계 시각화
- 식계는 본사시간외근무명령서승인건수, 본사근무자수, 중식&석식계 간에 상관관계가 컸다
- 생각보다 공휴일전/후날의 여부는 중식계와 석식계에 큰 영향이 없었다
피처 파악
- 추가할 피처
현장근무자 수
train['본사근무자수']=train['본사정원수']-train['본사휴가자수']-train['본사출장자수']-train['현본사소속재택근무자수']
일자별 파생변수 (date)
train['일자'] = pd.to_datetime(train['일자']) train['년'] = train['일자'].dt.year train['월'] = train['일자'].dt.month train['일'] = train['일자'].dt.day train['주'] = train['일자'].dt.week
공휴일
- 제거할 피처
- 조식메뉴 : 조식계 숫자가 없어서 조식 자체로 예측할 수 있는 것이 없다
- 고려할 점
- 석식 ↔ 근무시간외 숫자
이상치 파악
- 석식계 값이 0인데 메뉴가 있는 경우가 있음
모델링 전략
- 석식값은 이상치 처리
- 중식, 석식 별도로 학습 진행
- 날짜 데이터에 공휴일 포함하여 진행
베이스라인 모델
피처 엔지니어링
- 일자 데이터 breakdown → 년, 월, 주, 요일
- 법정 공휴일 데이터를 바탕으로 공휴일 전날, 다음날 컬럼표기
- 본사근무자수 피처 추가
평가지표 준비
- MAE
모델 생성 및 훈련
사용한 피처 | 년, 월, 주, 요일, 공휴일, 본사근무자수, 본사시간외 승인건수 사용 |
ML Model | Gradient Boosting Regressor + GridSearch |
점수 | public 83.1333333333
private 126.2142857143 |
성능 개선
피처 엔지니어링
일자 - 1년 주기로 코사인 인코딩 (시계열 데이터의 특성을 반영하기 위해)
start_date = '2016-02-01' end_date = '2021-04-19' date_range = pd.date_range(start_date, end_date, freq='B') # 평일 데이터만 추출 date_encoded = pd.DataFrame({'일자': date_range, '값': np.random.rand(len(date_range))}) day_diff = (date_encoded['일자'] - pd.to_datetime(start_date)).dt.days date_encoded['코사인_일자'] = np.cos(2 * np.pi * day_diff / 365) date_encoded['일자'] = pd.to_datetime(date_encoded['일자']) date_encoded = date_encoded.iloc[:,[0,2]] df_encoded['일자'] = pd.to_datetime(df_encoded['일자']) df_encoded = pd.merge(df_encoded, date_encoded, how='left', on='일자') df_encoded = df_encoded.iloc[:,1:] df_encoded = df_encoded.rename(columns={'코사인_일자':'일자'}) df_encoded.head(3)
메인메뉴 - 재료 중심으로 5그룹 원핫 인코딩
- 메인메뉴에 따른 평균 석식계에 큰 차이가 없다 → 카테고라이징을 다르게 해봐야할듯
휴일 - 공휴일 기준 2일 전후, 1일 전후 개별 인코딩
- 해당 데이터셋은 휴일에 대한 데이터가 아예 빠져있다. 법정공휴일 외에도 회사의 자체 휴무일이 존재할 수 있기 때문에, 이를 고려해보고자 다음과 같은 가설을 세웠다.
- 공휴일 데이터를 포함하여 결측치가 있었던 행은 공휴일이라고 판단하고, 기존 년도와 비교하여 중복된 null값이 있는 행은 공휴일로 한다
- 본사 휴가자수, 본사 출장자수, 본사 시간외 근무명령서승인건수, 본사근무자수
결과
Share article