评分卡

本文所用数据下载地址——>点这里下载个人消费类贷款数据

一个完整的模型开发流程,需要有以下流程:

一、数据清洗

1、导库,获取数据

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression as LR#其实日常在导库的时候,并不是一次性能够知道我们要用的所有库的。
#通常都是在建模过程中逐渐导入需要的库。
data = pd.read_csv('../数据/rankingcard.csv',index_col=0)
data.head()

2、探索数据与数据预处理

#观察数据结构
data.shape#(150000, 11)
data.info()

特征和标签含义

去除重复值,并且恢复索引

#去除重复值
data.drop_duplicates(inplace=True)#inplace=True表示替换原数据data.info()#删除之后千万不要忘记,恢复索引
data.index = range(data.shape[0])data.info()

填补缺失值

#探索缺失值
data.info()
data.isnull().sum()/data.shape[0]#得到缺失值的比例
#data.isnull().mean()#上一行代码的另一种形式书写


使用随机森林填补缺失值

def fill_missing_rf(X,y,to_fill):"""使用随机森林填补一个特征的缺失值的函数参数:X:要填补的特征矩阵y:完整的,没有缺失值的标签to_fill:字符串,要填补的那一列的名称"""#构建我们的新特征矩阵和新标签df = X.copy()fill = df.loc[:,to_fill]df = pd.concat([df.loc[:,df.columns != to_fill],pd.DataFrame(y)],axis=1)# 找出我们的训练集和测试集Ytrain = fill[fill.notnull()]Ytest = fill[fill.isnull()]Xtrain = df.iloc[Ytrain.index,:]Xtest = df.iloc[Ytest.index,:]#用随机森林回归来填补缺失值from sklearn.ensemble import RandomForestRegressor as rfrrfr = rfr(n_estimators=100)rfr = rfr.fit(Xtrain, Ytrain)Ypredict = rfr.predict(Xtest)return Ypredict
X = data.iloc[:,1:]
y = data["SeriousDlqin2yrs"]#y = data.iloc[:,0]
X.shape#(149391, 10)#=====[TIME WARNING:1 min]=====#
y_pred = fill_missing_rf(X,y,"MonthlyIncome")#注意可以通过以下代码检验数据是否数量相同
# y_pred.shape ==  data.loc[data.loc[:,"MonthlyIncome"].isnull(),"MonthlyIncome"].shape#确认我们的结果合理之后,我们就可以将数据覆盖了
data.loc[data.loc[:,"MonthlyIncome"].isnull(),"MonthlyIncome"] = y_preddata.info()

3、描述性统计处理异常值

描述性统计

#描述性统计
# data.describe()
data.describe([0.01,0.1,0.25,.5,.75,.9,.99]).T

#异常值也被我们观察到,年龄的最小值居然有0,这不符合银行的业务需求,即便是儿童账户也要至少8岁,我们可以
# 查看一下年龄为0的人有多少
(data["age"] == 0).sum()
#发现只有一个人年龄为0,可以判断这肯定是录入失误造成的,可以当成是缺失值来处理,直接删除掉这个样本
data = data[data["age"] != 0]"""
另外,有三个指标看起来很奇怪:"NumberOfTime30-59DaysPastDueNotWorse"
"NumberOfTime60-89DaysPastDueNotWorse"
"NumberOfTimes90DaysLate"这三个指标分别是“过去两年内出现35-59天逾期但是没有发展的更坏的次数”,“过去两年内出现60-89天逾期但是没
有发展的更坏的次数”,“过去两年内出现90天逾期的次数”。这三个指标,在99%的分布的时候依然是2,最大值却是
98,看起来非常奇怪。一个人在过去两年内逾期35~59天98次,一年6个60天,两年内逾期98次这是怎么算出来的?我们可以去咨询业务人员,请教他们这个逾期次数是如何计算的。如果这个指标是正常的,那这些两年内逾期了98次的
客户,应该都是坏客户。在我们无法询问他们情况下,我们查看一下有多少个样本存在这种异常:"""
data[data.loc[:,"NumberOfTimes90DaysLate"] > 90]
data[data.loc[:,"NumberOfTimes90DaysLate"] > 90].count()
data.loc[:,"NumberOfTimes90DaysLate"].value_counts()#有225个样本存在这样的情况,并且这些样本,我们观察一下,标签并不都是1,他们并不都是坏客户。因此,我们基
# 本可以判断,这些样本是某种异常,应该把它们删除。data = data[data.loc[:,"NumberOfTimes90DaysLate"] < 90]
#一定要恢复索引
data.index = range(data.shape[0])
data.info()

4、样本不均衡问题

#探索标签的分布
X = data.iloc[:,1:]
y = data.iloc[:,0]y.value_counts()#查看每一类别值得数据量,查看样本是否均衡n_sample = X.shape[0]n_1_sample = y.value_counts()[1]
n_0_sample = y.value_counts()[0]print('样本个数:{}; 1占{:.2%}; 0占{:.2%}'.format(n_sample,n_1_sample/n_sample,n_0_sample/n_sample))
#样本个数:149165; 1占6.62%; 0占93.38%


使用采样方法来平衡样本

#如果报错,就在prompt安装:pip install imblearn
import imblearn
#imblearn是专门用来处理不平衡数据集的库,在处理样本不均衡问题中性能高过sklearn很多
#imblearn里面也是一个个的类,也需要进行实例化,fit拟合,和sklearn用法相似from imblearn.over_sampling import SMOTEsm = SMOTE(random_state=42) #实例化
X,y = sm.fit_sample(X,y)n_sample_ = X.shape[0]#278584pd.Series(y).value_counts()n_1_sample = pd.Series(y).value_counts()[1]
n_0_sample = pd.Series(y).value_counts()[0]print('样本个数:{}; 1占{:.2%}; 0占{:.2%}'.format(n_sample_,n_1_sample/n_sample_,n_0_sample/n_sample_))
#样本个数:278584; 1占50.00%; 0占50.00%

5、分训练集和测试集

from sklearn.model_selection import train_test_split
X = pd.DataFrame(X)
y = pd.DataFrame(y)X_train, X_vali, Y_train, Y_vali = train_test_split(X,y,test_size=0.3,random_state=420)
model_data = pd.concat([Y_train, X_train], axis=1)#训练数据构建模型
model_data.index = range(model_data.shape[0])
model_data.columns = data.columnsvali_data = pd.concat([Y_vali, X_vali], axis=1)#验证集
vali_data.index = range(vali_data.shape[0])
vali_data.columns = data.columnsmodel_data.to_csv(r".\model_data.csv")#训练数据
vali_data.to_csv(r".\vali_data.csv")#验证数据

二、分箱

为了衡量特征上的信息量以及特征对预测函数的贡献,银行业定义了概念Information value(IV):

其中N是这个特征上箱子的个数,i代表每个箱子, good%是这个箱内的优质客户(标签为0的客户)占整个特征中所有优质客户的比例, bad%是这个箱子里的坏客户(就是那些会违约,标签为1的那些客户)占整个特征中所有坏客户的比例,而 WOEi则写作:

IV代表的意义是我们特征上的信息量以及这个特征对模型的贡献,由下表来控制:

IV并非越大越好,我们想要找到IV的大小和箱子个数的平衡点。箱子越多,IV必然越小,因为信息损失会非常多,所以,我们会对特征进行分箱,然后计算每 个特征在每个箱子数目下的WOE值,利用IV值的曲线,找出合适的分箱个数。

分箱步骤:

1)我们首先把连续型变量分成一组数量较多的分类型变量,比如,将几万个样本分成100组,或50组
2)确保每一组中都要包含两种类别的样本,否则IV值会无法计算
3)我们对相邻的组进行卡方检验,卡方检验的P值很大的组进行合并,直到数据中的组数小于设定的N箱为止
4)我们让一个特征分别分成[2,3,4…20]箱,观察每个分箱个数下的IV值如何变化,找出最适合的分箱个数
5)分箱完毕后,我们计算每个箱的WOE值, bad%,观察分箱效果

这些步骤都完成后,我们可以对各个特征都进行分箱,然后观察每个特征的IV值,以此来挑选特征。

下面代码仅供学习 参考,版权来自Hsiaofei Tsien。不得作为商用。

1、等频分箱

# dataframe["列名"]
#当这个列存在的时候,就是索引;当这个列名不存在的时候,DataFrame会自动生成叫做这个列名的一个新的列#按照等频对需要分箱的列进行分箱#“age”为例子
model_data["qcut"], updown = pd.qcut(model_data["age"], retbins=True, q=20)#等频分箱"""
pd.qcut,基于分位数的分箱函数,本质是将连续型变量离散化
只能够处理一维数据。返回箱子的上限和下限
参数q:要分箱的个数
参数retbins=True来要求同时返回结构为索引为样本索引,元素为分到的箱子的Series
现在返回两个值:每个样本属于哪个箱子,以及所有箱子的上限和下限
"""
#在这里时让model_data新添加一列叫做“分箱”,这一列其实就是每个样本所对应的箱子
model_data.head()
model_data["qcut"]
model_data["qcut"].value_counts()#所有箱子的上限和下限
updown

2、确保每个箱中都有0和1

# 统计每个分箱中0和1的数量
# 这里使用了数据透视表的功能groupby
coount_y0 = model_data[model_data["SeriousDlqin2yrs"] == 0].groupby(by="qcut").count()["SeriousDlqin2yrs"]coount_y1 = model_data[model_data["SeriousDlqin2yrs"] == 1].groupby(by="qcut").count()["SeriousDlqin2yrs"]#num_bins值分别为每个区间的上界,下界,0出现的次数,1出现的次数
num_bins = [*zip(updown,updown[1:],coount_y0,coount_y1)]#注意zip会按照最短列来进行结合
num_bins

 0 in num_bins[0][2:]

for i in range(20):#如果第一个组没有包含正样本或负样本,向后合并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"""合并了之后,第一行的组是否一定有两种样本了呢?不一定如果原本的第一组和第二组都没有包含正样本,或者都没有包含负样本,那即便合并之后,第一行的组也还是没有包含两种样本所以我们在每次合并完毕之后,还需要再检查,第一组是否已经包含了两种样本这里使用continue跳出了本次循环,开始下一次循环,所以回到了最开始的for i in range(20), 让i+1这就跳过了下面的代码,又从头开始检查,第一组是否包含了两种样本如果第一组中依然没有包含两种样本,则if通过,继续合并,每合并一次就会循环检查一次,最多合并20次如果第一组中已经包含两种样本,则if不通过,就开始执行下面的代码"""#已经确认第一组中肯定包含两种样本了,如果其他组没有包含两种样本,就向前合并#此时的num_bins已经被上面的代码处理过,可能被合并过,也可能没有被合并#但无论如何,我们要在num_bins中遍历,所以写成in range(len(num_bins))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#如果对第一组和对后面所有组的判断中,都没有进入if去合并,则提前结束所有的循环else:break"""这个break,只有在if被满足的条件下才会被触发也就是说,只有发生了合并,才会打断for i in range(len(num_bins))这个循环为什么要打断这个循环?因为我们是在range(len(num_bins))中遍历但合并发生后,len(num_bins)发生了改变,但循环却不会重新开始举个例子,本来num_bins是5组,for i in range(len(num_bins))在第一次运行的时候就等于for i in range(5)range中输入的变量会被转换为数字,不会跟着num_bins的变化而变化,所以i会永远在[0,1,2,3,4]中遍历进行合并后,num_bins变成了4组,已经不存在=4的索引了,但i却依然会取到4,循环就会报错因此在这里,一旦if被触发,即一旦合并发生,我们就让循环被破坏,使用break跳出当前循环循环就会回到最开始的for i in range(20)中此时判断第一组是否有两种标签的代码不会被触发,但for i in range(len(num_bins))却会被重新运行这样就更新了i的取值,循环就不会报错了"""

3、定义WOE和IV函数

#计算WOE和BAD RATE
#BAD RATE与bad%不是一个东西
#BAD RATE是一个箱中,坏的样本所占的比例 (bad/total)
#而bad%是一个箱中的坏样本占整个特征中的坏样本的比例def get_woe(num_bins):# 通过 num_bins 数据计算 woecolumns = ["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["good%"] = df.count_0/df.count_0.sum()df["bad%"] = df.count_1/df.count_1.sum()df["woe"] = np.log(df["good%"] / df["bad%"])return df#计算IV值
def get_iv(df):rate = df["good%"] - df["bad%"]iv = np.sum(rate * df.woe)return iv

4、卡方检验,合并箱体,画出IV曲线

num_bins_ = num_bins.copy()import matplotlib.pyplot as plt
import scipyIV = []
axisx = []while len(num_bins_) > 2:#大于设置的最低分箱个数pvs = []#获取 num_bins_两两之间的卡方检验的置信度(或卡方值)for i in range(len(num_bins_)-1):x1 = num_bins_[i][2:]x2 = num_bins_[i+1][2: ]# 0 返回 chi2 值,1 返回 p 值。pv = scipy.stats.chi2_contingency([x1,x2])[1]#p值# 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_)axisx.append(len(num_bins_))IV.append(get_iv(bins_df))plt.figure()
plt.plot(axisx,IV)
plt.xticks(axisx)
plt.xlabel("number of box")
plt.ylabel("IV")
plt.show()
#选择转折点处,也就是下坠最快的折线点,所以这里对于age来说选择箱数为6

5、 用最佳分箱个数分箱,并验证分箱结果

def get_bin(num_bins_,n):while len(num_bins_) > n:pvs = []for i in range(len(num_bins_)-1):x1 = num_bins_[i][2:]x2 = num_bins_[i+1][2:]pv = scipy.stats.chi2_contingency([x1,x2])[1]# chi2 = scipy.stats.chi2_contingency([x1,x2])[0]pvs.append(pv)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])]return num_bins_afterbins = get_bin(num_bins,6)afterbins

bins_df = get_woe(num_bins)bins_df
#希望每组的bad_rate相差越大越好;
# woe差异越大越好,应该具有单调性,随着箱的增加,要么由正到负,要么由负到正,只能有一个转折过程;
# 如果woe值大小变化是有两个转折,比如呈现w型,证明分箱过程有问题
# num_bins保留的信息越多越好

6、将选取最佳分箱个数的过程包装为函数

def graphforbestbin(DF, X, Y, n=5,q=20,graph=True):'''自动最优分箱函数,基于卡方检验的分箱参数:DF: 需要输入的数据X: 需要分箱的列名Y: 分箱数据对应的标签 Y 列名n: 保留分箱个数q: 初始分箱的个数graph: 是否要画出IV图像区间为前开后闭 (]'''DF = DF[[X,Y]].copy()DF["qcut"],bins = pd.qcut(DF[X], retbins=True, q=q,duplicates="drop")coount_y0 = DF.loc[DF[Y]==0].groupby(by="qcut").count()[Y]coount_y1 = DF.loc[DF[Y]==1].groupby(by="qcut").count()[Y]num_bins = [*zip(bins,bins[1:],coount_y0,coount_y1)]for i in range(q):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])]continuefor 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])]breakelse:breakdef get_woe(num_bins):columns = ["min","max","count_0","count_1"]df = pd.DataFrame(num_bins,columns=columns)df["total"] = df.count_0 + df.count_1df["percentage"] = df.total / df.total.sum()df["bad_rate"] = df.count_1 / df.totaldf["good%"] = df.count_0/df.count_0.sum()df["bad%"] = df.count_1/df.count_1.sum()df["woe"] = np.log(df["good%"] / df["bad%"])return dfdef get_iv(df):rate = df["good%"] - df["bad%"]iv = np.sum(rate * df.woe)return ivIV = []axisx = []while len(num_bins) > n:pvs = []for i in range(len(num_bins)-1):x1 = num_bins[i][2:]x2 = num_bins[i+1][2:]pv = scipy.stats.chi2_contingency([x1,x2])[1]pvs.append(pv)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 = pd.DataFrame(get_woe(num_bins))axisx.append(len(num_bins))IV.append(get_iv(bins_df))if graph:plt.figure()plt.plot(axisx,IV)plt.xticks(axisx)plt.xlabel("number of box")plt.ylabel("IV")plt.show()return bins_df

7、对所有特征进行分箱选择

model_data.columnsfor i in model_data.columns[1:-1]:print(i)graphforbestbin(model_data,i,"SeriousDlqin2yrs",n=2,q=20)


不能自动分箱的变量自己观察然后手写

auto_col_bins = {"RevolvingUtilizationOfUnsecuredLines":6,"age":5,"DebtRatio":4,"MonthlyIncome":3,"NumberOfOpenCreditLinesAndLoans":5}#不能使用自动分箱的变量
hand_bins = {"NumberOfTime30-59DaysPastDueNotWorse":[0,1,2,13],"NumberOfTimes90DaysLate":[0,1,2,17],"NumberRealEstateLoansOrLines":[0,1,2,4,54],"NumberOfTime60-89DaysPastDueNotWorse":[0,1,2,8],"NumberOfDependents":[0,1,2,3]}#保证区间覆盖使用 np.inf替换最大值,用-np.inf替换最小值
#原因:比如一些新的值出现,例如家庭人数为30,以前没出现过,改成范围为极大值之后,这些新值就都能分到箱里边了
hand_bins = {k:[-np.inf,*v[:-1],np.inf] for k,v in hand_bins.items()}

接下来对所有特征按照选择的箱体个数和手写的分箱范围进行分箱:

bins_of_col = {}# 生成自动分箱的分箱区间和分箱后的 IV 值for col in auto_col_bins:bins_df = graphforbestbin(model_data,col,"SeriousDlqin2yrs",n=auto_col_bins[col]#使用字典的性质来取出每个特征所对应的箱的数量,q=20,graph=False)bins_list = sorted(set(bins_df["min"]).union(bins_df["max"]))#保证区间覆盖使用 np.inf 替换最大值 -np.inf 替换最小值bins_list[0],bins_list[-1] = -np.inf,np.infbins_of_col[col] = bins_list#合并手动分箱数据
bins_of_col.update(hand_bins)bins_of_col

三、计算各箱的WOE并映射到数据中

data = model_data.copy()#函数pd.cut,可以根据已知的分箱间隔把数据分箱
#参数为 pd.cut(数据,以列表表示的分箱间隔)
data = data[["age","SeriousDlqin2yrs"]].copy()data["cut"] = pd.cut(data["age"],[-np.inf, 48.49986200790144, 58.757170160044694, 64.0, 74.0, np.inf])data.head()#将数据按分箱结果聚合,并取出其中的标签值
data.groupby("cut")["SeriousDlqin2yrs"].value_counts()#使用unstack()来将树状结构变成表状结构
data.groupby("cut")["SeriousDlqin2yrs"].value_counts().unstack()bins_df = data.groupby("cut")["SeriousDlqin2yrs"].value_counts().unstack()bins_df["woe"] = np.log((bins_df[0]/bins_df[0].sum())/(bins_df[1]/bins_df[1].sum()))bins_df


包装成函数

def get_woe(df,col,y,bins):df = df[[col,y]].copy()df["cut"] = pd.cut(df[col],bins)bins_df = df.groupby("cut")[y].value_counts().unstack()woe = bins_df["woe"] = np.log((bins_df[0]/bins_df[0].sum())/(bins_df[1]/bins_df[1].sum()))return woe#将所有特征的WOE存储到字典当中
woeall = {}
for col in bins_of_col:woeall[col] = get_woe(model_data,col,"SeriousDlqin2yrs",bins_of_col[col])woeall


把所有WOE映射到原始数据中:

#不希望覆盖掉原本的数据,创建一个新的DataFrame,索引和原始数据model_data一模一样
model_woe = pd.DataFrame(index=model_data.index)#将原数据分箱后,按箱的结果把WOE结构用map函数映射到数据中
model_woe["age"] = pd.cut(model_data["age"],bins_of_col["age"]).map(woeall["age"])#对所有特征操作可以写成:
for col in bins_of_col:model_woe[col] = pd.cut(model_data[col],bins_of_col[col]).map(woeall[col])#将标签补充到数据中
model_woe["SeriousDlqin2yrs"] = model_data["SeriousDlqin2yrs"]#这就是我们的建模数据了
model_woe.head()

四、建模与模型验证

处理测试集

vali_woe = pd.DataFrame(index=vali_data.index)for col in bins_of_col:vali_woe[col] = pd.cut(vali_data[col],bins_of_col[col]).map(woeall[col])
vali_woe["SeriousDlqin2yrs"] = vali_data["SeriousDlqin2yrs"]vali_X = vali_woe.iloc[:,:-1]
vali_y = vali_woe.iloc[:,-1]

开始建模

X = model_woe.iloc[:,:-1]
y = model_woe.iloc[:,-1]from sklearn.linear_model import LogisticRegression as LRlr = LR().fit(X,y)
lr.score(vali_X,vali_y)#0.8641356370249832


返回的结果一般,我们可以试着使用C和max_iter的学习曲线把逻辑回归的效果调上去。

c_1 = np.linspace(0.01,1,20)
c_2 = np.linspace(0.01,0.2,20)score = []
for i in c_1: lr = LR(solver='liblinear',C=i).fit(X,y)score.append(lr.score(vali_X,vali_y))
plt.figure()
plt.plot(c_1,score)
plt.show()lr.n_iter_#array([7], dtype=int32)score = []
for i in [1,2,3,4,5,6]: lr = LR(solver='liblinear',C=0.025,max_iter=i).fit(X,y)score.append(lr.score(vali_X,vali_y))
plt.figure()
plt.plot([1,2,3,4,5,6],score)
plt.show()



尽管从准确率来看,我们的模型效果属于一般,但我们可以来看看ROC曲线上的结果。

import scikitplot as skplt#%%cmd
#pip install scikit-plotvali_proba_df = pd.DataFrame(lr.predict_proba(vali_X))
skplt.metrics.plot_roc(vali_y, vali_proba_df,plot_micro=False,figsize=(6,6),plot_macro=False)

五、制作评分卡

评分卡的分数,由下面公式计算:


用numpy求出AB值

B = 20/np.log(2)
A = 600 + B*np.log(1/60)B,A


将截距作为log带入公式进行计算

base_score = A - B*lr.intercept_#lr.intercept_:截距
base_score#array([481.56390143])score_age = woeall["age"] * (-B*lr.coef_[0][1])#lr.coef_:每一个特征建模之后得出的系数
score_age#"age"特征中每个箱对应的分数


通过循环,将所有特征的评分卡内容全部一次性写往一个本地文件ScoreData.csv:

file = "./ScoreData.csv"#open是用来打开文件的python命令,第一个参数是文件的路径+文件名,如果你的文件是放在根目录下,则你只需要文件名就好
#第二个参数是打开文件后的用途,"w"表示用于写入,通常使用的是"r",表示打开来阅读
#首先写入基准分数
#之后使用循环,每次生成一组score_age类似的分档和分数,不断写入文件之中with open(file,"w") as fdata:fdata.write("base_score,{}\n".format(base_score))
for i,col in enumerate(X.columns):#[*enumerate(X.columns)]score = woeall[col] * (-B*lr.coef_[0][i])score.name = "Score"score.index.name = colscore.to_csv(file,header=True,mode="a")

数据保存以后效果:

python机器学习之用逻辑回归制作评分卡(个人消费类贷款数据案例实战)相关推荐

  1. 使用逻辑回归制作评分卡

    1.什么是评分卡? 在银行借贷场景中,评分卡是一种以分数形式来衡量一个客户的信用风险大小的手段,一般来说,评分卡打出的分数越高,客户的信用越好,风险越小. 2.评分卡怎么使用? 对于需要借贷的个人或者 ...

  2. sklearn_逻辑回归制作评分卡_菜菜视频学习笔记

    逻辑回归制作评分卡 3.0 前言 逻辑回归与线性回归的关系 消除特征间的多重共线性 为什么使用逻辑回归处理金融领域数据 正则化的选择 特征选择的方法 分箱的作用 3.1导库 3.2数据预处理 3.2. ...

  3. ​sklearn专题五:用逻辑回归​制作评分卡

    目录 3.1 导库,获取数据 3.2 探索数据与数据预处理 3.2.1 去除重复值 3.2.2 填补缺失值 3.2.3 描述性统计处理异常值 3.2.4 为什么不统一量纲,也不标准化数据分布? 3.2 ...

  4. 用逻辑回归制作评分卡

    目录 一.评分卡 二.导库,获取数据 三.探索数据与数据预处理 1.去除重复值 2.填补缺失值 3.描述性统计处理异常值 4.为什么不统一量纲,也不标准化数据分布 5.样本不均衡问题 6.分训练集和测 ...

  5. 【菜菜的sklearn课堂笔记】逻辑回归与评分卡-用逻辑回归制作评分卡-分箱

    视频作者:菜菜TsaiTsai 链接:[技术干货]菜菜的机器学习sklearn[全85集]Python进阶_哔哩哔哩_bilibili 分训练集和测试集 from sklearn.model_sele ...

  6. 案例:用逻辑回归制作评分卡

    文章目录 1. 探索数据与数据预处理 (1)去重 (2)填补缺失值 用平均值来填补家庭成员 用随机森林填补MonthlyIncome字段 (3)描述性统计处理异常值 (4)为什么不统一量纲,也不标准化 ...

  7. sklearn实战-----5.逻辑回归与评分卡

    1 概述 1.1 名为"回归"的分类器 在过去的四周中,我们接触了不少带"回归"二字的算法,回归树,随机森林的回归,无一例外他们都是区别于分类 算法们,用来处理 ...

  8. 《如何搭建小微企业风控模型》第十一节 逻辑回归与评分卡 节选

    <如何搭建小微企业风控模型>第十一节 逻辑回归与评分卡 节选 第一章 小微企业数据风控技术的框架 小微企业数据贷发展情况概述 搭建小微企业风控模型所需知识 风控模型概览 第二章 强相关变量 ...

  9. python机器学习库sklearn——逻辑回归

    全栈工程师开发手册 (作者:栾鹏) python数据挖掘系列教程 逻辑分类的相关的知识内容可以参考 http://blog.csdn.net/luanpeng825485697/article/det ...

最新文章

  1. 如何使用JavaScript Math.floor生成范围内的随机整数-已解决
  2. 怎样的架构设计能力,才能成功拿下阿里P7?
  3. numpy 的基本使用1
  4. linux有没有递归函数,递归函数
  5. Hadoop 2.0 中的资源管理框架 - YARN(Yet Another Resource Negotiator)
  6. 微星主板超频_内存超频能力依旧拔群!微星MEG Z490 ACE主板评测
  7. Java比较器概述即代码讲解实现
  8. Emmet插件比较实用常用的写法
  9. html响应式布局平移,jQuery图片内部缩放和平移插件jquery.zoom.js
  10. [枚举] Jzoj P3387 终极武器
  11. 华三 h3c 交换机RSTP配置
  12. R语言非参数检验(二)
  13. cass生成里程文件桩号不全,cass生成桩号
  14. 从百草园到三味书屋 鲁迅
  15. 计算机网络的未来的发展前景,浅谈计算机网络的未来发展趋势
  16. 【STM32】中断和事件
  17. 10个小故事,思考大数据
  18. ESP32 NVS同windows文件系统的类比,附上一段NVS操作的代码解析
  19. 赛联区块链教育:对区块链技术做个普及
  20. 如何写自己的lib文件并测试

热门文章

  1. 2023游戏开发制作如何选择合适的游戏引擎?看完本文就知道
  2. 食物和计算机中一样的英语,表示食物或饮料的英语单词有哪些?
  3. vue+echarts实现江苏省疫情地图
  4. cakephp 框架
  5. API电商平台“淘宝店铺所有商品接口”调用示例
  6. 研究生毕业入职阿里,2年升职到P7,晒出真实月薪,以为看错了
  7. C++.POJ_1004
  8. 天猫店铺介绍,影响天猫店铺转让价格的六个因素
  9. 基于WP7开发的Silverlight常用控件详解
  10. sdcms IIS7 windows server 2008 配置后,无法修改模板页面