r k-means 分类结果_R语言信用评分卡:数据分箱(binning)
作者:黃天元,復(fù)旦大學(xué)博士在讀,熱愛數(shù)據(jù)科學(xué)與R,熱衷推廣R在工業(yè)界與學(xué)術(shù)界的應(yīng)用。郵箱:huang.tian-yuan@qq.com.歡迎合作交流
library(knitr) opts_chunk$set(eval = F)在做信用評分卡的時候,很重要的一個環(huán)節(jié)就是特征工程。在學(xué)習(xí)了WOE、IF這些概念之后,我認(rèn)為對于機器學(xué)習(xí)的模型,無論是對分類變量還是數(shù)值變量,最好的特征工程方法都是分箱。至少對二分類的問題來說,是非常合適的。
本文先談?wù)劄槭裁匆獙?shù)據(jù)進(jìn)行分箱,然后給出分箱在R語言中的優(yōu)秀解決方案。
為什么要分箱?
談為什么要分箱之前,我們需要認(rèn)清楚的是,我們的表格中有兩種數(shù)據(jù):離散變量和連續(xù)變量。因此我們需要分開談為什么要對它們進(jìn)行分箱。
對于離散變量(也就是分類變量)而言,經(jīng)常遇到的問題就是,類別太多了!一下子幾十個種類,那么如果做one-hot就會產(chǎn)生維度災(zāi)難。但是機器學(xué)習(xí)從來都只是認(rèn)識數(shù)字,就算是給了因子變量,也是變成dummy來做,因此沒有辦法。唯一的辦法就是,手動地進(jìn)行分類。試想一下,雖然有這么多類別,我們能不能根據(jù)它們共有的特質(zhì),把它們歸并起來,分成幾個大類?又或者,其實很多類別只有很少的比例,能不能把這些小比例的類別合并為others?再不濟(jì),我們把它們先用one-hot編碼,然后采用降維技術(shù)再篩掉一些類別?這些都是可以在一定程度上解決這個問題的。而分箱方法就是其中一種,它能夠把非常多的類別按照一定的規(guī)則歸并為少數(shù)的類別,從而突出了整體特征,避免了維度災(zāi)難。
對于連續(xù)變量(也就是數(shù)值變量),我們需要認(rèn)真思考一個問題:我們最后得到的是一個極大似然估計,也就是概率值。如果使用邏輯回歸(LR),這個連續(xù)變量一個單位的變化,真的就呈線性地增加了這個概率了嗎?我認(rèn)為很多情況下,答案是否定的。當(dāng)一個人的年齡超過一定的歲數(shù),他再增長一歲給違約概率帶來的變化其實不是特別的大。而且在不同年齡范圍,每個人違約的概率收到年齡的影響也不盡相同,所以后面才會有一個叫做多元自適應(yīng)回歸樣條的出現(xiàn)(其本質(zhì)是分段線性回歸)。不過對于這么多變量而言,做樣條顯然不劃算,弄得模型非常復(fù)雜。比較好的選擇就是,在數(shù)據(jù)準(zhǔn)備的階段對這些連續(xù)變量做分箱。這樣有非常多的好處,我列舉一下:
- 解決了缺失值問題:在分箱中,缺失值會單獨成為一個類別。如果缺失機制是非隨機缺失,那么這個特征是非常有用的。如果是隨機缺失,則必須把這些記錄刪除,或者按照缺失機制進(jìn)行插值。
- 解決了離群值問題:在發(fā)現(xiàn)離群值的時候,我們會馬上判斷究竟是出錯了還是真實的。如果是出錯了,那么我們會剔除這些記錄,或者是糾正它們。如果是真實的,那么就這么剔除掉就有些可惜了(除非樣本量很大,剔除沒有什么影響)。離群值對于線性模型的影響非常大,肯定是要處理的,基本處理方法包括設(shè)為缺失值、平均值、眾數(shù)等,而分箱不失為一個非常優(yōu)秀的選擇。舉例,如果只有一位高齡的用戶,分箱的時候也只會進(jìn)入“>60歲”的組別,這樣模型就具有很強的穩(wěn)定性。
- 解決了不等斜率的問題:模型在不同的區(qū)間斜率是不一樣的,這個假設(shè)我認(rèn)為是真實存在的,前面也提到了。就是在不同的范圍內(nèi),單位自變量的變化帶來的因變量的變化是不同的。如果進(jìn)行了分箱,我們就可以客觀地得到不同箱帶來的影響。
不過分箱的問題也是有的,因為分箱把所有變量最后都轉(zhuǎn)化為分類變量,不過計算機只認(rèn)識數(shù)字,最后還是要變?yōu)閿?shù)值型變量的。這時候我們要知道分箱之后得到的分類變量,究竟是否存在著有序的關(guān)系。如果存在,是否是單調(diào)的。
- 如果不存在有序關(guān)系,比如不同的省份之間是并列的關(guān)系,那么就必須使用One-hot編碼。
- 如果存在有序關(guān)系,但是不是單調(diào)的,則需要特殊考慮。比如還貸能力是中年人最強,青年、老年都比較弱,這就是單峰模型,不是單調(diào)遞增或遞減的關(guān)系。如果我們只按照年齡大小來用無監(jiān)督分箱,肯定是不行的。
- 如果存在有序關(guān)系,而且是單調(diào)的,那么可以按照其關(guān)系直接賦予數(shù)值。不過無監(jiān)督地賦值1/2/3/4...,這樣會有問題。這個還是我們上面提到的斜率不等的問題。
雖然問題這么多,但是自從WOE編碼出現(xiàn)之后,這個問題似乎迎刃而解。這里不展開討論WOE的知識(感興趣找巨人的肩膀【詳解】銀行信用評分卡中的WOE在干什么),但是結(jié)論就是:WOE得到的證據(jù)權(quán)值表征了一個自變量類別對二分類因變量結(jié)果帶來的變化方向及其程度。這簡直就是自帶降維的one-hot編碼,在把所有分類變量轉(zhuǎn)化為數(shù)值變量的同時,優(yōu)秀地避免了上面提到的有序單調(diào)問題,還避免了維度災(zāi)難。
目前個人的認(rèn)識就是:至少是做二分類問題的時候,應(yīng)該對所有解釋變量變量進(jìn)行分箱(這同時也提高了數(shù)據(jù)的可解釋水平,無論是對于數(shù)值型還是分類變量)。分箱之后,統(tǒng)一使用WOE編碼將其數(shù)值化,然后再進(jìn)行建模。
分箱的種類
分箱大體分為無監(jiān)督分箱和有監(jiān)督分箱兩類,主要是針對數(shù)值型變量。
1. 無監(jiān)督分箱
- 等長分箱(Equal length intervals):分箱依據(jù)是數(shù)值的范圍。比如0-100分,分為4個箱,那么切分點就是25/50/75。
- 等頻分箱(Equal frequency intervals):分箱依據(jù)是分位數(shù),也就是分箱之后各個箱包含樣本量基本是一樣多的。
- 聚類分箱:分箱依據(jù)是,箱內(nèi)平均差距最小,箱之間的平均差距最大。算法有kmeans,以及基于隨機過程的“bagged clustering”。
2. 有監(jiān)督分箱
- 卡方分箱(ChiMerge):把數(shù)值排序后,計算相鄰兩個數(shù)值合并后的卡方值,合并所有卡方值最小的兩個值。重復(fù)上述過程,直到滿足結(jié)束條件。
- 決策樹分箱:以這個數(shù)值變量為自變量,結(jié)果變量為因變量,進(jìn)行決策樹模型擬合,根據(jù)擬合結(jié)果進(jìn)行分箱。
R語言實現(xiàn)
需要明確的,我們需要輸入什么,要得到什么輸出。輸入就是我們原始的數(shù)據(jù)表格,包含解釋變量和響應(yīng)變量。輸出應(yīng)該包含兩個部分:1.分箱的對應(yīng)關(guān)系;2.分箱后的結(jié)果表格。
無監(jiān)督分箱
無監(jiān)督分箱采用dlookr包的binning函數(shù)最佳。type參數(shù)可以控制無監(jiān)督分箱的類型,包含了5種分箱類型,其中等長分箱的參數(shù)為“equal”,等頻分箱的參數(shù)為“quantile”,K均值聚類的參數(shù)為“kmeans”,bagged clustering的參數(shù)為“bclust”。
直接上官方案例代碼:
library(pacman) p_load(dlookr)# Generate data for the example carseats <- ISLR::Carseats carseats[sample(seq(NROW(carseats)), 20), "Income"] <- NA carseats[sample(seq(NROW(carseats)), 5), "Urban"] <- NA # Binning the carat variable. default type argument is "quantile" bin <- binning(carseats$Income) # Print bins class object bin # Summarise bins class object summary(bin) # Plot bins class object plot(bin) # Using labels argument bin <- binning(carseats$Income, nbins = 4,labels = c("LQ1", "UQ1", "LQ3", "UQ3")) bin # Using another type argument bin <- binning(carseats$Income, nbins = 5, type = "equal") bin bin <- binning(carseats$Income, nbins = 5, type = "pretty") bin bin <- binning(carseats$Income, nbins = 5, type = "kmeans") bin bin <- binning(carseats$Income, nbins = 5, type = "bclust") bin# ------------------------- # Using pipes & dplyr # ------------------------- library(dplyr)carseats %>%mutate(Income_bin = binning(carseats$Income)) %>%group_by(ShelveLoc, Income_bin) %>%summarise(freq = n()) %>%arrange(desc(freq)) %>%head(10)代碼中,bin就是一個包含三個屬性值的因子向量,直接就是分箱的最終結(jié)果。而對應(yīng)關(guān)系也就在結(jié)果之中,其因子名稱即是區(qū)間的范圍。
有監(jiān)督分箱
如果只考慮決策樹分箱,而且不打算進(jìn)行WOE編碼,那么可以使用dlookr包的binning_by函數(shù),官方使用幫助如下:
# Generate data for the example carseats <- ISLR::Carseats carseats[sample(seq(NROW(carseats)), 20), "Income"] <- NA carseats[sample(seq(NROW(carseats)), 5), "Urban"] <- NA# optimal binning bin <- binning_by(carseats, "US", "Advertising") bin # summary optimal_bins class summary(bin) # visualize optimal_bins class plot(bin, sub = "bins of Advertising variable")不過既然是評分卡系列,一般分箱之后都是馬上要做WOE編碼的。如果要做評分卡,我們馬上使用優(yōu)秀的scorecard包,其中woebin是核心函數(shù),能夠獲得經(jīng)過分析后得到的分箱關(guān)系(分為哪幾個箱,每個箱的特征)。利用這個分箱關(guān)系,直接對原來的數(shù)據(jù)進(jìn)行分箱,則需要使用woebin_ply函數(shù)。這個函數(shù)不僅支持決策樹分箱(method = "tree"),還支持卡方分箱(method = "chimerge")。不過默認(rèn)的方法是決策樹方法。
此外,woebin_plot函數(shù)還可以支持作圖,得到基于ggplot2的一個圖形,展示了不同分箱中樣本的數(shù)量及其好壞的比例。這種功能不說是全自動化,也是半自動化做出評分卡了。幫助文檔已經(jīng)非常優(yōu)秀,我不再自己給出案例,直接搬運scorecard包的幫助文檔。 請自行下載scorecard包。
library(pacman) p_load(scorecard)woebin函數(shù):
# load germancredit data data(germancredit)# Example I # binning of two variables in germancredit dataset # using tree method bins2_tree = woebin(germancredit, y="creditability",x=c("credit.amount","housing"), method="tree") bins2_tree## Not run: # using chimerge method bins2_chi = woebin(germancredit, y="creditability",x=c("credit.amount","housing"), method="chimerge")# Example II # binning of the germancredit dataset bins_germ = woebin(germancredit, y = "creditability") # converting bins_germ into a dataframe # bins_germ_df = data.table::rbindlist(bins_germ)# Example III # customizing the breakpoints of binning library(data.table) dat = rbind(germancredit,data.table(creditability=sample(c("good","bad"),10,replace=TRUE)),fill=TRUE)breaks_list = list(age.in.years = c(26, 35, 37, "Inf%,%missing"),housing = c("own", "for free%,%rent") )special_values = list(credit.amount = c(2600, 9960, "6850%,%missing"),purpose = c("education", "others%,%missing") )bins_cus_brk = woebin(dat, y="creditability",x=c("age.in.years","credit.amount","housing","purpose"),breaks_list=breaks_list, special_values=special_values)## End(Not run)woebin_ply函數(shù):
# load germancredit data data(germancredit)# Example I dt = germancredit[, c("creditability", "credit.amount", "purpose")]# binning for dt bins = woebin(dt, y = "creditability")# converting original value to woe dt_woe = woebin_ply(dt, bins=bins) str(dt_woe)## Not run: # Example II # binning for germancredit dataset bins_germancredit = woebin(germancredit, y="creditability")# converting the values in germancredit to woe # bins is a list which generated from woebin() germancredit_woe = woebin_ply(germancredit, bins_germancredit)# bins is a dataframe bins_df = data.table::rbindlist(bins_germancredit) germancredit_woe = woebin_ply(germancredit, bins_df)## End(Not run)woebin_plot函數(shù):
# Load German credit data data(germancredit)# Example I bins1 = woebin(germancredit, y="creditability", x="credit.amount")p1 = woebin_plot(bins1) print(p1)## Not run: # Example II bins = woebin(germancredit, y="creditability") plotlist = woebin_plot(bins) print(plotlist$credit.amount)# # save binning plot # for (i in 1:length(plotlist)) { # ggplot2::ggsave( # paste0(names(plotlist[i]), ".png"), plotlist[[i]], # width = 15, height = 9, units="cm" ) # }## End(Not run)一鍵就能夠?qū)λ凶兞窟M(jìn)行分箱,并進(jìn)行WOE編碼。強大!不過,此包并非沒有競爭者,woeBinning包也能夠?qū)崿F(xiàn)類似的功能,還有smbinning也是專門針對分箱任務(wù)的包。不過scorecard加載了data.table包,又有foreach和doParallel這些并行支持,性能絕對是最強大的。如果要處理海量數(shù)據(jù),scorecard能夠調(diào)動更多的計算資源,更加快速。
這個包幫助文檔很簡潔,但是非常優(yōu)秀,值得深入學(xué)習(xí),也期待它開發(fā)更多豐富的功能。
總結(jié)
以上是生活随笔為你收集整理的r k-means 分类结果_R语言信用评分卡:数据分箱(binning)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 秀出两个相关联的表中满足条件
- 下一篇: python expect模块_成为顶级