kaggle竞赛实战10——特征优化

特征优化思路:

在完成常规流程后,如果不知道怎么办,可以针对文本or时间序列特征进行进一步处理

首先,我们注意到,每一笔信用卡的交易记录都有交易时间,而对于时间字段和文本字段,普通的批量创建特征的方法都是无法较好的挖掘其全部信息的,因此我们需要围绕交易字段中的交易时间进行额外的特征衍生。此处我们可以考虑构造一些用于描述用户行为习惯的特征(经过反复验证,用户行为特征是最为有效的提高预测结果的特征类),包括最近一次交易与首次交易的时间差、信用卡激活日期与首次交易的时间差、用户两次交易平均时间间隔、按照不同交易地点/商品品类进行聚合(并统计均值、方差等统计量)。此外,我们也知道越是接近当前时间点的用户行为越有价值,因此我们还需要重点关注用户最近两个月(实际时间跨度可以自行决定)的行为特征,以两个月为跨度,进一步统计该时间周期内用户的上述交易行为特点,并带入模型进行训练。

其次是二阶交叉特征,在此前的特征衍生过程中,我们曾进行了交叉特征衍生,但只是进行了一阶交叉衍生,例如交易额在不同商品上的汇总,但实际上还可以进一步构造二阶衍生,例如交易额在不同商品组合上的汇总。通常来说更高阶的衍生会导致特征矩阵变得更加稀疏,并且由于每一阶的衍生都会创造大量特征,因此更高阶的衍生往往也会造成维度爆炸,因此高阶交叉特征衍生需要谨慎。不过正如此前我们考虑的,由于用户行为特征对模型结果有更大的影响,因此我们可以单独围绕用户行为数据进行二阶交叉特征衍生,并在后续建模前进行特征筛选。

其三,我们需要对异常值加强思考,而模型误差大部分都由于异常值导致。实际上,用户评分是通过某套公式人工计算得出的,因此这些异常值极有可能是某类特殊用户的标记,因此我们不妨在实际建模过程中进行两阶段建模,即先预测每个输入样本是否是异常样本,并根据分类预测结果进行后续的回归建模

而为了保证后续两阶段建模时第一阶段的分类模型能够更加准确的识别异常用户,我们需要创建一些基于异常用户的特征聚合字段,例如异常用户平均消费次数、消费金额等

模型优化:

使用catboost,CatBoost是由俄罗斯搜索引擎Yandex在2017年7月开源的一个GBM算法,自开源之日起,CatBoost就因为其强大的效果与极快的执行效率广受算法工程人员青睐。在诸多CatBoost的算法优势中,最引人关注的当属该模型能够自主采用独热编码和平均编码的混合策略来处理类别特征,也就是说CatBoost将一些经过实践验证的、普遍有效的特征工程方法融入了实际模型训练过程;此外,CatBoost还提出了一种全新的梯度提升的机制,能够非常好的在经验风险和结构风险中做出权衡(即能够很好的提升精度、同时又能够避免过拟合问题)。

在后续的建模环节中,我们就将使用CatBoost替换随机森林,并最终带入CatBoost、XGBoost和LightGBM三个模型进行模型融合。

并且,需要注意的是,在实际二阶段建模过程时,我们需要在每个建模阶段都进行交叉验证与模型融合,才能最大化提升模型效果。也就是说我们需要训练三组模型(以及对应进行三次模型融合),来完成分类预测问题、普通用户回归预测问题和异常用户回归预测问题。三轮建模关系如下:

1356e2f466df400b8d9b61dacd69ad3c.png

不难发现,整体建模与融合过程都将变得更加复杂。不过这也是更加贴近真实状态的一种情况,很多时候算法和融合过程都只是元素,如何构建一个更加精准、高效的训练流程,才是进阶的算法工程人员更需要考虑的问题。

接下来从头开始

加载训练集,测试集,基本处理 
     train = pd.read_csv('data/train.csv') 
     test = pd.read_csv('data/test.csv') 
      
     target = train['target'] 

    for df in [train, test]:     
         df['year']  = df['first_active_month'].fillna('0-0').apply(lambda x:int(str(x).split('-')[0])) #把这列里的空值填充为0-0,然后将它按-分割,取出月份

        df['first_active_month'] = pd.to_datetime(df['first_active_month']) #在日期后面加个-01改为完整日期

   df['elapsed_time'] = (datetime.date(2018,3, 1) - df['first_active_month'].dt.date).dt.days #计算和2018-03-1间的间隔日期,因为数据集是以18年2月份为时间分割的

