關於特征篩選中的IV值


1 IV的用途

IV的全稱是Information Value,中文意思是信息價值,或者信息量。

我們在用邏輯回歸、決策樹等模型方法構建分類模型時,經常需要對自變量進行篩選。比如我們有200個候選自變量,通常情況下,不會直接把200個變量直接放到模型中去進行擬合訓練,而是會用一些方法,從這200個自變量中挑選一些出來,放進模型,形成入模變量列表。那么我們怎么去挑選入模變量呢?

挑選入模變量過程是個比較復雜的過程,需要考慮的因素很多,比如:變量的預測能力,變量之間的相關性,變量的簡單性(容易生成和使用),變量的強壯性(不容易被繞過),變量在業務上的可解釋性(被挑戰時可以解釋的通)等等。但是,其中最主要和最直接的衡量標准是變量的預測能力。

“變量的預測能力”這個說法很籠統,很主觀,非量化,在篩選變量的時候我們總不能說:“我覺得這個變量預測能力很強,所以他要進入模型”吧?我們需要一些具體的量化指標來衡量每自變量的預測能力,並根據這些量化指標的大小,來確定哪些變量進入模型。IV就是這樣一種指標,他可以用來衡量自變量的預測能力。類似的指標還有信息增益、基尼系數等等。

2 對IV的直觀理解

從直觀邏輯上大體可以這樣理解“用IV去衡量變量預測能力”這件事情:我們假設在一個分類問題中,目標變量的類別有兩類:Y1,Y2。對於一個待預測的個體A,要判斷A屬於Y1還是Y2,我們是需要一定的信息的,假設這個信息總量是I,而這些所需要的信息,就蘊含在所有的自變量C1,C2,C3,……,Cn中,那么,對於其中的一個變量Ci來說,其蘊含的信息越多,那么它對於判斷A屬於Y1還是Y2的貢獻就越大,Ci的信息價值就越大,Ci的IV就越大,它就越應該進入到入模變量列表中。

3 IV的計算

前面我們從感性角度和邏輯層面對IV進行了解釋和描述,那么回到數學層面,對於一個待評估變量,他的IV值究竟如何計算呢?為了介紹IV的計算方法,我們首先需要認識和理解另一個概念——WOE,因為IV的計算是以WOE為基礎的。

3.1 WOE

WOE的全稱是“Weight of Evidence”,即證據權重。WOE是對原始自變量的一種編碼形式。

要對一個變量進行WOE編碼,需要首先把這個變量進行分組處理(也叫離散化、分箱等等,說的都是一個意思)。分組后,對於第i組,WOE的計算公式如下:

WOE (weight of Evidence) 字面意思證據權重,對分箱后的每組進行計算。假設 good 為好客戶(未 違約),bad 為壞客戶(違約)。

                                                                

#goodi 表示每組中標簽為 good 的數量,#goodT 為 good 的總數量,bad 同理。

3.2 IV

IV (information value) 衡量的是某一個變量的信息量,公式如下:

                                                                 

N 為分組的組數,IV 可用來表示一個變量的預測能力。

 IV                      預測能力
<0.03 無預測能力
0.03-0.09
0.1-0.29
0.3-0.49
>=0.5 極高且可疑

可根據 IV 值來調整分箱結構並重新計算 WOE 和 IV。但並不完全是 IV 值越大越好,還需要考慮 分組數量合適,並且當 IV 值大於 0.5 時,我們需要對這個特征打個疑問,因為它過於太好而顯得不夠 真實。通常我們會選擇 IV 值在 0.1~0.5 這個范圍的特征。多數時候分箱都需要手動做一些調整。

 

python代碼如下:

import numpy as np
import pandas as pd
import scipy
import scipy.stats as st

