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) 
   ]

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI赋能软件测试

AI赋能软件测试 AI赋能软件测试软件测试分类软件质量模型:用来衡量软件质量的维度AI赋能软件测试 随着AI时代的到来,如何轻松掌握软件测试新趋势,将AI技术应用于软件测试行业,提高测试速度与测试效率~~ 传智星云AI助手:https://nebula.itcast.cn tips:各种AI工具应有尽有…

LeetCode | 66.加一

这道题有多个思路&#xff0c;可以依次取数组的每一位&#xff0c;乘10后加下一位&#xff0c;直到最后一位&#xff0c;就得到我们数组所表示的数字&#xff0c;然后加一&#xff0c;然后把新得到的数字再转化为对应的数组&#xff0c;我的做法是直接取数组的最后一位&#xf…

人工智能GPU互联技术分析,芯片巨头UALink向英伟达NVLink开战

芯片巨头组团&#xff0c;向英伟达NVLink开战 八大科技巨头——AMD、博通、思科、Google、惠普企业、英特尔、Meta及微软——联合推出UALink&#xff08;Ultra Accelerator Link&#xff09;技术&#xff0c;为人工智能数据中心网络设定全新互联标准。此举旨在打破Nvidia的市场…

Python 小市值股票模型代码及回测分析

目录 一、模型介绍 二、代码详解 2.1 初始化函数 2.2 股票筛选过滤函数 2.3 止损函数 2.4 开盘时运行函数 2.5 调仓函数 三、回测结果分析 3.1 收益净值图与概述 3.2 模型收益概览 3.3 年度收益图 3.4 月度收益的时间序列 3.5 月度收益热力图 3.6 月度收益频次分…

java之IO流和集合框架的笔记

1 File类的使用 1.1 概述 File类及本章下的各种流&#xff0c;都定义在java.io包下。 一个File对象代表硬盘或网络中可能存在的一个文件或者文件目录&#xff08;俗称文件夹&#xff09;&#xff0c;与平台无关。&#xff08;体会万事万物皆对象&#xff09; File 能新建、删…

Java---认识异常

欢迎大家来观看本博课------Java------认识异常。1.异常的概念和体系结构 1.异常的概念和体系结构 1.1 异常的概念 在Java中&#xff0c;在程序执行过程中发生的不正常行为称为异常。如在之前我们经常遇到的算数异常&#xff08;ArithmeticException&#xff09;、数组越界…

GDB:从零开始入门GDB

目录 1.前言 2.开启项目报错 3.GDB的进入和退出 4.GDB调试中查看代码和切换文件 5.GDB调试中程序的启动和main函数传参 6.GDB中断点相关的操作 7.GDB中的调试输出指令 8.GDB中自动输出值指令 9.GDB中的调试指令 前言 在日常开发中&#xff0c;调试是我们必不可少的技能。在专业…

408数据结构-图的遍历 自学知识点整理

前置知识&#xff1a;图的存储与基本操作 图的遍历是指从图的某一顶点出发&#xff0c;按照某种搜索方法沿着图中的边对图中的所有顶点访问一次&#xff0c;且仅访问一次。因为树是一种特殊的图&#xff0c;所以树的遍历实际上也可以视为一种特殊的图的遍历。图的遍历算法是求解…

利用鱼骨图进行项目问题复盘与改进

一、引言 在项目管理中&#xff0c;问题复盘是一个至关重要的环节。它不仅能帮助我们识别项目执行过程中出现的问题&#xff0c;还能促使我们深入探究问题的根本原因&#xff0c;从而采取有效的改进措施。在这个过程中&#xff0c;鱼骨图作为一种强大的工具&#xff0c;为我们…

MEMS:Lecture 16 Gyros

陀螺仪原理 A classic spinning gyroscope measures the rotation rate by utilizing the conservation of angular momentum. 经典旋转陀螺仪通过利用角动量守恒来测量旋转速率。 Coriolis Effect and Coriolis Force 科里奥利效应是一种出现在旋转参考系中的现象。它描述了…

Internet Download Manager ( 极速下载器 ) 序列号注册码 IDM下载器注册机中文激活破解版

IDM下载器(Internet Download Manager)是一款专业的下载管理软件&#xff0c;它通过多线程技术和智能文件分段技术&#xff0c;有效提升下载速度&#xff0c;并支持断点续传&#xff0c;还具有计划下载功能&#xff0c;用户可以设置特定的下载时间&#xff0c;非常适合需要在特…

代码随想录算法训练营刷题复习1 :动态规划背包问题 01背包+完全背包

动态规划刷题复习 一、01背包 416. 分割等和子集1049. 最后一块石头的重量 II494. 目标和474. 一和零 416. 分割等和子集 class Solution { public:bool canPartition(vector<int>& nums) {int sum0;for(int i0;i<nums.size();i) {sumnums[i];}if(sum%2!0)retu…

IP地址、子网掩码、网段、网关

前面相同就是在同一个网段 如果子网掩码和网络号相与的结果是一样的&#xff0c;那么他们就在同一个子网 IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?_掩码248可以用几个ip-CSDN博客

谷歌上架,APP被移除了,没封号,换个包名还能重新提审上架?

对于在Google Play上架应用的开发者来说&#xff0c;尤其是那些矩阵式上架马甲包的开发者&#xff0c;可能已经遭遇过无数次应用被暂停或移除的情况了。通常这种情况下&#xff0c;账号也随之会被封&#xff0c;且大多数开发者认为&#xff0c;没有立马收到封号邮件的话&#x…

基于scikit-learn的机器学习分类任务实践——集成学习

一、传统机器学习分类流程与经典思想算法简述 传统机器学习是指&#xff0c;利用线性代数、数理统计与优化算法等数学方式从设计获取的数据集中构建预测学习器&#xff0c;进而对未知数据分类或回归。其主要流程大致可分为七个部分&#xff0c;依次为设计获取数据特征集&#x…

【C++】stack、queue和deque的使用

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读 一、stack 1. stack介绍 2. stack使用 二、queue 1. queue介绍 2. queue使用 三、deque 1. deque介绍 2. deque的…

【Pandas驯化-01】一文介绍 Pandas中的Dataframe和Series 区别

【Pandas】驯化-01一文介绍 Pandas中的Dataframe和Series 区别 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 相关内容文档获取 微信公众…

第九届星华杯网络邀请赛

T1喵星人的身高 T2犇犇碑 T3嘤嘤词典 T4三角区间和

简单Mesh多线程合并,使用什么库性能更高

1&#xff09;简单Mesh多线程合并&#xff0c;使用什么库性能更高 2&#xff09;Unity Semaphore.WaitForSignal耗时高 3&#xff09;VS编辑的C#代码注释的中文部分乱码 4&#xff09;变量IntPtr m_cachePtr切换线程后变空 这是第389篇UWA技术知识分享的推送&#xff0c;精选了…

HBase中Master初始化错误~

ERROR&#xff1a;org.apache.hadoop.hbase.PleaseHoldException:Master is initializing 1、停止HBase运行 2、启动zookeeper中的zkCli.sh服务 ./zookeeper/bin/zkCli.sh 3、执行完毕显示以下结果,删除habse文件夹 4、重新启动HBase即可。