df['first_active_month'] = pd.to_datetime(df['first_active_month']) 
         df['weekofyear'] = df['first_active_month'].dt.weekofyear 
         df['dayofyear'] = df['first_active_month'].dt.dayofyear 
         df['month'] = df['first_active_month'].dt.month 

 ## 交易表合并train test 
     train_test = pd.concat([train[['card_id','first_active_month']], test[['card_id','first_active_month']] ], axis=0, ignore_index=True) 
     historical_transactions   = historical_transactions.merge(train_test[['card_id','first_active_month']], on=['card_id'], how='left') 
     new_transactions = new_transactions.merge(train_test[['card_id','first_active_month']], on=['card_id'], how='left') 

 然后需要进行时间字段的更细粒度的呈现: 

  def month_trans(x):  
         return x // 30 
      
   def week_trans(x):  
         return x // 7 

     def get_expand_common(df_): 
         df = df_.copy() #先复制下这个表,因为要直接修改表


         df['category_2'].fillna(1.0,inplace=True) 
         df['category_3'].fillna('A',inplace=True) 
         df['category_3'] = df['category_3'].map({'A':0, 'B':1, 'C':2}) 
         df['merchant_id'].fillna('M_ID_00a6ca8a8a',inplace=True) 
         df['installments'].replace(-1, np.nan,inplace=True) 
         df['installments'].replace(999, np.nan,inplace=True) 
         df['installments'].replace(0, 1,inplace=True) 
          
         df['purchase_amount'] = np.round(df['purchase_amount'] / 0.00150265118 + 497.06,8) 
         df['purchase_amount'] = df.purchase_amount.apply(lambda x: np.round(x)) 
          
         df['purchase_date']          =  pd.to_datetime(df['purchase_date'])  
         df['first_active_month']     =  pd.to_datetime(df['first_active_month'])  
         df['purchase_hour']          =  df['purchase_date'].dt.hour 
         df['year']                   = df['purchase_date'].dt.year 
         df['month']                  =  df['purchase_date'].dt.month 
         df['day']                    = df['purchase_date'].dt.day 
         df['hour']                   = df['purchase_date'].dt.hour 
         df['weekofyear'] = df['purchase_date'].dt.weekofyear 
         df['dayofweek']              =  df['purchase_date'].dt.dayofweek 
         df['weekend']                =  (df.purchase_date.dt.weekday >=5).astype(int)  
         df                           =  df.sort_values(['card_id','purchase_date'])  
         df['purchase_date_floorday'] =  df['purchase_date'].dt.floor('d')  #删除小于day的时间 
          
         # 距离激活时间的相对时间,0, 1,2,3,...,max-act 
         df['purchase_day_since_active_day']   = df['purchase_date_floorday'] - df['first_active_month']  #ht_card_id_gp['purchase_date_floorday'].transform('min') 
         df['purchase_day_since_active_day']   = df['purchase_day_since_active_day'].dt.days  #.astype('timedelta64[D]')  
         df['purchase_month_since_active_day'] = df['purchase_day_since_active_day'].agg(month_trans).values 
         df['purchase_week_since_active_day']  = df['purchase_day_since_active_day'].agg(week_trans).values 
          
         # 距离最后一天时间的相对时间,0,1,2,3,...,max-act 
         ht_card_id_gp = df.groupby('card_id') 
         df['purchase_day_since_reference_day']   =  ht_card_id_gp['purchase_date_floorday'].transform('max') - df['purchase_date_floorday'] 
         df['purchase_day_since_reference_day']   =  df['purchase_day_since_reference_day'].dt.days 
         # 一个粗粒度的特征(距离最近购买过去了几周,几月) 
         df['purchase_week_since_reference_day']  = df['purchase_day_since_reference_day'].agg(week_trans).values 
         df['purchase_month_since_reference_day'] = df['purchase_day_since_reference_day'].agg(month_trans).values 
          
         df['purchase_day_diff']   =  df['purchase_date_floorday'].shift() 
         df['purchase_day_diff']   =  df['purchase_date_floorday'].values - df['purchase_day_diff'].values 
         df['purchase_day_diff']   =  df['purchase_day_diff'].dt.days 
         df['purchase_week_diff']  =  df['purchase_day_diff'].agg(week_trans).values 
         df['purchase_month_diff'] =  df['purchase_day_diff'].agg(month_trans).values  
          
         df['purchase_amount_ddgd_98']  = df['purchase_amount'].values * df['purchase_day_since_reference_day'].apply(lambda x:0.98**x).values 
         df['purchase_amount_ddgd_99']  = df['purchase_amount'].values * df['purchase_day_since_reference_day'].apply(lambda x:0.99**x).values     
         df['purchase_amount_wdgd_96']  = df['purchase_amount'].values * df['purchase_week_since_reference_day'].apply(lambda x:0.96**x).values  
         df['purchase_amount_wdgd_97']  = df['purchase_amount'].values * df['purchase_week_since_reference_day'].apply(lambda x:0.97**x).values  
         df['purchase_amount_mdgd_90']  = df['purchase_amount'].values * df['purchase_month_since_reference_day'].apply(lambda x:0.9**x).values 
         df['purchase_amount_mdgd_80']  = df['purchase_amount'].values * df['purchase_month_since_reference_day'].apply(lambda x:0.8**x).values  
          
         df = reduce_mem_usage(df) 
          
         return df 


     historical_transactions = get_expand_common(historical_transactions) 
     new_transactions        = get_expand_common(new_transactions) 

在执行完数据清洗与时间字段的处理之后,接下来我们需要开始执行特征优化。根据此前介绍的思路,首先我们需要进行基础行为特征字段衍生: 

构造基本统计特征 
     def aggregate_transactions(df_, prefix):  
          
         df = df_.copy() 
          
         df['month_diff'] = ((datetime.datetime.today() - df['purchase_date']).dt.days)//30 
         df['month_diff'] = df['month_diff'].astype(int) 
         df['month_diff'] += df['month_lag'] 
          
         df['price'] = df['purchase_amount'] / df['installments'] 
         df['duration'] = df['purchase_amount'] * df['month_diff'] 
         df['amount_month_ratio'] = df['purchase_amount'] / df['month_diff'] 
          
         df.loc[:, 'purchase_date'] = pd.DatetimeIndex(df['purchase_date']).\\ 
                                           astype(np.int64) * 1e-9 
          
         agg_func = { 
             'category_1':      ['mean'], 
             'category_2':      ['mean'], 
             'category_3':      ['mean'], 
             'installments':    ['mean', 'max', 'min', 'std'], 
             'month_lag':       ['nunique', 'mean', 'max', 'min', 'std'], 
             'month':           ['nunique', 'mean', 'max', 'min', 'std'], 
             'hour':            ['nunique', 'mean', 'max', 'min', 'std'], 
             'weekofyear':      ['nunique', 'mean', 'max', 'min', 'std'], 
             'dayofweek':       ['nunique', 'mean'], 
             'weekend':         ['mean'], 
             'year':            ['nunique'], 
             'card_id':         ['size','count'], 
             'purchase_date':   ['max', 'min'], 
             ### 
             'price':             ['mean','max','min','std'], 
             'duration':          ['mean','min','max','std','skew'], 
             'amount_month_ratio':['mean','min','max','std','skew'], 
             }  
          
         for col in ['category_2','category_3']: 
             df[col+'_mean'] = df.groupby([col])['purchase_amount'].transform('mean') 
             agg_func[col+'_mean'] = ['mean'] 
          
         agg_df = df.groupby(['card_id']).agg(agg_func) 
         agg_df.columns = [prefix + '_'.join(col).strip() for col in agg_df.columns.values] 
         agg_df.reset_index(drop=False, inplace=True) 
        
         return agg_df 
     print('generate statistics features...') 
     auth_base_stat = aggregate_transactions(historical_transactions[historical_transactions['authorized_flag']==1], prefix='auth_') 
     print('generate statistics features...') 
     hist_base_stat = aggregate_transactions(historical_transactions[historical_transactions['authorized_flag']==0], prefix='hist_') 
     print('generate statistics features...') 
     new_base_stat  = aggregate_transactions(new_transactions, prefix='new_') 

    def get_quantile(x, percentiles = [0.1, 0.25, 0.75, 0.9]): 
         x_len = len(x) 
         x = np.sort(x) 
         sts_feas = []   
         for per_ in percentiles: 
             if per_ == 1: 
                 sts_feas.append(x[x_len - 1])  
             else: 
                 sts_feas.append(x[int(x_len * per_)])  
         return sts_feas  
      
     def get_cardf_tran(df_, month = 3, prefix = '_'): 
          
         df = df_.copy()  
         if prefix == 'hist_cardf_': 
             df['month_to_now']  =  (datetime.date(2018, month, 1) - df['purchase_date_floorday'].dt.date).dt.days 
          
         df['month_diff'] = ((datetime.datetime.today() - df['purchase_date']).dt.days)//30 
         df['month_diff'] = df['month_diff'].astype(int) 
         df['month_diff'] += df['month_lag'] 
          
         print('*'*30,'Part1, whole data','*'*30) 
         cardid_features = pd.DataFrame() 
         cardid_features['card_id'] = df['card_id'].unique()    
         print( '*' * 30, 'Traditional Features', '*' * 30) 
         ht_card_id_gp = df.groupby('card_id')  
         cardid_features['card_id_cnt'] = ht_card_id_gp['authorized_flag'].count().values 
          
         if  prefix == 'hist_cardf_': 
             cardid_features['card_id_isau_mean'] = ht_card_id_gp['authorized_flag'].mean().values 
             cardid_features['card_id_isau_sum'] = ht_card_id_gp['authorized_flag'].sum().values  
          
         cardid_features['month_diff_mean']   = ht_card_id_gp['month_diff'].mean().values 
         cardid_features['month_diff_median'] = ht_card_id_gp['month_diff'].median().values 
          
         if prefix == 'hist_cardf_': 
             cardid_features['reference_day']           =  ht_card_id_gp['purchase_date_floorday'].max().values 
             cardid_features['first_day']               =  ht_card_id_gp['purchase_date_floorday'].min().values  
             cardid_features['activation_day']          =  ht_card_id_gp['first_active_month'].max().values 
             
             # first to activation day 
             cardid_features['first_to_activation_day']  =  (cardid_features['first_day'] - cardid_features['activation_day']).dt.days 
             # activation to reference day  
             cardid_features['activation_to_reference_day']  =  (cardid_features['reference_day'] - cardid_features['activation_day']).dt.days 
             # first to last day  
             cardid_features['first_to_reference_day']  =  (cardid_features['reference_day'] - cardid_features['first_day']).dt.days 
             # reference day to now   
             cardid_features['reference_day_to_now']  =  (datetime.date(2018, month, 1) - cardid_features['reference_day'].dt.date).dt.days  
             # first day to now 
             cardid_features['first_day_to_now']  =  (datetime.date(2018, month, 1) - cardid_features['first_day'].dt.date).dt.days  
              
             print('card_id(month_lag, min to reference day):min') 
             cardid_features['card_id_month_lag_min'] = ht_card_id_gp['month_lag'].agg('min').values    
             # is_purchase_before_activation,first_to_reference_day_divide_activation_to_reference_day 
             cardid_features['is_purchase_before_activation'] = cardid_features['first_to_activation_day'] < 0  
             cardid_features['is_purchase_before_activation'] = cardid_features['is_purchase_before_activation'].astype(int) 
             cardid_features['first_to_reference_day_divide_activation_to_reference_day'] = cardid_features['first_to_reference_day']  / (cardid_features['activation_to_reference_day']  + 0.01) 
             cardid_features['days_per_count'] = cardid_features['first_to_reference_day'].values / cardid_features['card_id_cnt'].values 
         
         if prefix == 'new_cardf_': 
             print(' Eight time features, ')  
             cardid_features['reference_day']           =  ht_card_id_gp['reference_day'].last().values 
             cardid_features['first_day']               =  ht_card_id_gp['purchase_date_floorday'].min().values  
             cardid_features['last_day']                =  ht_card_id_gp['purchase_date_floorday'].max().values 
             cardid_features['activation_day']          =  ht_card_id_gp['first_active_month'].max().values 
             # reference to first day 
             cardid_features['reference_day_to_first_day']  =  (cardid_features['first_day'] - cardid_features['reference_day']).dt.days 
             # reference to last day 
             cardid_features['reference_day_to_last_day']  =  (cardid_features['last_day'] - cardid_features['reference_day']).dt.days   
             # first to last day  
             cardid_features['first_to_last_day']  =  (cardid_features['last_day'] - cardid_features['first_day']).dt.days 
             # activation to first day  
             cardid_features['activation_to_first_day']  =  (cardid_features['first_day'] - cardid_features['activation_day']).dt.days 
             # activation to first day  
             cardid_features['activation_to_last_day']  =  (cardid_features['last_day'] - cardid_features['activation_day']).dt.days 
             # last day to now   
             cardid_features['reference_day_to_now']  =  (datetime.date(2018, month, 1) - cardid_features['reference_day'].dt.date).dt.days  
             # first day to now 
             cardid_features['first_day_to_now']  =  (datetime.date(2018, month, 1) - cardid_features['first_day'].dt.date).dt.days  
              
             print('card_id(month_lag, min to reference day):min') 
             cardid_features['card_id_month_lag_max'] = ht_card_id_gp['month_lag'].agg('max').values   
             cardid_features['first_to_last_day_divide_reference_to_last_day'] = cardid_features['first_to_last_day']  / (cardid_features['reference_day_to_last_day']  + 0.01) 
             cardid_features['days_per_count'] = cardid_features['first_to_last_day'].values / cardid_features['card_id_cnt'].values 
          
         for f in ['reference_day', 'first_day', 'last_day', 'activation_day']: 
             try: 
                 del cardid_features[f] 
             except: 
                 print(f, '不存在!!!') 
      
         print('card id(city_id,installments,merchant_category_id,.......):nunique, cnt/nunique')  
         for col in tqdm_notebook(['category_1','category_2','category_3','state_id','city_id','installments','merchant_id', 'merchant_category_id','subsector_id','month_lag','purchase_date_floorday']): 
             cardid_features['card_id_%s_nunique'%col]            =  ht_card_id_gp[col].nunique().values 
             cardid_features['card_id_cnt_divide_%s_nunique'%col] =  cardid_features['card_id_cnt'].values / cardid_features['card_id_%s_nunique'%col].values 
               
         print('card_id(purchase_amount & degrade version ):mean,sum,std,median,quantile(10,25,75,90)')  
         for col in tqdm_notebook(['installments','purchase_amount','purchase_amount_ddgd_98','purchase_amount_ddgd_99','purchase_amount_wdgd_96','purchase_amount_wdgd_97','purchase_amount_mdgd_90','purchase_amount_mdgd_80']): 
             if col =='purchase_amount': 
                 for opt in ['sum','mean','std','median','max','min']: 
                     cardid_features['card_id_' +col+ '_' + opt] = ht_card_id_gp[col].agg(opt).values 
                  
                 cardid_features['card_id_' +col+ '_range'] =  cardid_features['card_id_' +col+ '_max'].values - cardid_features['card_id_' +col+ '_min'].values 
                 percentiles = ht_card_id_gp[col].apply(lambda x:get_quantile(x,percentiles = [0.025, 0.25, 0.75, 0.975]))  
      
                 cardid_features[col + '_2.5_quantile']  = percentiles.map(lambda x:x[0]).values 
                 cardid_features[col + '_25_quantile'] = percentiles.map(lambda x:x[1]).values 
                 cardid_features[col + '_75_quantile'] = percentiles.map(lambda x:x[2]).values 
                 cardid_features[col + '_97.5_quantile'] = percentiles.map(lambda x:x[3]).values 
                 cardid_features['card_id_' +col+ '_range2'] =  cardid_features[col+ '_97.5_quantile'].values - cardid_features[col+ '_2.5_quantile'].values 
                 del cardid_features[col + '_2.5_quantile'],cardid_features[col + '_97.5_quantile'] 
                 gc.collect() 
             else: 
                 for opt in ['sum']: 
                     cardid_features['card_id_' +col+ '_' + opt] = ht_card_id_gp[col].agg(opt).values           
          
         print( '*' * 30, 'Pivot Features', '*' * 30) 
         print('Count  Pivot') #purchase_month_since_reference_day(可能和month_lag重复),百分比降分,暂时忽略 (dayofweek,merchant_cate,state_id)作用不大installments 
         for pivot_col in tqdm_notebook(['category_1','category_2','category_3','month_lag','subsector_id','weekend']): #'city_id',, 
          
             tmp     = df.groupby(['card_id',pivot_col])['merchant_id'].count().to_frame(pivot_col + '_count') 
             tmp.reset_index(inplace =True)   
               
             tmp_pivot = pd.pivot_table(data=tmp,index = 'card_id',columns=pivot_col,values=pivot_col + '_count',fill_value=0) 
             tmp_pivot.columns = [tmp_pivot.columns.names[0] + '_cnt_pivot_'+ str(col) for col in tmp_pivot.columns] 
             tmp_pivot.reset_index(inplace = True) 
             cardid_features = cardid_features.merge(tmp_pivot, on = 'card_id', how='left') 
            
             if  pivot_col!='weekend' and  pivot_col!='installments': 
                 tmp            = df.groupby(['card_id',pivot_col])['purchase_date_floorday'].nunique().to_frame(pivot_col + '_purchase_date_floorday_nunique')  
                 tmp1           = df.groupby(['card_id'])['purchase_date_floorday'].nunique().to_frame('purchase_date_floorday_nunique')  
                 tmp.reset_index(inplace =True)   
                 tmp1.reset_index(inplace =True)    
                 tmp  = tmp.merge(tmp1, on ='card_id', how='left') 
                 tmp[pivot_col + '_day_nunique_pct'] = tmp[pivot_col + '_purchase_date_floorday_nunique'].values / tmp['purchase_date_floorday_nunique'].values 
               
                 tmp_pivot = pd.pivot_table(data=tmp,index = 'card_id',columns=pivot_col,values=pivot_col + '_day_nunique_pct',fill_value=0) 
                 tmp_pivot.columns = [tmp_pivot.columns.names[0] + '_day_nunique_pct_'+ str(col) for col in tmp_pivot.columns] 
                 tmp_pivot.reset_index(inplace = True) 
                 cardid_features = cardid_features.merge(tmp_pivot, on = 'card_id', how='left') 
          
         if prefix == 'new_cardf_': 
         ######## 在卡未激活之前就有过消费的记录  ##############    
             print('*'*30,'Part2, data with time less than activation day','*'*30) 
             df_part = df.loc[df.purchase_date < df.first_active_month] 
      
             cardid_features_part = pd.DataFrame() 
             cardid_features_part['card_id'] = df_part['card_id'].unique()    
             ht_card_id_part_gp = df_part.groupby('card_id') 
             cardid_features_part['card_id_part_cnt'] = ht_card_id_part_gp['authorized_flag'].count().values 
      
             print('card_id(purchase_amount): sum')  
             for col in tqdm_notebook(['purchase_amount']):  
                 for opt in ['sum','mean']: 
                     cardid_features_part['card_id_part_' +col+ '_' + opt] = ht_card_id_part_gp[col].agg(opt).values 
      
             cardid_features = cardid_features.merge(cardid_features_part, on ='card_id', how='left') 
             cardid_features['card_id_part_purchase_amount_sum_percent'] = cardid_features['card_id_part_purchase_amount_sum'] / (cardid_features['card_id_purchase_amount_sum'] + 0.01) 
      
         cardid_features = reduce_mem_usage(cardid_features) 
          
         new_col_names = [] 
         for col in cardid_features.columns: 
             if col == 'card_id': 
                 new_col_names.append(col) 
             else: 
                 new_col_names.append(prefix + col) 
         cardid_features.columns = new_col_names 
          
         return cardid_features 
     print('auth...') 
     authorized_transactions = historical_transactions.loc[historical_transactions['authorized_flag'] == 1] 
     auth_cardf_tran = get_cardf_tran(authorized_transactions, 3, prefix='auth_cardf_') 
     print('hist...') 
     hist_cardf_tran = get_cardf_tran(historical_transactions, 3, prefix='hist_cardf_') 
     print('new...') 
     reference_days = historical_transactions.groupby('card_id')['purchase_date'].last().to_frame('reference_day') 
     reference_days.reset_index(inplace = True) 
     new_transactions = new_transactions.merge(reference_days, on ='card_id', how='left') 
     new_cardf_tran  = get_cardf_tran(new_transactions, 5, prefix='new_cardf_') 
   ]
  }

  然后,需要进一步考虑最近两个月的用户行为特征: 

   def get_cardf_tran_last2(df_, month = 3, prefix = 'last2_'):  
          
         df = df_.loc[df_.month_lag >= -2].copy() 
         print('*'*30,'Part1, whole data','*'*30) 
         cardid_features = pd.DataFrame() 
         cardid_features['card_id'] = df['card_id'].unique()    
          
         df['month_diff'] = ((datetime.datetime.today() - df['purchase_date']).dt.days)//30 
         df['month_diff'] = df['month_diff'].astype(int) 
         df['month_diff'] += df['month_lag'] 
          
         print( '*' * 30, 'Traditional Features', '*' * 30) 
         ht_card_id_gp = df.groupby('card_id') 
         print(' card id : count') 
         cardid_features['card_id_cnt'] = ht_card_id_gp['authorized_flag'].count().values 
          
         cardid_features['card_id_isau_mean'] = ht_card_id_gp['authorized_flag'].mean().values  
         cardid_features['card_id_isau_sum']  = ht_card_id_gp['authorized_flag'].sum().values 
          
         cardid_features['month_diff_mean']   = ht_card_id_gp['month_diff'].mean().values 
      
         print('card id(city_id,installments,merchant_category_id,.......):nunique, cnt/nunique')  
         for col in tqdm_notebook(['state_id','city_id','installments','merchant_id', 'merchant_category_id','purchase_date_floorday']): 
             cardid_features['card_id_%s_nunique'%col] = ht_card_id_gp[col].nunique().values 
             cardid_features['card_id_cnt_divide_%s_nunique'%col] = cardid_features['card_id_cnt'].values / cardid_features['card_id_%s_nunique'%col].values 
               
         for col in tqdm_notebook(['purchase_amount','purchase_amount_ddgd_98','purchase_amount_wdgd_96','purchase_amount_mdgd_90','purchase_amount_mdgd_80']): #,'purchase_amount_ddgd_98','purchase_amount_ddgd_99','purchase_amount_wdgd_96','purchase_amount_wdgd_97','purchase_amount_mdgd_90','purchase_amount_mdgd_80']): 
             if col =='purchase_amount': 
                 for opt in ['sum','mean','std','median']: 
                     cardid_features['card_id_' +col+ '_' + opt] = ht_card_id_gp[col].agg(opt).values   
             else: 
                 for opt in ['sum']: 
                     cardid_features['card_id_' +col+ '_' + opt] = ht_card_id_gp[col].agg(opt).values  
          
         print( '*' * 30, 'Pivot Features', '*' * 30) 
         print('Count  Pivot') #purchase_month_since_reference_day(可能和month_lag重复),百分比降分,暂时忽略 (dayofweek,merchant_cate,state_id)作用不大 
          
         for pivot_col in tqdm_notebook(['category_1','category_2','category_3','month_lag','subsector_id','weekend']): #'city_id',  
          
             tmp     = df.groupby(['card_id',pivot_col])['merchant_id'].count().to_frame(pivot_col + '_count') 
             tmp.reset_index(inplace =True)   
               
             tmp_pivot = pd.pivot_table(data=tmp,index = 'card_id',columns=pivot_col,values=pivot_col + '_count',fill_value=0) 
             tmp_pivot.columns = [tmp_pivot.columns.names[0] + '_cnt_pivot_'+ str(col) for col in tmp_pivot.columns] 
             tmp_pivot.reset_index(inplace = True) 
             cardid_features = cardid_features.merge(tmp_pivot, on = 'card_id', how='left') 
            
             if  pivot_col!='weekend' and  pivot_col!='installments': 
                 tmp            = df.groupby(['card_id',pivot_col])['purchase_date_floorday'].nunique().to_frame(pivot_col + '_purchase_date_floorday_nunique')  
                 tmp1           = df.groupby(['card_id'])['purchase_date_floorday'].nunique().to_frame('purchase_date_floorday_nunique')  
                 tmp.reset_index(inplace =True)   
                 tmp1.reset_index(inplace =True)    
                 tmp  = tmp.merge(tmp1, on ='card_id', how='left') 
                 tmp[pivot_col + '_day_nunique_pct'] = tmp[pivot_col + '_purchase_date_floorday_nunique'].values / tmp['purchase_date_floorday_nunique'].values 
               
                 tmp_pivot = pd.pivot_table(data=tmp,index = 'card_id',columns=pivot_col,values=pivot_col + '_day_nunique_pct',fill_value=0) 
                 tmp_pivot.columns = [tmp_pivot.columns.names[0] + '_day_nunique_pct_'+ str(col) for col in tmp_pivot.columns] 
                 tmp_pivot.reset_index(inplace = True) 
                 cardid_features = cardid_features.merge(tmp_pivot, on = 'card_id', how='left') 
           
         cardid_features = reduce_mem_usage(cardid_features) 
          
         new_col_names = [] 
         for col in cardid_features.columns: 
             if col == 'card_id': 
                 new_col_names.append(col) 
             else: 
                 new_col_names.append(prefix + col) 
         cardid_features.columns = new_col_names 
          
         return cardid_features   
      
     hist_cardf_tran_last2 = get_cardf_tran_last2(historical_transactions, month = 3, prefix = 'hist_last2_') 
   ]
     以及进一步进行二阶交叉特征衍生:

    def successive_aggregates(df_, prefix = 'levelAB_'): 
         df = df_.copy() 
         cardid_features = pd.DataFrame() 
         cardid_features['card_id'] = df['card_id'].unique()     
           
         level12_nunique = [('month_lag','state_id'),('month_lag','city_id'),('month_lag','subsector_id'),('month_lag','merchant_category_id'),('month_lag','merchant_id'),('month_lag','purchase_date_floorday'),\\ 
                            ('subsector_id','merchant_category_id'),('subsector_id','merchant_id'),('subsector_id','purchase_date_floorday'),('subsector_id','month_lag'),\\ 
                            ('merchant_category_id', 'merchant_id'),('merchant_category_id','purchase_date_floorday'),('merchant_category_id','month_lag'),\\ 
                            ('purchase_date_floorday', 'merchant_id'),('purchase_date_floorday','merchant_category_id'),('purchase_date_floorday','subsector_id')]     
         for col_level1,col_level2 in tqdm_notebook(level12_nunique):   
              
             level1  = df.groupby(['card_id',col_level1])[col_level2].nunique().to_frame(col_level2 + '_nunique') 
             level1.reset_index(inplace =True)   
               
             level2 = level1.groupby('card_id')[col_level2 + '_nunique'].agg(['mean', 'max', 'std']) 
             level2 = pd.DataFrame(level2) 
             level2.columns = [col_level1 + '_' + col_level2 + '_nunique_' + col for col in level2.columns.values] 
             level2.reset_index(inplace = True) 
              
             cardid_features = cardid_features.merge(level2, on='card_id', how='left')  
          
         level12_count = ['month_lag','state_id','city_id','subsector_id','merchant_category_id','merchant_id','purchase_date_floorday'] 
         for col_level in tqdm_notebook(level12_count):  
          
             level1  = df.groupby(['card_id',col_level])['merchant_id'].count().to_frame(col_level + '_count') 
             level1.reset_index(inplace =True)   
               
             level2 = level1.groupby('card_id')[col_level + '_count'].agg(['mean', 'max', 'std']) 
             level2 = pd.DataFrame(level2) 
             level2.columns = [col_level + '_count_' + col for col in level2.columns.values] 
             level2.reset_index(inplace = True) 
              
             cardid_features = cardid_features.merge(level2, on='card_id', how='left')  
          
         level12_meansum = [('month_lag','purchase_amount'),('state_id','purchase_amount'),('city_id','purchase_amount'),('subsector_id','purchase_amount'),\\ 
                            ('merchant_category_id','purchase_amount'),('merchant_id','purchase_amount'),('purchase_date_floorday','purchase_amount')] 
         for col_level1,col_level2 in tqdm_notebook(level12_meansum):  
          
             level1  = df.groupby(['card_id',col_level1])[col_level2].sum().to_frame(col_level2 + '_sum') 
             level1.reset_index(inplace =True)   
               
             level2 = level1.groupby('card_id')[col_level2 + '_sum'].agg(['mean', 'max', 'std']) 
             level2 = pd.DataFrame(level2) 
             level2.columns = [col_level1 + '_' + col_level2 + '_sum_' + col for col in level2.columns.values] 
             level2.reset_index(inplace = True) 
      
             cardid_features = cardid_features.merge(level2, on='card_id', how='left')            
          
         cardid_features = reduce_mem_usage(cardid_features) 
          
         new_col_names = [] 
         for col in cardid_features.columns: 
             if col == 'card_id': 
                 new_col_names.append(col) 
             else: 
                 new_col_names.append(prefix + col) 
         cardid_features.columns = new_col_names 
          
         return cardid_features   
      
     print('hist...') 
     hist_levelAB = successive_aggregates(historical_transactions, prefix = 'hist_levelAB_') 
   ]    
      接下来,将上述衍生特征进行合并: 

 print('#_____基础统计特征') 
     train = pd.merge(train, auth_base_stat, on='card_id', how='left') 
     test  = pd.merge(test,  auth_base_stat, on='card_id', how='left') 
     train = pd.merge(train, hist_base_stat, on='card_id', how='left') 
     test  = pd.merge(test,  hist_base_stat, on='card_id', how='left') 
     train = pd.merge(train, new_base_stat , on='card_id', how='left') 
     test  = pd.merge(test,  new_base_stat , on='card_id', how='left') 
     print(train.shape) 
     print(test.shape) 
     print('#_____全局cardid特征') 
     train = pd.merge(train, auth_cardf_tran, on='card_id', how='left') 
     test  = pd.merge(test,  auth_cardf_tran, on='card_id', how='left') 
     train = pd.merge(train, hist_cardf_tran, on='card_id', how='left') 
     test  = pd.merge(test,  hist_cardf_tran, on='card_id', how='left') 
     train = pd.merge(train, new_cardf_tran , on='card_id', how='left') 
     test  = pd.merge(test,  new_cardf_tran , on='card_id', how='left') 
     print(train.shape) 
     print(test.shape) 
     print('#_____最近两月cardid特征') 
     train = pd.merge(train, hist_cardf_tran_last2, on='card_id', how='left') 
     test  = pd.merge(test,  hist_cardf_tran_last2, on='card_id', how='left') 
     print(train.shape) 
     print(test.shape) 
     print('#_____补充二阶特征') 
     train = pd.merge(train, hist_levelAB, on='card_id', how='left') 
     test  = pd.merge(test,  hist_levelAB, on='card_id', how='left') 
     print(train.shape) 
     print(test.shape) 
   ]

并在此基础上补充部分简单四折运算后的衍生特征: 

    train['outliers'] = 0 
     train.loc[train['target'] < -30, 'outliers'] = 1 
     train['outliers'].value_counts() 
     for f in ['feature_1','feature_2','feature_3']: 
         colname = f+'_outliers_mean' 
         order_label = train.groupby([f])['outliers'].mean() 
         for df in [train, test]: 
             df[colname] = df[f].map(order_label) 
      
     for df in [train, test]: 
          
         df['days_feature1'] = df['elapsed_time'] * df['feature_1'] 
         df['days_feature2'] = df['elapsed_time'] * df['feature_2'] 
         df['days_feature3'] = df['elapsed_time'] * df['feature_3'] 
      
         df['days_feature1_ratio'] = df['feature_1'] / df['elapsed_time'] 
         df['days_feature2_ratio'] = df['feature_2'] / df['elapsed_time'] 
         df['days_feature3_ratio'] = df['feature_3'] / df['elapsed_time'] 
      
         df['feature_sum'] = df['feature_1'] + df['feature_2'] + df['feature_3'] 
         df['feature_mean'] = df['feature_sum']/3 
         df['feature_max'] = df[['feature_1', 'feature_2', 'feature_3']].max(axis=1) 
         df['feature_min'] = df[['feature_1', 'feature_2', 'feature_3']].min(axis=1) 
         df['feature_var'] = df[['feature_1', 'feature_2', 'feature_3']].std(axis=1) 
          
         df['card_id_total'] = df['hist_card_id_size']+df['new_card_id_size'] 
         df['card_id_cnt_total'] = df['hist_card_id_count']+df['new_card_id_count'] 
         df['card_id_cnt_ratio'] = df['new_card_id_count']/df['hist_card_id_count'] 
         df['purchase_amount_total'] = df['hist_cardf_card_id_purchase_amount_sum']+df['new_cardf_card_id_purchase_amount_sum'] 
         df['purchase_amount_ratio'] = df['new_cardf_card_id_purchase_amount_sum']/df['hist_cardf_card_id_purchase_amount_sum'] 
         df['month_diff_ratio'] = df['new_cardf_month_diff_mean']/df['hist_cardf_month_diff_mean'] 
         df['installments_total'] = df['new_cardf_card_id_installments_sum']+df['auth_cardf_card_id_installments_sum'] 
         df['installments_ratio'] = df['new_cardf_card_id_installments_sum']/df['auth_cardf_card_id_installments_sum'] 
         df['price_total'] = df['purchase_amount_total']/df['installments_total'] 
         df['new_CLV'] = df['new_card_id_count'] * df['new_cardf_card_id_purchase_amount_sum'] / df['new_cardf_month_diff_mean'] 
         df['hist_CLV'] = df['hist_card_id_count'] * df['hist_cardf_card_id_purchase_amount_sum'] / df['hist_cardf_month_diff_mean'] 
         df['CLV_ratio'] = df['new_CLV'] / df['hist_CLV'] 

3.特征筛选 

在创建完全部特征后即可进行特征筛选了。此处我们考虑手动进行特征筛选,排除部分过于稀疏的特征后即可将数据保存在本地: 

    del_cols = [] 
     for col in train.columns: 
         if 'subsector_id_cnt_' in col and 'new_cardf':  
             del_cols.append(col) 
     del_cols1 = [] 
     for col in train.columns: 
         if 'subsector_id_cnt_' in col and 'hist_last2_' in col: 
             del_cols1.append(col) 
     del_cols2 = [] 
     for col in train.columns: 
         if 'subsector_id_cnt_' in col and 'auth_cardf' in col: 
             del_cols2.append(col) 
     del_cols3 = [] 
     for col in train.columns: 
         if 'merchant_category_id_month_lag_nunique_' in col and '_pivot_supp' in col: 
             del_cols3.append(col) 
         if 'city_id' in col and '_pivot_supp' in col: 
             del_cols3.append(col) 
         if 'month_diff' in col and 'hist_last2_' in col: 
             del_cols3.append(col) 
         if 'month_diff_std' in col or 'month_diff_gap' in col: 
             del_cols3.append(col)  
     fea_cols = [col for col in train.columns if train[col].dtypes!='object' and train[col].dtypes != '<M8[ns]' and col!='target' not in col and col!='min_num'\\ 
                 and col not in del_cols and col not in del_cols1 and col not in del_cols2 and col!='target1' and col!='card_id_cnt_ht_pivot_supp'  and col not in del_cols3]    
     print('删除前:',train.shape[1]) 
     print('删除后:',len(fea_cols)) 
      
     train = train[fea_cols+['target']] 
     fea_cols.remove('outliers') 
     test = test[fea_cols] 
      
     train.to_csv('./data/all_train_features.csv',index=False) 
     test.to_csv('./data/all_test_features.csv',index=False) 

  实际执行过程中,可以按照如下方式进行读取: 

     ## load all features 
     train = pd.read_csv('./data/all_train_features.csv') 
     test  = pd.read_csv('./data/all_test_features.csv') 
      
     inf_cols = ['new_cardf_card_id_cnt_divide_installments_nunique', 'hist_last2_card_id_cnt_divide_installments_nunique'] 
     train[inf_cols] = train[inf_cols].replace(np.inf, train[inf_cols].replace(np.inf, -99).max().max()) 
     ntrain[inf_cols] = ntrain[inf_cols].replace(np.inf, ntrain[inf_cols].replace(np.inf, -99).max().max()) 
     test[inf_cols] = test[inf_cols].replace(np.inf, test[inf_cols].replace(np.inf, -99).max().max()) 
      
     # ## load sparse 
     # train_tags = sparse.load_npz('train_tags.npz') 
     # test_tags  = sparse.load_npz('test_tags.npz') 
      
     ## 获取非异常值的index 
     normal_index = train[train['outliers']==0].index.tolist() 
     ## without outliers 
     ntrain = train[train['outliers'] == 0] 
      
     target        = train['target'].values 
     ntarget       = ntrain['target'].values 
     target_binary = train['outliers'].values 
     ### 
     y_train        = target 
     y_ntrain       = ntarget 
     y_train_binary = target_binary 
      
     print('train:',train.shape) 
     print('ntrain:',ntrain.shape) 
   ]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/711734.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SAP BOM项目类别N非库存项目简介

在BOM的项目类别中用的最多的就是L类型的库存管理,还有T类型的文本类型,但是在实际业务中也会存在物料不做库存管理,但是物料需要进行成本的管控,进入对应的工单成本中,比如在电子行业中需要烧录的正版软件,或者是电脑制造行业中需要预装的正版的Windows系统,购买的软件…

sql优化之利用聚簇索引减少回表次数:limit 100000,10

1. 问题描述 产品&#xff1a;我要对订单列表页做一个分页功能&#xff0c;每页10条数据&#xff0c;商家可以根据金额过滤订单 技术&#xff1a;好的&#xff0c;我写一个sql实现分页&#xff0c;x表示偏移页数&#xff0c;自测limit 10,10耗时200ms&#xff1a; SELECT * …

程序员基本功之git的使用

阿里网盘-资料链接 文章目录 git分布式版本控制工具git的基本概念开发过程中的问题常见的版本控制工具git分布式版本控制工具特点git系统所定制的若干目标git的工作流程图 GIT的安装和常用命令**创建本地git仓库步骤****本地git仓库的使用**git log详解 git分布式版本控制工具…

java:spring actuator扩展原有info endpoint的功能

# 项目代码资源&#xff1a; 可能还在审核中&#xff0c;请等待。。。 https://download.csdn.net/download/chenhz2284/89437506 # 项目代码 【pom.xml】 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId&…

ssm162基于SSM的药房药品采购集中管理系统的设计与实现+vue

药房药品采购集中管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对药房药品采购信息管理混乱&…

MacOS安装redis

文章目录 前言一、介绍二、下载三、安装四、启动五、配置六、Redis 可视化工具下载七、配置详解八、常用命令总结 前言 Redis因其高性能和低延迟而成为现代应用程序的理想选择&#xff0c;尤其适合需要快速读写操作的场景。随着技术的不断发展&#xff0c;Redis继续在性能、功…

学习笔记——网络管理与运维——SNMP(SNMP版本)

二、SNMP版本 1、SNMP版本 SNMP共有三个版本&#xff1a;SNMPv1、SNMPv2c和SNMPv3。 (1)SNMPv1 1990年5月&#xff0c;RFC1157定义了SNMP的第一个版本SNMPv1。RFC1157提供了一种监口控和管理计算机网络的系统方法。SNMPv1基于团体名认证&#xff0c;安全性较差&#xff0c;…

5.数据仓库与数据挖掘期末复习

ETL的含义Extract 、 Transformation、Load。ODS的全称Operational Data Store。 DW全称 Data WarehourseDM全称是Data Mart数据仓库数据抽取时所用到技术是增量、全量、定时、调度STAGE层作用是提供业务系统数据文件的临时存储ODS层作用ods提供业务系统细节数据长期沉淀MID层…

系统架构师考点--计算机硬件

大家好。今天我总结一下计算机硬件的一些考点。 一、中央处理单元&#xff08;CPU&#xff09; 我们知道&#xff0c;计算机的基本硬件系统由运算器、控制器、存储器、输入设备和输出设备5大部件组成。其中运算器、控制器等部件被集成在一起统称为中央处理单元(Central Proce…

挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构

目标 在清晨的代码编辑器上&#xff0c;一场新的挑战即将开始。程序员们肃立于安静的办公室&#xff0c;眼神专注地盯着屏幕&#xff0c;等待着编译器的一声提示。 随着编译器输出的激动人心的"start!"的提示&#xff0c;战斗的序幕拉开了。Bug如潮水般涌来&#x…

C++ bfS

岛屿的最大面积 . - 力扣&#xff08;LeetCode&#xff09; 1.刚开始mn又加了int 2.bfs里符合条件了&#xff0c;不push&#xff0c;&#xff0c;&#xff0c;在写什么几把 class Solution { public:int dx[4] {0, 0, 1, -1};int dy[4] {1, -1, 0, 0};bool vis[50][50];int…

Centos7安装jdk8或11以及切换方案

目录 jdk安装 安装OpenJDK11 安装OpenJDK8 配置默认的 Java 版本 验证 全局环境变量&#xff08;选配&#xff09; 个人版&#xff08;自己可以用&#xff0c;公司不建议&#xff09; 公司版本 /etc/profile 和 ~/.bash_profile 区别 前言-与正文无关 生活远不止眼前的苦…

解决 kali 中使用 vulhub 拉取不到镜像问题

由于默认情况下&#xff0c;访问的镜像是国外的&#xff0c;而从 2023 年开始&#xff0c;docker 的镜像网站就一直访问不了&#xff0c;所以我们可以把镜像地址改成国内的阿里云镜像地址。 1、在 cd /etc/docker/目录下创建或修改daemon.json文件 sudo touch daemon.json 2、在…

【ARMv8/ARMv9 硬件加速系列 3 -- SVE 指令语法及编译参数详细介绍】

文章目录 SVE 汇编语法SVE 单通道谓词SVE 测试代码 SVE 软件和库支持SVE 编译参数配置-marcharmv8-alseprofilememtagsve2-aessve2-bitpermcryptosve2sve2-sha3sve2-sm4 SVE 汇编语法 在介绍 SVE 汇编指令语法之前&#xff0c;先介绍下如何判断自己所使用的芯片是否实现了SVE功…

【TypeScript】泛型工具

跟着 小满zs 学 ts&#xff1a;学习TypeScript24&#xff08;TS进阶用法-泛型工具&#xff09;_ts泛型工具-CSDN博客 Partial 所有属性可选的意思Required 所有属性必选的意思Pick 提取部分属性Exclude 排除部分属性emit 排除部分属性并且返回新的类型 Partial 属性变为可选。…

1. C++面向过程

b站黑马程序员C课程学习笔记&#xff0c;图片存在github&#xff0c;可能需要翻墙才可见 一、C简介 1.1 C的产生及其特点 从C语言发展演变而来&#xff0c;解决了C语言中存在的一些问题&#xff0c;并增加了对面向对象程序设计方法的支持 与其他高级语言相比&#xff0c;C语…

【Unity学习笔记】第十八 基于物理引擎的日月地系统简单实现

转载请注明出处: https://blog.csdn.net/weixin_44013533/article/details/139701843 作者&#xff1a;CSDN|Ringleader| 目录 目标数学理论资源准备数据准备代码实现Unity准备效果展示注意事项后记 目标 目标&#xff1a;利用Unity的物理引擎实现 “日地月三体系统” 。 效果…

【Android面试八股文】请描述new一个对象的流程

文章目录 请描述new一个对象的流程JVM创建对象的过程检查加载分配内存内存空间初始化设置对象初始化请描述new一个对象的流程 JVM创建对象的过程 当JVM遇到一条new指令时,它需要完成以下几个步骤: 类加载与检查内存分配 并发安全性内存空间初始化设置对象信息对象初始化下图…

upload-labs第十二关教程

upload-labs第十二关教程 一、源代码分析代码审计 二、绕过分析截断绕过magic_quotes_gpc函数介绍关闭magic_quotes_gpc上传eval.png文件使用burpsuite进行抓包修改放包&#xff0c;查看是否上传成功使用中国蚁剑进行连接 一、源代码分析 代码审计 $is_upload false; $msg …

网络协议,OSI,简单通信,IP和mac地址

认识协议 1.讲故事 2004年&#xff0c;小明因为给他爹打电话&#xff08;座机&#xff09;费用太贵&#xff0c;所以约定一种信号&#xff0c;响一次是报平安&#xff0c;响两次是要钱&#xff0c;响三次才需要接通。 2.概念 协议&#xff1a;是一种约定&#xff0c;这种约…