본문 바로가기

Project/BOAZ

[BOAZ] 백화점 고객 성별 예측 컴피티션

 

안녕하세요.

오늘은 BOAZ 머신러닝 스터디에서 진행한 '백화점 고객 성별 예측 컴피티션' 후기를 남겨보려 합니다.

약 2주간 진행되었고, 새로운 feature 생성에 신경을 많이 썼던 대회였습니다.

바로 시작해 볼게요!

 


순서

  1. 데이터 설명
  2. 변수 아이디어 정리
  3. 코드 설명
  4. 결론 및 배운점

 


 

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

 

sanghwi-git/predict-gender

Contribute to sanghwi-git/predict-gender development by creating an account on GitHub.

github.com

 

 

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시간에 걸쳐 압축했었습니다,,