Python數據分箱,計算woe,iv

Python數據分箱,計算woe,iv

來自專欄小鑫的數據分析筆記7 人贊了文章

數據分箱的重要性及優勢:

  1. 離散特徵的增加和減少都很容易,易於模型的快速迭代;
  2. 稀疏向量內積乘法運算速度快,計算結果方便存儲,容易擴展;
  3. 離散化後的特徵對異常數據有很強的魯棒性:比如一個特徵是年齡>30是1,否則0。如果特徵沒有離散化,一個異常數據「年齡300歲」會給模型造成很大的干擾;
  4. 邏輯回歸屬於廣義線性模型,表達能力受限;單變數離散化為N個後,每個變數有單獨的權重,相當於為模型引入了非線性,能夠提升模型表達能力,加大擬合;
  5. 離散化後可以進行特徵交叉,由M+N個變數變為M*N個變數,進一步引入非線性,提升表達能力;
  6. 特徵離散化後,模型會更穩定,比如如果對用戶年齡離散化,20-30作為一個區間,不會因為一個用戶年齡長了一歲就變成一個完全不同的人。當然處於區間相鄰處的樣本會剛好相反,所以怎麼劃分區間是門學問;
  7. 特徵離散化以後,起到了簡化了邏輯回歸模型的作用,降低了模型過擬合的風險。
  8. 可以將缺失作為獨立的一類帶入模型。
  9. 將所有變數變換到相似的尺度上。
  • 無監督分箱:
  1. 等頻分箱:

def bin_frequency(x,y,n=10): # x為待分箱的變數,y為target變數.n為分箱數量 total = y.count() # 計算總樣本數 bad = y.sum() # 計算壞樣本數 good = y.count()-y.sum() # 計算好樣本數 d1 = pd.DataFrame({x:x,y:y,bucket:pd.qcut(x,n)}) # 用pd.cut實現等頻分箱 d2 = d1.groupby(bucket,as_index=True) # 按照分箱結果進行分組聚合 d3 = pd.DataFrame(d2.x.min(),columns=[min_bin]) d3[min_bin] = d2.x.min() # 箱體的左邊界 d3[max_bin] = d2.x.max() # 箱體的右邊界 d3[bad] = d2.y.sum() # 每個箱體中壞樣本的數量 d3[total] = d2.y.count() # 每個箱體的總樣本數 d3[bad_rate] = d3[bad]/d3[total] # 每個箱體中壞樣本所佔總樣本數的比例 d3[badattr] = d3[bad]/bad # 每個箱體中壞樣本所佔壞樣本總數的比例 d3[goodattr] = (d3[total] - d3[bad])/good # 每個箱體中好樣本所佔好樣本總數的比例 d3[woe] = np.log(d3[goodattr]/d3[badattr]) # 計算每個箱體的woe值 iv = ((d3[goodattr]-d3[badattr])*d3[woe]).sum() # 計算變數的iv值 d4 = (d3.sort_values(by=min_bin)).reset_index(drop=True) # 對箱體從大到小進行排序 print(分箱結果:) print(d4) print(IV值為:) print(iv) cut = [] cut.append(float(-inf)) for i in d4.min_bin: cut.append(i) cut.append(float(inf)) woe = list(d4[woe].round(3)) return d4,iv,cut,woe

2. 等距分箱:

def bin_distince(x,y,n=10): # x為待分箱的變數,y為target變數.n為分箱數量 total = y.count() # 計算總樣本數 bad = y.sum() # 計算壞樣本數 good = y.count()-y.sum() # 計算好樣本數 d1 = pd.DataFrame({x:x,y:y,bucket:pd.cut(x,n)}) #利用pd.cut實現等距分箱 d2 = d1.groupby(bucket,as_index=True) # 按照分箱結果進行分組聚合 d3 = pd.DataFrame(d2.x.min(),columns=[min_bin]) d3[min_bin] = d2.x.min() # 箱體的左邊界 d3[max_bin] = d2.x.max() # 箱體的右邊界 d3[bad] = d2.y.sum() # 每個箱體中壞樣本的數量 d3[total] = d2.y.count() # 每個箱體的總樣本數 d3[bad_rate] = d3[bad]/d3[total] # 每個箱體中壞樣本所佔總樣本數的比例 d3[badattr] = d3[bad]/bad # 每個箱體中壞樣本所佔壞樣本總數的比例 d3[goodattr] = (d3[total] - d3[bad])/good # 每個箱體中好樣本所佔好樣本總數的比例 d3[woe] = np.log(d3[goodattr]/d3[badattr]) # 計算每個箱體的woe值 iv = ((d3[goodattr]-d3[badattr])*d3[woe]).sum() # 計算變數的iv值 d4 = (d3.sort_values(by=min_bin)).reset_index(drop=True) # 對箱體從大到小進行排序 print(分箱結果:) print(d4) print(IV值為:) print(iv) cut = [] cut.append(float(-inf)) for i in d4.min_bin: cut.append(i) cut.append(float(inf)) woe = list(d4[woe].round(3)) return d4,iv,cut,woe

3. 自定義分箱:

def bin_self(x,y,cut): # x為待分箱的變數,y為target變數,cut為自定義的分箱(list) total = y.count() # 計算總樣本數 bad = y.sum() # 計算壞樣本數 good = y.count()-y.sum() # 計算好樣本數 d1 = pd.DataFrame({x:x,y:y,bucket:pd.cut(x,cut)}) d2 = d1.groupby(bucket,as_index=True) # 按照分箱結果進行分組聚合 d3 = pd.DataFrame(d2.x.min(),columns=[min_bin]) d3[min_bin] = d2.x.min() # 箱體的左邊界 d3[max_bin] = d2.x.max() # 箱體的右邊界 d3[bad] = d2.y.sum() # 每個箱體中壞樣本的數量 d3[total] = d2.y.count() # 每個箱體的總樣本數 d3[bad_rate] = d3[bad]/d3[total] # 每個箱體中壞樣本所佔總樣本數的比例 d3[badattr] = d3[bad]/bad # 每個箱體中壞樣本所佔壞樣本總數的比例 d3[goodattr] = (d3[total] - d3[bad])/good # 每個箱體中好樣本所佔好樣本總數的比例 d3[woe] = np.log(d3[goodattr]/d3[badattr]) # 計算每個箱體的woe值 iv = ((d3[goodattr]-d3[badattr])*d3[woe]).sum() # 計算變數的iv值 d4 = (d3.sort_values(by=min_bin)).reset_index(drop=True) # 對箱體從大到小進行排序 print(分箱結果:) print(d4) print(IV值為:) print(iv) woe = list(d4[woe].round(3)) return d4,iv,woe

  • 無監督分箱
  1. 卡方分箱

def chi_bins(df,col,target,confidence=3.841,bins=20): # 設定自由度為1,卡方閾值為3.841,最大分箱數20 total = df.target.count() #計算總樣本數 bad = df.target.sum() # 計算壞樣本總數 good = total - bad # 計算好樣本總數 total_bin = df.groupby([col])[target].count() # 計算每個箱體總樣本數 total_bin_table = pd.DataFrame({total:total_bin}) #創建一個數據框保存結果 bad_bin = df.groupby([col])[target].sum() # 計算每個箱體的壞樣本數 bad_bin_table = pd.DataFrame({bad:bad_bin}) #創建一個數據框保存結果 regroup = pd.merge(total_bin_table,bad_bin_table,left_index=True,right_index=True,how=inner) #組合total_bin 和 bad_bin regroup.reset_index(inplace=True) regroup[good] = regroup[total]-regroup[bad] #計算每個箱體的好樣本數 regroup = regroup.drop([total],axis=1) #刪除total np_regroup = np.array(regroup) # 將regroup轉為numpy # 處理連續沒有正樣本和負樣本的區間,進行合併,以免卡方報錯 i = 0 while (i <= np_regroup.shape[0] - 2): if ((np_regroup[i, 1] == 0 and np_regroup[i + 1, 1] == 0) or ( np_regroup[i, 2] == 0 and np_regroup[i + 1, 2] == 0)): np_regroup[i, 1] = np_regroup[i, 1] + np_regroup[i + 1, 1] # 正樣本 np_regroup[i, 2] = np_regroup[i, 2] + np_regroup[i + 1, 2] # 負樣本 np_regroup[i, 0] = np_regroup[i + 1, 0] np_regroup = np.delete(np_regroup, i + 1, 0) i = i - 1 i = i + 1 # 對相鄰兩個區間的值進行卡方計算 chi_table = np.array([]) # 創建一個數組保存相鄰兩個區間的卡方值 for i in np.arange(np_regroup.shape[0]-1): chi = ((np_regroup[i,1]*np_regroup[i+1,2]-np_regroup[i,2]*np_regroup[i+1,1])**2* (np_regroup[i,1]+np_regroup[i,2]+np_regroup[i+1,1]+np_regroup[i+1,2]))/ ((np_regroup[i,1]+np_regroup[i,2])*(np_regroup[i+1,1]+np_regroup[i+1,2])* (np_regroup[i,1]+np_regroup[i+1,1])*(np_regroup[i,2]+np_regroup[i+1,2])) chi_table = np.append(chi_table,chi) # 將卡方值最小的兩個區間進行合併 while(1): #除非設置break,否則會一直循環下去 if(len(chi_table)<=(bins-1) or min(chi_table)>=confidence): break # 當chi_table的值個數小於等於(箱體數-1) 或 最小的卡方值大於等於卡方閾值時,循環停止 chi_min_index = np.where(chi_table = min(chi_min_index))[0] # 找出卡方最小值的索引 np_regroup[chi_min_index,1] = np_regroup[chi_min_index,1] + np_regroup[chi_min_index+1,1] np_regroup[chi_min_index,2] = np_regroup[chi_min_index,2] + np_regroup[chi_min_index+1,2] np_regroup[chi_min_index,0] = np_regroup[chi_min_index+1,0] np_regroup = np.delete(np_regroup,chi_min_index+1,axis=0) if (chi_min_index == np_regroup.shape[0]-1): # 當卡方最小值是最後兩個區間時,計算當前區間和前一個區間的卡方值並替換 chi_table[chi_min_index-1] = ((np_regroup[chi_min_index-1,1]*np_regroup[chi_min_index,2]-np_regroup[chi_min_index-1,2] *np_regroup[chi_min_index,1])**2*(np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index-1,2] +np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]))/((np_regroup[chi_min_index-1,1]+ np_regroup[chi_min_index-1,2])*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2])* (np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index,1])*(np_regroup[chi_min_index-1,2]+ np_regroup[chi_min_index,2])) chi_table = np.delete(chi_table,chi_min_index,axis=0) #刪除替換前的卡方值 else: # 計算合併後當前區間和前一個區間的卡方值並替換 chi_table[chi_min_index-1] = ((np_regroup[chi_min_index-1,1]*np_regroup[chi_min_index,2]-np_regroup[chi_min_index-1,2] *np_regroup[chi_min_index,1])**2*(np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index-1,2] +np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2]))/((np_regroup[chi_min_index-1,1]+ np_regroup[chi_min_index-1,2])*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2])* (np_regroup[chi_min_index-1,1]+np_regroup[chi_min_index,1])*(np_regroup[chi_min_index-1,2]+ np_regroup[chi_min_index,2])) # 計算合併後當前區間和後一個區間的卡方值並替換 chi_table[chi_min_index] = ((np_regroup[chi_min_index,1]*np_regroup[chi_min_index+1,2]-np_regroup[chi_min_index,2] *np_regroup[chi_min_index+1,1])**2*(np_regroup[chi_min_index,1]+np_regroup[chi_min_index,2] +np_regroup[chi_min_index+1,1]+np_regroup[chi_min_index+1,2]))/((np_regroup[chi_min_index,1]+ np_regroup[chi_min_index,2])*(np_regroup[chi_min_index+1,1]+np_regroup[chi_min_index+1,2])* (np_regroup[chi_min_index,1]+np_regroup[chi_min_index+1,1])*(np_regroup[chi_min_index,2]+ np_regroup[chi_min_index+1,2])) chi_table = np.delete(chi_table,chi_min_index+1,axis=0) #刪除替換前的卡方值 # 將結果保存為一個數據框 result_data = pd.DataFrame() result_data[col] = [col]*np_regroup.shape[0] #結果第一列為變數名 list_temp=[] # 創建一個空白的分組列 for i in np.arange(np_regroup.shape[0]): if i==0: # 當為第一個箱體時 x= 0 + , + str(np_regroup[i,0]) elif i == np_regroup.shape[0]-1: # 當為最後一個箱體時 x = str(np_regroup[i-1,0]) + + else: x = str(np_regroup[i-1,0]) + , + str(np_regroup[i,0]) list_temp.append(x) result_data[bin] = list_temp result_data[bad] = np_regroup[:,1] result_data[good] = np_regroup[:,2] result_data[bad_rate] = result_data[bad]/total #計算每個箱體壞樣本所佔總樣本比例 result_data[badattr] = result_data[bad]/bad #計算每個箱體壞樣本所佔壞樣本總數的比例 result_data[goodattr] = result_data[good]/good #計算每個箱體好樣本所佔好樣本總數的比例 result_data[woe] = np.log(result_data[goodattr]/result_data[badattr]) #計算每個箱體的woe值 iv = ((result_data[goodattr]-result_data[badattr])*result_data[woe]).sum() #計算每個變數的iv值 print(分箱結果:) print(result_data) print(IV值為:) print(iv) return result_data,iv

推薦閱讀:

大數據計算服務MaxCompute 5月新功能發布一覽
主成分分析(PCA)原理詳解
閑話國內大數據發展簡史&產業化落地
MIT開發的新設備幫助數據科學家用「筆記本」分析大規模圖形數據
給妹子講python-S02E19GroupBy實現分割、應用和組合

TAG:Python | 數據分析 | 數據挖掘 |