写了好久,整理了好久,可运行,超详细的评分卡模型实践。
跟着做一遍一定大有收获!
文章目录
数据集:Kaggle上的Give Me Some Credit的数据
1、了解变量
2、数据集分析与预处理
查看缺失值
df = pd.read_csv("./GiveMeSomeCredit/cs-training.csv").drop("Unnamed: 0", axis=1)
df.info()
可知, 月收入 与 家属数量 存在 缺失值。
# 详细查看每个变量的情况与缺失率
df.describe().T.assign(missing_rate = df.apply(lambda x: (len(x)-x.count())/float(len(x))))
缺失值处理
原则:缺失值较少可以直接删除(也可以进行一些其他的填补操作。此例中删除),若较多则根据变量之间存在的关系来填补缺失值(此处采用随机森林的方法)。
此处源码中有一个小报错,我这边有做修改:
from sklearn.ensemble import RandomForestRegressor
# 用随机森林对'月收入'缺失值预测填充函数
def set_missing(df):
# 把已有的数值型特征取出来
process_df = df.iloc[:, [5,0,1,2,3,4,6,7,8,9]]
# 分成已知该特征和未知该特征两部分
known = process_df[process_df.MonthlyIncome.notnull()]
unknown = process_df[process_df.MonthlyIncome.isnull()]
# X为特征属性值
X = known.iloc[:, 1:]
# y为结果标签值
y = known.iloc[:, 0]
# fit到RandomForestRegressor之中
rfr = RandomForestRegressor(random_state=0, n_estimators=200, max_depth=3, n_jobs=-1)
rfr.fit(X, y)
# 用得到的模型进行未知特征值预测
predicted = rfr.predict(unknown.iloc[:, 1:]).round(0)
print("预测值: ", predicted)
# 用得到的预测结果填补原缺失数据
df.loc[(df.MonthlyIncome.isnull()), 'MonthlyIncome'] = predicted
return df
df = set_missing(df)
# 删除所剩空值
df = df.dropna()
# 删除重复值
df = df.drop_duplicates()
查看并处理异常值
异常值:偏离大多数抽样数据的数值,通常指测定值中与平均值的偏差超过两倍标准差的测定值。
通常采用离群值检测的方法对异常值进行检测
此处也在源代码的基础上增加了不同的展示(直方图+箱线图):
# 1.RevolvingUtilizationOfUnsecuredLines
f,[ax1,ax2]=plt.subplots(1,2,figsize=(12,5))
sns.distplot(df['RevolvingUtilizationOfUnsecuredLines'],ax=ax1)
sns.boxplot(y='RevolvingUtilizationOfUnsecuredLines',data=df,ax=ax2)
plt.show()
print(df['RevolvingUtilizationOfUnsecuredLines'].describe())
可知,数据分布及其不正常,中位数和四分之三位数都小于1,但是最大值确达到了50708,可用额度比值应该小于1,所以后面将大于1的值当做异常值剔除。
len(df[df['RevolvingUtilizationOfUnsecuredLines']>1]) # 3260条
df = df[df['RevolvingUtilizationOfUnsecuredLines']<=1]
# 2、年龄分布
f,[ax1,ax2]=plt.subplots(1,2,figsize=(12,5))
sns.distplot(df['age'],ax=ax1)
sns.boxplot(y='age',data=df,ax=ax2)
plt.show()
print(df['age'].describe())
# 存在小于0的情况,明显异常可去除。 大于100的较多且连续,可保留
print('count >100:', len(df[df.age>100]))
df = df[df.age>0]
# 3、逾期30-59天 | 60-89天 | 90天笔数分布:
f,[[ax1,ax2],[ax3,ax4],[ax5,ax6]] = plt.subplots(3,2,figsize=(24,10))
sns.distplot(df['NumberOfTime30-59DaysPastDueNotWorse'],ax=ax1)
sns.boxplot(y='NumberOfTime30-59DaysPastDueNotWorse',data=df,ax=ax2)
sns.distplot(df['NumberOfTime60-89DaysPastDueNotWorse'],ax=ax3)
sns.boxplot(y='NumberOfTime60-89DaysPastDueNotWorse',data=df,ax=ax4)
sns.distplot(df['NumberOfTimes90DaysLate'],ax=ax5)
sns.boxplot(y='NumberOfTimes90DaysLate',data=df,ax=ax6)
plt.show()
不够清晰,换一种:
df.boxplot(column=["NumberOfTime30-59DaysPastDueNotWorse", "NumberOfTime60-89DaysPastDueNotWorse", "NumberOfTimes90DaysLate"],
rot=30)
上面的箱线图可以看出 NumberOfTime30-59DaysPastDueNotWorse,NumberOfTime60-89DaysPastDueNotWorse,NumberOfTimes90DaysLate三个特征都存在两个异常值,下面使用 unique() 方法查看具体的异常值:
print("NumberOfTime30-59DaysPastDueNotWorse:", df["NumberOfTime30-59DaysPastDueNotWorse"].unique())
print("NumberOfTime60-89DaysPastDueNotWorse:", df["NumberOfTime60-89DaysPastDueNotWorse"].unique())
print("NumberOfTimes90DaysLate:", df["NumberOfTimes90DaysLate"].unique())
此处可以直接删除,也可以用中位数等来代替,可以自行考虑并选择。
若想删除:
df = df[df["NumberOfTime30-59DaysPastDueNotWorse"]<95]
df = df[df["NumberOfTime60-89DaysPastDueNotWorse"]<95]
df = df[df["NumberOfTimes90DaysLate"]<95]
此处用中位数代替:
# 用中位数替代异常值
def replaceOutlier(data):
New = []
med = data.median()
for val in data:
if ((val == 98) | (val == 96)):
New.append(med)
else:
New.append(val)
return New
df["NumberOfTime30-59DaysPastDueNotWorse"] = replaceOutlier(df["NumberOfTime30-59DaysPastDueNotWorse"])
df["NumberOfTime60-89DaysPastDueNotWorse"] = replaceOutlier(df["NumberOfTime60-89DaysPastDueNotWorse"])
df["NumberOfTimes90DaysLate"] = replaceOutlier(df["NumberOfTimes90DaysLate"])
# 替换后的箱线图
df.boxplot(column=["NumberOfTime30-59DaysPastDueNotWorse", "NumberOfTime60-89DaysPastDueNotWorse", "NumberOfTimes90DaysLate"],
rot=30)
plt.show()
正常多了,下一个:
#4、DebtRatio负债率特征分布
f,[ax1,ax2] = plt.subplots(1,2,figsize=(12,5)</