안녕하세요.
오늘은 BOAZ 머신러닝 스터디에서 진행한 '백화점 고객 성별 예측 컴피티션' 후기를 남겨보려 합니다.
약 2주간 진행되었고, 새로운 feature 생성에 신경을 많이 썼던 대회였습니다.
바로 시작해 볼게요!
순서
- 데이터 설명
- 변수 아이디어 정리
- 코드 설명
- 결론 및 배운점
1. 데이터 설명
- train - 약 70만개의 구매/환불 데이터 + 성별 정보
- test - 약 34만개의 구매/환불 데이터
* 데이터 형태
데이터의 첫 번째 줄을 대략적으로 해석해보면 "고객번호(custid)가 0인 사람이 2000년 6월 25일 12시 12분 무역지점에서 제품코드(goodcd)가 2.12E+12인 에스티로더 화장품을 3개월 할부로 81000원에 샀다" 라고 합니다.
여기서 저희가 예측해야할 것은 이 사람의 성별입니다.
자 그럼 시작해볼까요?
2. 변수 아이디어 정리
정확한 예측을 위해 의미있는 파생변수를 도출해내야 합니다.
예를 들어 주말/주중의 특성 혹은 할인율 특성이 있습니다.
처음에는 다양한 아이디어가 떠올라 무작정 추가해버리고 싶지만, 이렇게 하면 변수를 최대한으로 이용하지 못합니다.
제 경험으로는 한 변수에 대해 파생될 수 있는 특성들을 충분히 고민해보고 그 다음 변수를 고민하는 방법이 가장 효율적이라고 생각합니다.
그리고 그 과정을 표로 정리한다면 더 좋겠죠?
처음 제가 정리한 변수들 입니다.
이렇게 데이터를 받자마자 한 번 정리한 후 변수들을 추가하고, 그 이후에 생각나는 파생 변수들은 바로바로 추가하는 편입니다.
한 번 해보세요!! 정말 도움이 됩니다!!
최종적으로 제가 추가한 파생변수들은 다음과 같습니다.
별표(★)친 컬럼은 특히 성능이 많이 올랐던, 그리고 특성 중요도에서 높은 점수를 받은 변수들입니다.
기존 변수 | 파생 변수 |
구매 날짜 (sales date) | 구매 횟수 |
주말 구매 비율 | |
자주 가는 월 평균 [★] | |
구매 시간 (sales time) | 시간별 구매 횟수 |
퇴근 전/후 구매 횟수 | |
퇴근 전/후 구매 비율 [★] | |
아침/ 낮/ 밤 구매 횟수 | |
지점 (str_nm) | 가장 자주 가는 지점 [★] |
상품 고유 코드 (goodcd) | 남성/ 여성 선호 제품 상위 10개 구성 비율 고객별 평균 [★] |
브랜드명 (brd_nm) | 남성/ 여성 선호 브랜드 상위 10개 점수 맵핑 [★] |
코너명 (corner_nm), 세부사항 (pc_nm), 파트명 (part_nm) | '남성', '여성' 단어 들어간 이력 횟수 -> 3개 컬럼 평균 [★] |
종류 (buyer_nm) | 각 종류별 구매 횟수 [★] |
총 금액(tot_amt) | 환불 받은 횟수 |
환불 받은 횟수의 비율 [★] | |
할인금액 (dis_amt) | 총 할인 금액 |
최대 할인율 | |
평균 할인율 | |
할인 받은 횟수 | |
할인 받은 횟수의 비율 [★] | |
순수익 (net_amt) | 총 구매액 |
최대 구매액 | |
최소 구매액 | |
평균 구매액 | |
할부개월 (inst_mon) | 할부 결제한 횟수 |
할부 결제한 횟수의 비율 | |
평균 할부 개월 |
3. 코드 설명
전체적인 틀은 위와 같이 파생변수를 추가하고,
세 개의 알고리즘(RandomForest, XGBoost, LightGBM) 예측값을 가중치를 이용하여 합산하는 것입니다.
전체 코드는 아래 깃허브를 참고해주세요.
github.com/sanghwi-git/predict-gender/blob/master/2nd_competition.ipynb
4. 결론 및 배운점
score는 roc_auc를 사용했고, 성능은 약 0.7 정도로 스터디에서 1등을 차지할 수 있었습니다.
주최자의 말에 따르면 0.7 정도면 실제 대회에서도 상위권에 속한다고 하는데, 이번 대회를 통해 feature engineering 의 중요성과 변수를 만드는 약간의 노하우를 터득한 것 같습니다.
마지막으로 이번 컴피티션을 통해 새롭게 익힌 문법들에 대해 정리하고 마무리하겠습니다.
# 자주가는 지점 특성 추가
from tqdm.notebook import tqdm
f = data.groupby(['custid','str_nm'])['str_nm'].agg([('str_freq','count')]).reset_index()
lst = []
for i in tqdm(f['custid'].unique()) :
f2 = f.query("custid == @i")
lst.append(f2.sort_values(by='str_freq',ascending=False)['str_nm'].iloc[0])
features['str_freq'] = lst
features = pd.get_dummies(features, 'str_freq')
1) tqdm
for 문을 사용할 때, 범위를 지정해주는 부분을 tqdm으로 씌워주면 진행 표시바가 나타납니다.
원래 enumerate를 이용해서 진행현황을 지켜봤는데, 훨씬 쉬운 방법이 있었네요!
2) query
쿼리는 판다스의 데이터프레임에서 사용할 수 있습니다.
데이터 프레임 중에서 특정 조건을 만족하는 데이터만 불러오라는 메서드인데요,
기존의 대괄호( [ ] )로도 구현이 가능하지만 데이터 양이 많은 경우에는 query가 더 유리하다고 합니다.
* 위의 query 사용에서 @를 사용하는 것은 외부 변수로 i를 사용하기 때문입니다.
# 퇴근전/후 구매 빈도, 비율
def get_off_work(x):
if 9<=x<=17:
return('bf_work')
else:
return('af_work')
data['sales_type'] = data['sales_time'].map(get_off_work)
sales_type = pd.pivot_table(index='custid',columns='sales_type',values='tot_amt',
aggfunc='count', fill_value=0,data = data.query('tot_amt>0')[['custid','sales_type','tot_amt']]).reset_index()
3) pivot_table
구매 데이터와 같이 같은 사람이 여러 구매 이력을 가지고 있는 경우, 하나의 행으로 압축하는 과정이 필요합니다.
이에 유용한 pivot_table이라는 메서드가 있는데, 한번 해석해보겠습니다.
"총 구매액이 0보다 큰 ['custid', 'sales_type', 'tot_amt'] 데이터를 피벗 테이블로 변경하겠습니다.
tot_amt를 count한 값을 만들건데, 인덱스를 custid로 컬럼을 sales_type로 지정하겠습니다.
또한 NaN인 값들은 0으로 채워줍니다."
언뜻 보면 어려워 보이지만 하나하나 풀어보면 쉽죠?!
이걸 모를땐 반복문과 groupby를 활용해서 5시간에 걸쳐 압축했었습니다,,
'Project > BOAZ' 카테고리의 다른 글
[BOAZ] 보아즈 데이터 분석 부문 수료 및 활동 후기 (6) | 2021.08.16 |
---|---|
[BOAZ] ADV 프로젝트 - 특정 시간대의 주가 변동 패턴을 이용한 실시간 주가 예측 (2) (2) | 2021.08.07 |
빅데이터 연합 동아리 BOAZ 17기 모집 (0) | 2021.06.10 |
[BOAZ] ADV 프로젝트 - 특정 시간대의 주가 변동 패턴을 이용한 실시간 주가 예측 (1) (3) | 2021.02.18 |
[BOAZ] 미니 프로젝트 - 데이콘 소설작가분류 경진대회 1등 코드 분석 (0) | 2021.01.01 |