SaveText.Ru

binning_1
  1. class Simple_Binner(object):
  2.    
  3.     fitted = False
  4.     report_table = None
  5.     data = None
  6.     na_size = None
  7.    
  8.     def __init__(self, n_init, p_treshold):
  9.         self.n_init = n_init #initial number of bins
  10.         self.p_treshold = p_treshold #stopping merging critical p-value
  11.    
  12.     #search for our bins optimal splitting points
  13.     def fit(self, df, feat, target):
  14.         self.feat = feat
  15.         self.target = target
  16.         self.df = df
  17.        
  18.         #preprocessing
  19.         data = df[[feat, target]]
  20.         data.sort_values(feat, inplace=True)
  21.         data.reset_index(drop=True, inplace=True)
  22.         na_size = len(data[data[feat].apply(np.isnan)==True])
  23.         if na_size > 0: #getting rid of n/a, we'll process them later
  24.             data_na = data.iloc[len(data)-len(data[data[feat].apply(np.isnan)==True]):]
  25.             data = data.iloc[:len(data)-len(data[data[feat].apply(np.isnan)==True])]
  26.             data.reset_index(drop=True, inplace=True)
  27.        
  28.         #initializing our report table
  29.         p_df= pd.DataFrame(columns=['bottom_idx', 'top_idx', 'bottom', 'top', 0, 1, 'p-value', 'NA_p-value', 'WoE', 'IV', 'TR'])
  30.         init = range(-1, len(data), int(len(data)/(self.n_init+1)))
  31.        
  32.         #index boundaries
  33.         k = 0
  34.         while True:
  35.             p_df.loc[k, 'bottom_idx'] = init[k] + 1
  36.             p_df.loc[k, 'bottom'] = data.loc[init[k] + 1, feat]
  37.             if k==len(init)-2:
  38.                 p_df.loc[k, 'top_idx'] = len(data)-1
  39.                 p_df.loc[k, 'top'] = data.loc[len(data)-1, feat]
  40.                 break
  41.             else:
  42.                 p_df.loc[k, 'top_idx'] = init[k+1]
  43.                 p_df.loc[k, 'top'] = data.loc[init[k+1], feat]
  44.             k+=1
  45.        
  46.         #good/bad counting
  47.         for j in range(len(p_df)):
  48.             for i in range(2):
  49.                 try:
  50.                     p_df.loc[j, i] = data.loc[p_df.loc[j, 'bottom_idx']:p_df.loc[j, 'top_idx'], target].value_counts()[i]
  51.                 except KeyError:
  52.                     p_df.loc[j, i] = 0
  53.                    
  54.         #fisher_exact p-value merging
  55.         for i in range(len(p_df)-1):
  56.             p_df.loc[i, 'p-value'] = ss.fisher_exact([[p_df.loc[i, 0], p_df.loc[i, 1]], [p_df.loc[i+1, 0], p_df.loc[i+1, 1]]])[1]
  57.  
  58.         while p_df[p_df['p-value']==p_df['p-value'].max()]['p-value'].values[0] > self.p_treshold:
  59.             pmax_idx = p_df[p_df['p-value']==p_df['p-value'].max()]['p-value'].index[0]
  60.             p_df.loc[pmax_idx, 'top_idx'] = p_df.loc[pmax_idx+1, 'top_idx']
  61.             p_df.loc[pmax_idx, 'top'] = data.loc[p_df.loc[pmax_idx+1, 'top_idx'], feat]
  62.             p_df.loc[pmax_idx, 0] = p_df.loc[pmax_idx, 0] + p_df.loc[pmax_idx+1, 0]
  63.             p_df.loc[pmax_idx, 1] = p_df.loc[pmax_idx, 1] + p_df.loc[pmax_idx+1, 1]
  64.             p_df.drop(axis=0, index=pmax_idx+1, inplace=True)
  65.             p_df.reset_index(drop=True, inplace=True)
  66.             p_df['p-value'] = None
  67.             for i in range(len(p_df)-1):
  68.                 p_df.loc[i, 'p-value'] = ss.fisher_exact([[p_df.loc[i, 0], p_df.loc[i, 1]], [p_df.loc[i+1, 0], p_df.loc[i+1, 1]]])[1]
  69.  
  70.         #NA-merging
  71.         if na_size > 0:
  72.             p_df.loc[len(p_df), 'bottom_idx'] = 'N/A'
  73.             p_df.loc[len(p_df)-1, 'top_idx'] = 'N/A'
  74.             for i in range(2):
  75.                 p_df.loc[len(p_df)-1, i] = data_na[target].value_counts()[i]
  76.             for i in range(len(p_df)-1):    
  77.                 p_df.loc[i, 'NA_p-value'] = ss.fisher_exact([[p_df.loc[i, 0], p_df.loc[i, 1]], [p_df.loc[len(p_df)-1, 0], p_df.loc[len(p_df)-1, 1]]])[1]
  78.             if p_df['NA_p-value'].max() > self.p_treshold:
  79.                 p_df.loc[len(p_df)-1, 'NA_p-value'] = p_df[p_df['NA_p-value']==p_df['NA_p-value'].max()]['NA_p-value'].index[0]
  80.  
  81.        
  82.         #Weight of Evidence & Information Value & Target Rate computation
  83.         if np.isnan(p_df.loc[len(p_df)-1, 'NA_p-value']) == True:
  84.             p_df['TR'] = p_df[1]/(p_df[0]+p_df[1])
  85.             for i in range(len(p_df)):
  86.                 p_df.loc[i, 'WoE'] = np.log((p_df.loc[i, 0]/p_df[0].sum())/(p_df.loc[i, 1]/p_df[1].sum()))
  87.             p_df.loc[0, 'IV'] = 0
  88.             for i in range(len(p_df)):
  89.                 p_df.loc[0, 'IV'] += p_df.loc[i, 'WoE']*((p_df.loc[i, 0]/p_df[0].sum()) - (p_df.loc[i, 1]/p_df[1].sum()))
  90.         else:
  91.             for i in range(len(p_df)-1):
  92.                 if i == p_df.loc[len(p_df)-1, 'NA_p-value']:
  93.                     p_df.loc[i, 'TR'] = (p_df.loc[i, 1]+p_df.loc[len(p_df)-1, 1])/(p_df.loc[i, 0]+p_df.loc[i, 1]+p_df.loc[len(p_df)-1, 0]+p_df.loc[len(p_df)-1, 1])
  94.                     p_df.loc[i, 'WoE'] = np.log(((p_df.loc[i, 0]+p_df.loc[len(p_df)-1, 0])/p_df[0].sum())/((p_df.loc[i, 1]+p_df.loc[len(p_df)-1, 1])/p_df[1].sum()))
  95.                 else:
  96.                     p_df.loc[i, 'TR'] = p_df.loc[i, 1]/(p_df.loc[i, 0]+p_df.loc[i, 1])
  97.                     p_df.loc[i, 'WoE'] = np.log((p_df.loc[i, 0]/p_df[0].sum())/(p_df.loc[i, 1]/p_df[1].sum()))
  98.  
  99.             p_df.loc[0, 'IV'] = 0
  100.             for i in range(len(p_df)-1):
  101.                 p_df.loc[0, 'IV'] += p_df.loc[i, 'WoE']*((p_df.loc[i, 0]/p_df[0].sum()) - (p_df.loc[i, 1]/p_df[1].sum()))
  102.        
  103.         #results saving
  104.         self.fitted = True
  105.         self.report_table = p_df
  106.         self.data = data
  107.         self.na_size = na_size
  108.        
  109.         return p_df #completed report table
  110.    
  111.     #printing short report about binning results
  112.     def report(self):
  113.         if not self.fitted:
  114.             print('Not fitted yet')
  115.         else:
  116.             p_df = self.report_table
  117.             if np.isnan(p_df.loc[len(p_df)-1, 'NA_p-value']) == True:
  118.                 for i in range(len(p_df)):
  119.                     if p_df.loc[i, 'bottom_idx'] == 'N/A':
  120.                         print('#%d %s N/A' % ((i+1), self.feat))
  121.                     else:
  122.                         print('#%d %s from ' % ((i+1), self.feat), self.data.loc[p_df.loc[i, 'bottom_idx'], self.feat],
  123.                               ' to ', self.data.loc[p_df.loc[i, 'top_idx'], self.feat])
  124.             else:
  125.                 for i in range(len(p_df)-1):
  126.                     if i == p_df.loc[len(p_df)-1, 'NA_p-value']:
  127.                         print('#%d %s from ' % ((i+1), self.feat), self.data.loc[p_df.loc[i, 'bottom_idx'], self.feat],
  128.                               ' to ', self.data.loc[p_df.loc[i, 'top_idx'], self.feat], ' + N/A')
  129.                     else:
  130.                         print('#%d %s from ' % ((i+1), self.feat), self.data.loc[p_df.loc[i, 'bottom_idx'], self.feat],
  131.                               ' to ', self.data.loc[p_df.loc[i, 'top_idx'], self.feat])
  132.    
  133.     def plot(self):
  134.         pass
  135.    
  136.     def woe_transform(self, df):
  137.         if not self.fitted:
  138.             print('Not fitted yet')
  139.         else:
  140.             p_df = self.report_table
  141.            
  142.             #number of bin defining for the feature value
  143.             def num_of_bin(x):
  144.                 if self.na_size > 0:
  145.                     if np.isnan(p_df.loc[len(p_df)-1, 'NA_p-value']) == True:
  146.                         if x<=p_df.loc[0, 'top']:
  147.                             return 0
  148.                         for i in range(1, len(p_df)-2):
  149.                             if x<=p_df.loc[i, 'top'] and x>p_df.loc[i-1, 'top']:
  150.                                 return i
  151.                         if x > p_df.loc[len(p_df)-3, 'top']:
  152.                             return len(p_df)-2
  153.                         if np.isnan(x):
  154.                             return len(p_df)-1
  155.                     else:
  156.                         if p_df.loc[len(p_df)-1, 'NA_p-value'] == 0:
  157.                             if x<=p_df.loc[0, 'top'] or np.isnan(x):
  158.                                 return 0
  159.                         else:
  160.                             if x<=p_df.loc[0, 'top']:
  161.                                 return 0
  162.                         for i in range(1, len(p_df)-2):
  163.                             if p_df.loc[len(p_df)-1, 'NA_p-value'] == i:
  164.                                 if x<=p_df.loc[i, 'top'] and x>p_df.loc[i-1, 'top'] or np.isnan(x):
  165.                                     return i
  166.                             else:
  167.                                 if x<=p_df.loc[i, 'top'] and x>p_df.loc[i-1, 'top']:
  168.                                     return i
  169.                         if p_df.loc[len(p_df)-1, 'NA_p-value'] == len(p_df)-2:
  170.                             if x > p_df.loc[len(p_df)-2, 'top'] or np.isnan(x):
  171.                                 return len(p_df)-2
  172.                         else:
  173.                             if x > p_df.loc[len(p_df)-2, 'top']:
  174.                                 return len(p_df)-2
  175.                 else:
  176.                     if x<=p_df.loc[0, 'top']:
  177.                         return 0
  178.                     for i in range(1, len(p_df)-1):
  179.                         if x<=p_df.loc[i, 'top'] and x>p_df.loc[i-1, 'top']:
  180.                             return i
  181.                     if x > p_df.loc[len(p_df)-2, 'top']:
  182.                         return len(p_df)-1
  183.            
  184.             if np.isnan(p_df.loc[len(p_df)-1, 'NA_p-value']) == True:
  185.                 woe_list = [p_df.loc[i, 'WoE'] for i in range(len(p_df))]
  186.             else:
  187.                 woe_list = [p_df.loc[i, 'WoE'] for i in range(len(p_df)-1)]
  188.            
  189.             df_clone = df
  190.             df_clone['woe_%s' % self.feat] = df_clone[self.feat].apply(lambda x: woe_list[num_of_bin(x)])
  191.             df_clone.drop(feat, axis=1, inplace=True)
  192.            
  193.             return df_clone  

Share with your friends:

Распечатать