def auto_bin(DF, X, Y, n=5, iv=True, detail=False,q=20):
    """
    自動最優分箱函數,基於卡方檢驗的分箱

    參數:
    DF: DataFrame 數據框
    X: 需要分箱的列名
    Y: 分箱數據對應的標簽 Y 列名
    n: 保留分箱個數
    iv: 是否輸出執行過程中的 IV 值
    detail: 是否輸出合並的細節信息
    q: 初始分箱的個數

    區間為前開后閉 (]

    返回值:

    """


    # DF = model_data
    # X = "age"
    # Y = "SeriousDlqin2yrs"

    DF = DF[[X,Y]].copy()

    # 按照等頻對需要分箱的列進行分箱
    DF["qcut"],bins = pd.qcut(DF[X], retbins=True, q=q, duplicates="drop")
    # 統計每個分段 0,1的數量
    coount_y0 = DF.loc[DF[Y]==0].groupby(by="qcut")[Y].count()
    coount_y1 = DF.loc[DF[Y]==1].groupby(by="qcut")[Y].count()
    # num_bins值分別為每個區間的上界,下界,0的頻次,1的頻次
    num_bins = [*zip(bins,bins[1:],coount_y0,coount_y1)]

    # 定義計算 woe 的函數
    def get_woe(num_bins):
        # 通過 num_bins 數據計算 woe
        columns = ["min","max","count_0","count_1"]
        df = pd.DataFrame(num_bins,columns=columns)

        df["total"] = df.count_0 + df.count_1
        df["percentage"] = df.total / df.total.sum()
        df["bad_rate"] = df.count_1 / df.total
        df["woe"] = np.log((df.count_0/df.count_0.sum()) /
                           (df.count_1/df.count_1.sum()))
        return df

    # 創建計算 IV 值函數
    def get_iv(bins_df):
        rate = ((bins_df.count_0/bins_df.count_0.sum()) -
                (bins_df.count_1/bins_df.count_1.sum()))
        IV = np.sum(rate * bins_df.woe)
        return IV


    # 確保每個分組的數據都包含有 0 和 1
    for i in range(20): # 初始分組不會超過20
        # 如果是第一個組沒有 0 或 1,向后合並
        if 0 in num_bins[0][2:]:
            num_bins[0:2] = [(
                num_bins[0][0],
                num_bins[1][1],
                num_bins[0][2]+num_bins[1][2],
                num_bins[0][3]+num_bins[1][3])]
            continue

        # 其他組出現沒有 0 或 1,向前合並
        for i in range(len(num_bins)):
            if 0 in num_bins[i][2:]:
                num_bins[i-1:i+1] = [(
                    num_bins[i-1][0],
                    num_bins[i][1],
                    num_bins[i-1][2]+num_bins[i][2],
                    num_bins[i-1][3]+num_bins[i][3])]
                break
        # 循環結束都沒有出現則提前結束外圈循環
        else:
            break

    # 重復執行循環至分箱保留 n 組:
    while len(num_bins) > n:
        # 獲取 num_bins 兩兩之間的卡方檢驗的置信度(或卡方值)
        pvs = []
        for i in range(len(num_bins)-1):
            x1 = num_bins[i][2:]
            x2 = num_bins[i+1][2:]
            # 0 返回 chi2 值,1 返回 p 值。
            pv = st.chi2_contingency([x1,x2])[1]
            # chi2 = scipy.stats.chi2_contingency([x1,x2])[0]
            pvs.append(pv)

        # 通過 p 值進行處理。合並 p 值最大的兩組
        i = pvs.index(max(pvs))
        num_bins[i:i+2] = [(
            num_bins[i][0],
            num_bins[i+1][1],
            num_bins[i][2]+num_bins[i+1][2],
            num_bins[i][3]+num_bins[i+1][3])]

        # 打印合並后的分箱信息
        bins_df = get_woe(num_bins)
        if iv:
            print(f"{X} 分{len(num_bins):2}組 IV 值: ",get_iv(bins_df))
        if detail:
            print(bins_df)
    # print("\n".join(map(lambda x:f"{x:.16f}",pvs)))
    # 返回分組后的信息
    return get_woe(num_bins) #, get_iv(bins_df)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM