천지 경쟁 O2O 쿠폰 예측 + XGboost 사용 방법, AUC 0.5379

70117 단어
알고리즘 경쟁에 처음 진출한 컴퓨터 요리닭으로서 자신의 이번 역정을 공유하여 경기에서 자신의 발전을 기록하고자 한다.
2월부터 천지 경기 코드를 봤는데 지금까지 거의 두 달 동안 기본적인 입문이라고 할 수 있습니다. 어떤 경기 절차인지 알고 혼자서 이리저리 굴러다녔습니다. 중간 과정은 매우 힘들었습니다. 여러 번 버그카드 때문에 며칠 동안 해결해 준 사람이 없어서 효율이 낮았습니다.
자, 본론으로 돌아가서 최근에 한 O2O 경연을 나눠보도록 하겠습니다.
천지 신인전에 등록한 후에 기술권에 가서 공부를 했습니다. 100줄 코드가 천지 O2O 쿠폰에 입문하여 신인전의 베이스라인을 사용하는 것을 보고 디버깅 코드를 가져왔습니다. 결과는 순조롭게 진행되었고 코드도 비교적 간단했습니다.특징 공정 처리를 거의 하지 않고 SGDClassifier 알고리즘을 사용했다. 마지막으로 AUC는 0.5287이고 랭킹은 412/13500으로 1위의 0.81과 차이가 매우 크다.
그래서 이를 바탕으로 코드 수정을 다시 하고 일부 특징을 추출하여 XGboost 알고리즘 모델을 이용하여 결과를 제출했다. 마지막으로 AUC는 0.5379로 60여 명, 350/13500에 랭크되었다.다음은 이번 코드를 공유하고 상세하게 해독하겠습니다.
import os, sys, pickle
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

import seaborn as sns

from datetime import date

from sklearn.model_selection import KFold, train_test_split, StratifiedKFold, cross_val_score, GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.linear_model import SGDClassifier, LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import log_loss, roc_auc_score, auc, roc_curve
from sklearn.preprocessing import MinMaxScaler
import xgboost as xgb
#import xgboost as xgb
#import lightgbm as lgb

display for this notebook


%matplotlib inline %config InlineBackend.figure_format = ‘retina’
1: 데이터 세트 가져오기 대략적인 데이터 세트 보기
dfoff = pd.read_csv("..\input\ccf_offline_stage1_train.csv",keep_default_na = False)
dftest = pd.read_csv("..\input\ccf_offline_stage1_test_revised.csv",keep_default_na = False)

dfon = pd.read_csv("..\input\ccf_online_stage1_train.csv",keep_default_na = False)
dfoff.head(5)   #       5 
dftest.head(5)  #       5 

이를 통해 알 수 있듯이 훈련 집합은 테스트 집합보다 한 열의Date가 많다. 우리가 해야 할 일은 훈련 훈련 집합의 데이터를 통해 마지막으로 테스트 집합을 통해 사용자가 소비할지 안 할지 예측하는 것이다.
본 대회 문제는 사용자가 2016년 1월 1일부터 2016년 6월 30일 사이에 실제 온·오프라인 소비 행위를 제공하고, 사용자가 2016년 7월 쿠폰을 수령한 후 15일 이내의 사용 상황을 예측한다.
데이터 세트 세척 방법 1: 부족한 값이 있는지 확인
dfoff.isnull().sum().sort_values(ascending=False).head(10)   #         

Date 0 Date_received 0 Distance 0 Discount_rate 0 Coupon_id 0 Merchant_id 0 User_id 0 dtype: int64
dfoff.info()   #      

RangeIndex: 1754884 entries, 0 to 1754883 Data columns (total 7 columns): User_id int64 Merchant_id int64 Coupon_id object Discount_rate object Distance object Date_received object Date object dtypes: int64(2), object(5) memory usage: 93.7+ MB
dfoff["Date_received"].unique()      #           Data_receive      
dfoff["Date"].unique()
print("    ,    :" ,dfoff[(dfoff["Date_received"] != "null") & (dfoff["Date"] != "null")].shape[0])
print('    ,    :%d' % dfoff[(dfoff['Date_received'] != 'null') & (dfoff['Date'] == 'null')].shape[0])
print('    ,    :%d' % dfoff[(dfoff['Date_received'] == 'null') & (dfoff['Date'] != 'null')].shape[0])
print('    ,    :%d' % dfoff[(dfoff['Date_received'] == 'null') & (dfoff['Date'] == 'null')].shape[0])

쿠폰 있음, 구매 상품: 75382 쿠폰 있음, 미구매 상품: 977900 쿠폰 없음, 구매 상품: 701602 쿠폰 없음, 미구매 상품: 0
#                   
print('1. User_id in training set but not in test set', set(dftest['User_id']) - set(dfoff['User_id']))
#                   
print('2. Merchant_id in training set but not in test set', set(dftest['Merchant_id']) - set(dfoff['Merchant_id']))
  • User_id in training set but not in test set {2495873, 1286474}
  • Merchant_id in training set but not in test set {5920}

  • 2: 세척 데이터 집합과 추출 특징 지식점 1: unique 함수는 몇 가지 유형을 볼 수 있습니까
    지식점2: 데이터가 한 줄에 집중된 특징을 추출하려면 dfoff['중간 저장 제목']+.함수라는 형식.예: 아래
    특징1: 할인율
    우리는 첫 번째 특징이 바로 할인율이라고 생각했다. 할인 강도가 클수록 사용자가 쿠폰을 사용할 확률이 높다. 먼저 유니크 함수로 몇 가지 할인 유형이 있는지 살펴보자.
    print('Discount_rate   :',dfoff['Discount_rate'].unique())
    

    Discount_rate 유형:[‘null’ ‘150:20’ ‘20:1’ ‘200:20’ ‘30:5’ ‘50:10’ ‘10:5’ ‘100:10’ ‘200:30’ ‘20:5’ ‘30:10’ ‘50:5’ ‘150:10’ ‘100:30’ ‘200:50’ ‘100:50’ ‘300:30’ ‘50:20’ ‘0.9’ ‘10:1’ ‘30:1’ ‘0.95’ ‘100:5’ ‘5:1’ ‘100:20’ ‘0.8’ ‘50:1’ ‘200:10’ ‘300:20’ ‘100:1’ ‘150:30’ ‘300:50’ ‘20:10’ ‘08.85'0.6','150:50','0.75','0.5','200:5','0.7','30:20','300:10','0.2','50:30','200:100','150:5'] 인쇄 결과에 따르면 할인 유형은 모두 3종이다.
    첫 번째는 nan, 세일 없음
    두 번째는 150:20으로 150원 차면 20원 감소.
    세 번째는 0.95 할인. 0.95 할인.
    따라서 다음과 같은 네 가지 특성을 추출할 수 있는 함수를 구축했습니다.
    할인 유형: getDiscountType()
    할인율:convertRate
    가득 참: getDiscountMan
    감량: getDiscountJian
    # convert Discount_rate and Distance
    
    def getDiscountType(row):   # row        
        if row == 'null':
            return 'null'       #   
        elif ':' in row:
            return 1            #   
        else:
            return 0            #   
    
    def convertRate(row):
        """Convert discount to rate"""
        if row == 'null':                                   #   
            return 1.0
        elif ':' in row:
            rows = row.split(':')
            return 1.0 - float(rows[1])/float(rows[0])      #   ,      
        else:
            return float(row)                               #   
    
    def getDiscountMan(row):
        if ':' in row:
            rows = row.split(':')
            return int(rows[0])                             #      ,        row[0]
        else:
            return 0                                        #     0
    
    def getDiscountJian(row): 
        if ':' in row:
            rows = row.split(':')                 
            return int(rows[1])                             #      ,        row[1]
        else:
            return 0                                        #    0
        
    def processData(df):                                  #      processData,
        
        # convert discunt_rate
        df['discount_rate'] = df['Discount_rate'].apply(convertRate)#            , Discount_rate   
        df['discount_man'] = df['Discount_rate'].apply(getDiscountMan)
        df['discount_jian'] = df['Discount_rate'].apply(getDiscountJian)
        df['discount_type'] = df['Discount_rate'].apply(getDiscountType)
        print(df['discount_rate'].unique())                         #      discount_rate  
        
        # convert distance
       
        df['distance'] = df['Distance'].replace('null', -1).astype(int) 
        
        return df 
    
    dfoff = processData(dfoff)     #  ,  processData    ,         
    dftest = processData(dftest)   #          ,dftest       
    

    [1. 0.86666667 0.95 0.9 0.83333333 0.8 0.5 0.85 0.75 0.66666667 0.93333333 0.7 0.6 0.96666667 0.98 0.99 0.975 0.33333333 0.2 0.4 ] [0.83333333 0.9 0.96666667 0.8 0.95 0.75 0.98 0.5 0.86666667 0.6 0.66666667 0.7 0.85 0.33333333 0.94 0.93333333 0.975 0.99 ]
    #     ,        4 "Date	discount_rate","discount_man","discount_jian","discount_type"
    dfoff.head(2)  
    
    #print('Distance   :', dfoff['Distance'].unique())
    

    데이터 세트 클리닝 2: 데이터 세트의 유형 변환 처리 "Distance"거리 열
    #df['distance'] = df['Distance'].replace('null', -1).astype(int)  # distance  null   -1,       
    #print(df['distance'].unique())                                   #  distance          
    
    dftest.head(2)     #  ,        
    
    date_received = dfoff['Date_received'].unique()     #   data_received     
    date_received = sorted(date_received[date_received != 'null'])
    
    date_buy = dfoff['Date'].unique()
    date_buy = sorted(date_buy[date_buy != 'null'])
    
    date_buy = sorted(dfoff[dfoff['Date'] != 'null']['Date'])
    print('        ',date_received[0],' ', date_received[-1])   
    print('     ', date_buy[0], ' ', date_buy[-1])
    

    쿠폰 수령일 20160101~20160615 소비일 20160101~20160630
    특징2: 요일 특징을 추출하고 소비 시간은 요일과 관련이 있을 가능성이 더 높다.
    def getWeekday(row):         #   getWeekday   ,row     
        if row == 'null':        #    ,      
            return row
        else:
            return date(int(row[0:4]), int(row[4:6]), int(row[6:8])).weekday() + 1
    
    #     Date_received      ,      ,    getWeekday  ,      weekday
    dfoff['weekday'] = dfoff['Date_received'].astype(str).apply(getWeekday)
    dftest['weekday'] = dftest['Date_received'].astype(str).apply(getWeekday)
    
    # weekday_type :        1,   0
    dfoff['weekday_type'] = dfoff['weekday'].apply(lambda x : 1 if x in [6,7] else 0 )
    dftest['weekday_type'] = dftest['weekday'].apply(lambda x : 1 if x in [6,7] else 0 )
    dfoff.head()
    

    데이터 집합 처리 방식 3: 원-hot 독열 인코딩 진행
    # change weekday to one-hot encoding       
    weekdaycols = ['weekday_' + str(i) for i in range(1,8)]
    print(weekdaycols)                                                     #   weekdaycols     
    
    tmpdf = pd.get_dummies(dfoff['weekday'].replace('null', np.nan))  #  one_hot  
    tmpdf.columns = weekdaycols   #      
    dfoff[weekdaycols] = tmpdf
    
    tmpdf = pd.get_dummies(dftest['weekday'].replace('null', np.nan))  #  one_hot  
    tmpdf.columns = weekdaycols
    dftest[weekdaycols] = tmpdf
    dfoff.head()
    

    [‘weekday_1’, ‘weekday_2’, ‘weekday_3’, ‘weekday_4’, ‘weekday_5’, ‘weekday_6’, ‘weekday_7’]
    자, 상기 간단한 특징 추출을 통해 우리는 모두 14개의 유용한 특징을 얻었습니다:discountrate
    discount_type
    discount_man
    discount_jian
    distanceweek
    distance
    dayweekday_type
    weekday_1
    weekday_2
    weekday_3
    weekday_4
    weekday_5
    weekday_6
    weekday_7
    마크업 레이블 레이블
    특징이 생긴 후에 우리는 훈련 견본에 대해 label 표시를 해야 한다. 즉, 어떤 것이 정견본(y=1)이고 어떤 것이 마이너스 견본(y=0)인지 확인해야 한다.우리가 예측해야 할 것은 사용자가 쿠폰을 받은 후 15 이내의 소비 상황이다.그래서 총 세 가지 상황이 있다.
    1.Date_received == ‘null’:
    쿠폰을 받지 못했다는 뜻이니 고려할 필요가 없다. y=-1
    2.(Date_received != ‘null’) & (Date != ‘null’) & (Date - Date_received <= 15):
    쿠폰을 수령하고 15일 이내에 사용한다는 뜻, 즉 정견본, y=1
    3.(Date_received != ‘null’) & ((Date == ‘null’) | (Date - Date_received > 15)):
    쿠폰 수령이 15일 이내에 사용되지 않음, 즉 마이너스 샘플, y=0
    자, 규칙을 알게 되면 탭 비고 함수를 정의할 수 있습니다.
    플러스 마이너스 샘플
    def label(row):            #       label,row   
        if row['Date_received'] == 'null':
            return -1                #Date_received    ,       ,    
        if row['Date'] != 'null':
            td = pd.to_datetime(row['Date'], format='%Y%m%d') -  pd.to_datetime(row['Date_received'], format='%Y%m%d')
            if td <= pd.Timedelta(15, 'D'):               #         15      ,     
                return 1
        return 0                 #           15      ,     
    dfoff['label'] = dfoff.apply(label, axis = 1)
    
    dfoff["label"].unique()  #unique       
    

    array([-1, 0, 1], dtype=int64)
    value_counts 함수는 각 유형의 개수를 통계하는 것이다
    우리는 이 함수를 사용하여 훈련집에 표시를 해서 양과 음의 견본이 도대체 얼마나 많은지 볼 수 있다.
    print(dfoff['label'].value_counts())   #          
    

    0 988887 -1 701602 1 64395 Name: label, dtype: int64
    분명히 정본은 모두 64395건, 음본은 모두 988887건이다.분명히 플러스와 마이너스 견본의 수량 차이가 매우 크다.모델 성능 평가 기준으로 AUC를 사용하는 이유이기도 합니다.
    dfoff.columns.tolist () 함수는 헤더를 보는 데 사용됩니다
    print('  columns:',dfoff.columns.tolist())     #          
    

    이미 있는 columns:[‘User_id’, ‘Merchant_id’, ‘Coupon_id’, ‘Discount_rate’, ‘Distance’, ‘Date_received’, ‘Date’, ‘discount_rate’, ‘discount_man’, ‘discount_jian’, ‘discount_type’, ‘distance’, ‘weekday’, ‘weekday_type’, ‘weekday_1’, ‘weekday_2’, ‘weekday_3’, ‘weekday_4’, ‘weekday_5’, ‘weekday_6’, ‘weekday_7’, ‘label’]
    dfoff.head(2)
    

    셋째, 모형을 세우는 것은 다음으로 가장 주요한 기계 학습 모형을 세우는 것이다.먼저 우리가 선택한 특징은 위에서 추출한 14가지 특징이다. 모델의 성능을 검증하기 위해 검증집을 구분하여 모델 검증을 해야 한다. 구분 방식은 수령 날짜, 즉 훈련집: 20160101-20160515, 검증집: 20160516-20160615이다.우리는 XGboost 알고리즘을 채택했다
    xgboost 1.훈련집과 검증집을 구분하여 얻은 결과에 주의하라predprob는 확률값이다.
    마지막으로 검증 세트에 대해 AUC를 계산할 수 있습니다.sklearn 라이브러리에서 자체 계산한 AUC 함수를 직접 호출하면 됩니다.
    # data split
    df = dfoff[dfoff['label'] != -1].copy()               #             df
    train = df[(df['Date_received'] < '20160516')].copy()   #df     20160516      
    valid = df[(df['Date_received'] >= '20160516') & (df['Date_received'] <= '20160615')].copy()  #df     20160516      
    print(train['label'].value_counts())               #           
    print(valid['label'].value_counts())               #           
    

    0 759172 1 41524 Name: label, dtype: int64 0 229715 1 22871 Name: label, dtype: int64
    #train.head(5)
    
    #valid.head(5)
    
    y = train.label
    #drop        
    #          ,              
    X = train.drop(["User_id","Merchant_id","Coupon_id","Discount_rate","Distance","Date","Date_received","label"],axis=1)  
    val_y = valid.label
    
    #          ,              
    val_X = valid.drop(["User_id","Merchant_id","Coupon_id","Discount_rate","Distance","Date","Date_received","label"],axis=1)
    
    #          ,              
    tests = dftest.drop(["User_id","Merchant_id","Coupon_id","Discount_rate","Distance","Date_received"],axis=1)
    
    val_X["weekday"].unique(),val_X["discount_type"].unique()
     #      ,  weekday discount_type object  ,          
    

    (array([6, 1, 4, 3, 2, 7, 5], dtype=object), array([1, 0], dtype=object))
    #astype            
    # weekday      ,   int 
    X["weekday"] = X["weekday"].astype(int)
    X["discount_type"] =X["discount_type"].astype(int)
    val_X["weekday"]=val_X["weekday"].astype(int)
    val_X["discount_type"]=val_X["discount_type"].astype(int)
    
    tests["weekday"].unique()
    
    array([2, 3, 5, 6, 7, 1, 4], dtype=int64)
    

    val_X[“weekday”].unique () # 보기
     array([6, 1, 4, 3, 2, 7, 5], dtype=int64)
    
    #xgb    
    xgb_val = xgb.DMatrix(val_X,label=val_y)  #             xgb  
    xgb_train = xgb.DMatrix(X, label=y)       #     label   xgb  
    xgb_test = xgb.DMatrix(tests)             #           xgb  
    xgb_val_X = xgb.DMatrix(val_X)            #           xgb  
    

    C:\ProgramData\Anaconda3\lib\site-packages\xgboost\core.py:587: FutureWarning: Series.base is deprecated and will be removed in a future version if getattr(data, ‘base’, None) is not None and C:\ProgramData\Anaconda3\lib\site-packages\xgboost\core.py:588: FutureWarning: Series.base is deprecated and will be removed in a future version data.base is not None and isinstance(data, np.ndarray)\
    #          (auc)
    def myauc(test):
        testgroup = test.groupby(["Coupon_id"])
        aucs = []
        for i in testgroup:
            tmpdf = i[1]
            if len(tmpdf['label'].unique()) != 2:
                continue
            fpr, tpr, thresholds = roc_curve(tmpdf['label'], tmpdf['pred'], pos_label=1)
            aucs.append(auc(fpr, tpr))
        return np.average(aucs)
    

    XGboost 알고리즘 프레임워크
    params = {'booster': 'gbtree',
              #'objective': 'rank:pairwise',
              'eval_metric': 'auc',
              'gamma': 0.1,             
              'min_child_weight': 1.1,
              'max_depth': 5,            
              'lambda': 10,
              'subsample': 0.7,
              'colsample_bytree': 0.7,
              'colsample_bylevel': 0.7,
              'eta': 0.01,
              'tree_method': 'exact',
              'seed': 0,
              'nthread': 12
              }
    watchlist = [(xgb_train, 'train')]
    model = xgb.train(params, xgb_train, num_boost_round=1000, evals=watchlist,early_stopping_rounds=100)
    
    model.save_model('C:/Users/Administrator/o2o.code/notebook/xgbmodel')
    model = xgb.Booster(params)
    model.load_model('C:/Users/Administrator/o2o.code/notebook/xgbmodel')
    
    val_X.head()
    
    valid.head()
    
    model = xgb.Booster()
    model.load_model('C:/Users/Administrator/o2o.code/notebook/xgbmodel')   #    
    
    temp = valid[["Coupon_id", "label"]].copy()   #  "Coupon_id", "label"  
    temp['pred'] = model.predict(xgb_val)         #    
    temp.pred = MinMaxScaler(copy=True, feature_range=(0, 1)).fit_transform(temp['pred'].values.reshape(-1, 1))
    print(myauc(temp))                            #         AUC 
    temp.head()
    

    0.5518357641394374
    tests.head()
    
    val_X.head()
    
    #predeict,    
    y_test = dftest[['User_id','Coupon_id',"Date_received"]].copy()
    
    y_test['label'] =  model.predict(xgb_test)   #        
    
    #       
    y_test.to_csv("C:/Users/Administrator/o2o.code/notebook/second.csv", index=None, header=None)
    y_test.head()
    

    좋은 웹페이지 즐겨찾기