2014年10月11日 星期六

[R] 相見太晚,回頭是岸,分析WOE和IV值的神器套件

        

        最近因為投入研究Spark,對於資料分析的東西比較沒時間琢磨,難得一個雙十假日又可以來體驗一下礦工的生活了.
        
        Data mining不管是用何種方式建立模型,從幾百個變項中選出幾個候選變項往往是整個資料採掘過程最耗人的一個過程,實務上常使用WOE和IV用於變項篩選和分析(特別是信用評分卡模型),並透過WOE將原始資料離散化並取代後放入logistic模型中進行二分變項(結果只有0或1)的分析.


        WOE和IV值雖然相關但是分別代表兩種不同的概念:
WOE是Weight of Evidence的縮寫,公式為
從白話來理解這個公式的意思,WOE代表了變項中各選項對於最後結果(好或壞)的影響.(因為最近在教統計,所以比較多話一點)在看這個公式的時候,從裡面往外理解,最核心的部分就是Goods/Bads,其實就是單一選項與結果的比例,可以作為判斷單一選項對於事件的判別力.因為受到自然對數的影響,如果Goods>Bads,相除>1,WOE就會是正值;相反的當Goods<Bads,WOE就會是負值.

IV為Information Value的縮寫,公式為
i為單一變項中的值的數量,例如性別這個變項,有男女兩個值,i就等於2;如果我們將年齡這個連續變項切成十段,那i = 10.因此IV值就是將一個變項中的WOE整理一下加總,代表的是單一變項對於結果的影響力.评分卡模型剖析之一(woe、IV、ROC、信息熵)這篇文章有提到IV值的公式跟信息 熵(entropy)的相似,可以用類似的概念,將IV值理解為這一個變項帶給我們的得知結果的信息強度.通常IV > 0.3代表這個變項對結果有強烈影響力.0.1 ~ 0.3代表中度影響力.

整理成表格來說的話就會是這樣:

        將AGE這個連續變項切成六個區段後,分別去計算每個區段中好與壞的數量與占比,好壞占比相除取對數就是WOE,好壞占比相減乘上WOE就是單一區段的IV值,加總後就成了這個變項的IV值.

        雖然從表格看起來好像很簡單,但是data mining一開始都是丟幾百個變項進來看的,如果每個變項都要做一次這個表格,然後在比較每個變項的IV值.而且別忘了,所有的連續變項還要先離散化才能計算,這真的是一個超級苦差事.

        不過,既然這是件苦差事,自然就有專家幫我們找到解決的辦法(終於來到本篇重點).R有一個套件能夠幫我們計算WOE和IV值!更神的是還可以自動將連續變項離散化!!並且將WOE值取代原本的變項!!!(我媽問我怎麼跪在電腦前面玩電腦)

        這個套件叫做riv,一起來體驗他的強大:
library(devtools)
install_github("riv","tomasgreif")
library(woe)

    這次用的資料來自kaggle上的鐵達尼資料

train <- read.csv("/Titanic/train.csv", stringsAsFactors = FALSE, fill =TRUE)
library(woe)
iv.mult(train,"Survived",TRUE)
##       Variable InformationValue Bins ZeroBins    Strength
## 1          Sex          1.34168    2        0  Suspicious
## 2         Fare          0.63354    6        0 Very strong
## 3       Pclass          0.50095    3        0 Very strong
## 4     Embarked          0.12237    4        1     Average
## 5        Cabin          0.11997  148      131     Average
## 6       Ticket          0.11918  681      632     Average
## 7        Parch          0.08972    2        0        Weak
## 8        SibSp          0.05600    2        0        Weak
## 9  PassengerId          0.04650    4        0        Weak
## 10         Age          0.01191    5        0   Wery weak
## 11        Name          0.00000  891      891   Wery weak
看到沒有,超強! 最後還很貼心的幫你依IV值的強弱排序.

對於IT來說,由於要用IV來取代變項值,原本還要另外寫sql語法寫回資料庫,但是有了這個套件,連SQL語法都幫你寫好了!
iv.mult(train,"Survived",vars=c("Sex","Age"))
## Information Value 1.34 
## Information Value 0.01
## [[1]]
##   variable  class outcome_0 outcome_1  pct_0  pct_1   odds     woe    miv
## 1      Sex female        81       233 0.1475 0.6813 0.2166 -1.5299 0.8166
## 2      Sex   male       468       109 0.8525 0.3187 2.6747  0.9838 0.5251
## 
## [[2]]
##   variable         class outcome_0 outcome_1  pct_0  pct_1   odds      woe
## 1      Age       (;16.5)        64        36 0.1166 0.1053 1.1075  0.10208
## 2      Age   <16.5;21.5)        65        39 0.1184 0.1140 1.0383  0.03754
## 3      Age  <21.5;30.75)       123        84 0.2240 0.2456 0.9122 -0.09192
## 4      Age <30.75;36.25)        60        48 0.1093 0.1404 0.7787 -0.25014
## 5      Age      <36.25;)       123        72 0.2240 0.2105 1.0642  0.06223
##         miv                                                       sql
## 1 0.0011547                    when Age < 16.5 then 0.102076440456636
## 2 0.0001637   when Age >= 16.5 AND Age < 21.5 then 0.0375379193190652
## 3 0.0019827 when Age >= 21.5 AND Age < 30.75 then -0.0919201479178216
## 4 0.0077698 when Age >= 30.75 AND Age < 36.25 then -0.250144153132716
## 5 0.0008412                 when Age >= 36.25 then 0.0622305319094367
(為什麼我邊流淚邊寫網誌T_T)

另外剛剛有提到riv套件自動幫我們將連續資料區段化,但是有沒有辦法自動調整呢? 畢竟不同的切點會影響到最後的結果,當然也有!
library(rpart)
iv.num(german_data,"duration","gb",rcontrol=rpart.control(cp=.001))
## Information Value 0.36
##    variable       class outcome_0 outcome_1   pct_0    pct_1   odds
## 1  duration      (;8.5)        84        10 0.12000 0.033333 3.6000
## 2  duration   <8.5;9.5)        35        14 0.05000 0.046667 1.0714
## 3  duration  <9.5;11.5)        34         3 0.04857 0.010000 4.8571
## 4  duration <11.5;12.5)       130        49 0.18571 0.163333 1.1370
## 5  duration <12.5;15.5)        59        13 0.08429 0.043333 1.9451
## 6  duration   <15.5;19)        72        43 0.10286 0.143333 0.7176
## 7  duration   <19;20.5)         7         1 0.01000 0.003333 3.0000
## 8  duration <20.5;34.5)       191        85 0.27286 0.283333 0.9630
## 9  duration <34.5;37.5)        46        37 0.06571 0.123333 0.5328
## 10 duration <37.5;43.5)        12         5 0.01714 0.016667 1.0286
## 11 duration     <43.5;)        30        40 0.04286 0.133333 0.3214
##         woe       miv
## 1   1.28093 1.110e-01
## 2   0.06899 2.300e-04
## 3   1.58045 6.096e-02
## 4   0.12842 2.874e-03
## 5   0.66529 2.725e-02
## 6  -0.33183 1.343e-02
## 7   1.09861 7.324e-03
## 8  -0.03768 3.947e-04
## 9  -0.62957 3.628e-02
## 10  0.02817 1.341e-05
## 11 -1.13498 1.027e-01
##                                                                   sql
## 1                           when duration < 8.5 then 1.28093384546206
## 2     when duration >= 8.5 AND duration < 9.5 then 0.0689928714869514
## 3      when duration >= 9.5 AND duration < 11.5 then 1.58045037556085
## 4    when duration >= 11.5 AND duration < 12.5 then 0.128416291957752
## 5    when duration >= 12.5 AND duration < 15.5 then 0.665290226056979
## 6     when duration >= 15.5 AND duration < 19 then -0.331831857064711
## 7       when duration >= 19 AND duration < 20.5 then 1.09861228866811
## 8  when duration >= 20.5 AND duration < 34.5 then -0.0376756888308902
## 9   when duration >= 34.5 AND duration < 37.5 then -0.629574376542333
## 10  when duration >= 37.5 AND duration < 43.5 then 0.0281708769666964
## 11                       when duration >= 43.5 then -1.13497993283898

這邊透過rpart套件,將資料依據cp大小切成不同等份,可以觀察不同切點上WOE和IV的變化,找到最適合的切點.

        甚至還可以做圖
iv.plot.woe(iv.num(german_data,"duration","gb",rcontrol=rpart.control(cp=.001)))
## Information Value 0.36
plot of chunk unnamed-chunk-4
(我知道大家都已經跪在電腦前分析了)

        這個套件還有其他包括將WOE直接寫回data中的功能,相信能夠大大節省data mining人員處理變項的時間,riv套件真的神!

同場加映: