HW10-第八讲:特征工程与慈善机构精准营销案例

浏览: 2099

课程链接:   https://edu.hellobi.com/course/280

涉及内容:信用卡客户流失预警模型-CRISP_DM建模流程、数据清洗、变量压缩、模型开发与评估

1、背景介绍:

随着信用卡市场的成熟,人均持卡量不断增加,加上第三方支付牌照的持续发放,人们可选择的支付手段不断丰富,信用卡客户流失(销卡)呈现常态化。

C银行在国内信用卡市场中处于领先地位,管理层非常重视客户生命周期管理并取得了良好的回报,为进一步完善对客户流失及挽留环节的管理,管理层要求建立大数据模型,

基于对客户销卡决心和预期价值的准确预测,制定差异化挽留策略,实现收益与成本的最佳平衡。具体来说,当客户打进电话提出销卡时,将客户的销卡决心、预期价值以及相应的应对策略,

展示在客服人员的工作指导窗口上,在客户挽留环节改进客户体验,加强对潜在高价值客户的挽留力度。

本次作业根据提供的数据(“CSR_CHURN_Samp.csv”,引用自陈春宝等出版的《SAS金融数据挖掘与建模》)信用卡客户流失预警模型。

2、本案例涉及的部分变量说明如下:

STA_DTE 数据提取时间

Evt_Flg 是否流失

Age 年龄

Gen 性别,1=男

Buy_Type 近一个月主要的购物类型

R3m_Avg_Cns_Amt 近3个月月均消费金额

R6m_Avg_Rdm_Pts 近6个月月均兑换积分

R12m_Avg_Cns_Cnt 近12个月月均消费次数

R6m_Cls_Nbr 近半年还款拖欠次数

Ilt_Bal_Amt 当前分期未还余额

Lmth_Fst_Ilt 累计分期产品办理次数

Lmth_Fst_Int 累计小额信贷申请次数

Csr_Dur 累计持卡时长

R6m_Call_Nbr 近半年投诉次数

Total_Call_Nbr 累计投诉次数

Net_Cns_Cnt 累计网上交易次数

Ovs_Cns_Amt 累计境外交易次数

其他略:学习到这个阶段,已经可以适应不需要知道变量含义,凭借数据分析工序建立分类模型的状态。

3、作业安排:

3.1 基础知识:

      1)分箱的作用,WOE和IV的作用;

      2)比较IV法和统计检验法筛选变量重要性之间的异同。

3.2 案例解答步骤如下:

     1)根据STA_DTE字段进行拆分,分出建模和测试数据集

     2)对涉及的X进行分箱,计算分箱后X的WOE值和IV值。考虑数据清洗和缺失值处理

     3)进行解释变量X之间的信息压缩。

    4)使用逻辑回归建模并检验

import pandas as pd
import numpy as np
import os
#%%
os.chdir(r'D:\Learningfile\天善学院\280_Ben_八大直播八大案例配套课件\提交-第八讲:特征工程与慈善机构精准营销案例\作业')
data=pd.read_csv('CSR_CHURN_Samp.csv')
data1=data.copy()

data1.dtypes
#%%
data1.Evt_Flg.value_counts()

#%%
#1)根据STA_DTE字段进行拆分,分出建模和测试数据集
#%%
import time

time_d={}
for i in data1['STA_DTE']:
tempTime = time.strptime(i,'%d%b%Y')
time_d[i] = time.strftime('%Y-%m-%d',tempTime)

data1['STA_DTE'] = data1['STA_DTE'].map(time_d)

data1=data1.drop(['CSR_ID'],axis=1)

#%%
#拆分训练集与测试集
train_data=data1.loc[data1['STA_DTE']=='2014-12-31'].drop('STA_DTE',axis=1)
test_data=data1.loc[data1['STA_DTE']=='2015-01-31'].drop('STA_DTE',axis=1)

#%%
#2)对涉及的X进行分箱,计算分箱后X的WOE值和IV值。考虑数据清洗和缺失值处理

#%%
y='Evt_Flg'

# 连续变量
var_c = ["Value","Age","Csr_Dur",'Ctr_R1y','R1m_Trd3_Cns_Amt',
"R3m_Avg_Cns_Amt",'Lmth_Fst_Int','Ovs_Cns_Cnt','Ilt_Bal_Amt',
"Net_Cns_Cnt","Ovs_Cns_Amt",
"R6m_Avg_Rdm_Pts","R12m_Avg_Cns_Cnt","R3m_Max_Csh_Amt",
"R6m_Max_Csh_Amt","R12m_Max_Csh_Amt","R6m_Csh_Mth_Nbr",
"R3m_Max_Ilt_Amt","R6m_Max_Ilt_Amt","Lmth_Fst_Ilt",
"Total_call_nbr","R6M_CALL_NBR","R6M_CLS_NBR"]

# 分类变量
var_d = ['Gen', 'Edu', 'Buy_Type']


#%%
train_X = train_data[var_c + var_d].copy()
train_Y = train_data[y].copy()

test_X = test_data[var_c + var_d].copy()
test_Y = test_data[y].copy()
#%%

#根据IV值筛选变量-连续变量

for j in var_c:
if train_X[j].dtypes=='object':
train_X[j]=train_X[j].str.replace(',','').astype(int)
test_X[j]=test_X[j].str.replace(',','').astype(int)

train_X.dtypes

#%%
from woe2 import WoE
#由于数据极度右偏,分箱后出现多个节点都0的情况,报错。
#这里的处理方法是修改老师给的woe包中cuts, bins = pd.qcut(df["X"].rank(method='first').values, self.qnt_num, retbins=True, labels=False)
#使相同节点整合到一起

iv_c = {}
for i in var_c:
iv_c[i] = WoE(v_type='c',t_type='b',qnt_num=3).fit(train_X[i], train_Y).iv

#%%
sort_iv_c = pd.Series(iv_c).sort_values(ascending=False)
sort_iv_c


# In[10]:

# 以 2%-60% 作为选取变量的阈值
var_c_s = list(sort_iv_c[(sort_iv_c > 0.02)&(sort_iv_c <0.6)].index)
var_c_s


#%%
##根据IV值筛选变量 - 分类变量

iv_d = {}
for i in var_d:
iv_d[i] = WoE(v_type='d').fit(train_X[i].copy(), train_Y.copy()).iv


sort_iv_d=pd.Series(iv_d).sort_values(ascending = False)
sort_iv_d
# In[8]:

# 保留iv值较高的分类变量
var_d_s = ['Buy_Type','Edu']


#%%
train_X = train_X[var_c_s + var_d_s].copy()
train_Y = train_data[y].copy()

test_X = test_X[var_c_s + var_d_s].copy()
test_Y = test_data[y].copy()
#%%
# # 针对每个变量的E(探索)阶段

# ## 对连续变量的统计探索
train_X[var_c_s].describe().T

#%%
# 利用众数减去中位数的差值除以四分位距来查找是否有可能存在异常值
abs((train_X[var_c_s].mode().iloc[0,] - train_X[var_c_s].median()) /
(train_X[var_c_s].quantile(0.75) - train_X[var_c_s].quantile(0.25)))
#%%
# 对嫌疑最大的几个变量进行可视化分析

import matplotlib.pyplot as plt
for n in var_c_s:
plt.hist(train_X[n], bins=20)
plt.title(n)
plt.show()

#%%
# ## 对分类变量的统计探索
#查看是否分类过多
# In[15]:
train_X["Buy_Type"].value_counts()
train_X["Edu"].value_counts()

# In[16]:

# # 针对有问题的变量进行修改的M(修改)阶段

#查看缺失值,结果无缺失
1 - (train_X.describe().T["count"]) / len(train_X)


#%%
# 对分类变量进行woe转换

# 创建一个列表,用来保存所有的建模数据清洗的相关信息
DATA_CLEAN = []

train_X_rep = train_X.copy()
test_X_rep = test_X.copy()

for i in var_d_s:
train_X_rep[i+"_woe"] = WoE(v_type='d').fit_transform(train_X_rep[i],train_Y)

#%%
# 将woe转换的过程保存.3、4
Buy_Type_woe = train_X_rep[["Buy_Type","Buy_Type_woe"]].drop_duplicates().set_index("Buy_Type").to_dict()
Edu_woe = train_X_rep[["Edu","Edu_woe"]].drop_duplicates().set_index("Edu").to_dict()

DATA_CLEAN.append(Buy_Type_woe)
DATA_CLEAN.append(Edu_woe)


test_X_rep['Buy_Type'] = test_X_rep["Buy_Type"].map(Buy_Type_woe['Buy_Type_woe'])

#由于test集中Edu出现了train集中没有的值:1,2,4,5。这里暂将其做同3的处理
test_X_rep['Edu'] = test_X_rep['Edu'].str.replace('1|2|4|5','3')
test_X_rep['Edu'] = test_X_rep['Edu'].map(Edu_woe['Edu_woe'])

# In[34]:
del train_X_rep["Buy_Type"]
del train_X_rep["Edu"]

# In[35]:
train_X_rep.rename(columns={"Buy_Type_woe":"Buy_Type","Edu_woe":"Edu"},inplace=True)


# In[36]:
# 通过随机森林对变量的重要性进行筛选
import sklearn.ensemble as ensemble

rfc = ensemble.RandomForestClassifier(criterion='entropy', n_estimators=3, max_features=0.5, min_samples_split=5)
rfc_model = rfc.fit(train_X_rep, train_Y)
rfc_model.feature_importances_
rfc_fi = pd.DataFrame()
rfc_fi["features"] = list(train_X_rep.columns)
rfc_fi["importance"] = list(rfc_model.feature_importances_)
rfc_fi=rfc_fi.set_index("features",drop=True)
var_sort = rfc_fi.sort_values(by="importance",ascending=False)
var_sort.plot(kind="bar")

#%%
# 以 2% 作为选取变量的阈值
var_x = list(var_sort.importance[var_sort.importance > 0.02].index)
var_x

#%%
# ## 解释变量分布转换
# 查看解释变量的分布情况

for i in var_x:
print(i)
plt.hist(train_X_rep[i], bins=20)
plt.show()

# In[39]:
#查看变量偏离度
skew_var_x = {}
for i in var_x:
skew_var_x[i] = abs(train_X_rep[i].skew())

skew = pd.Series(skew_var_x).sort_values(ascending=False)
skew


# In[40]:

# 将偏度大于1的变量进行对数运算
var_x_ln = skew.index[skew > 1]
var_x_ln


# In[41]:
# 加入数据清洗.5
DATA_CLEAN.append({"val_x_ln":var_x_ln})

# In[42]:


for i in var_x_ln:
if min(train_X_rep[i]) <= 0:
train_X_rep[i] =np.log(train_X_rep[i] + abs(min(train_X_rep[i])) + 0.01)
test_X_rep[i] =np.log(test_X_rep[i] + abs(min(test_X_rep[i])) + 0.01)
else:
train_X_rep[i] =np.log(train_X_rep[i])
test_X_rep[i] =np.log(test_X_rep[i])


# In[43]:


skew_var_x = {}
for i in var_x:
skew_var_x[i]=abs(train_X_rep[i].skew())

skew = pd.Series(skew_var_x).sort_values(ascending=False)
skew

#%%
# ## 变量压缩

from sklearn import preprocessing
pcadata = preprocessing.scale(train_X_rep)
test_pcadata=preprocessing.scale(test_X_rep)
# - 4、使用sklearn的主成分分析,用于判断保留主成分的数量

# In[5]:
from sklearn.decomposition import PCA
'''
此处作主成分分析,主要是进行冗余变量的剔出,因此注意以下两个原则:
1、保留的变量个数尽量多,累积的explained_variance_ratio_尽量大,比如阈值设定为0.95
2、只剔出单位根非常小的变量,比如阈值设定为0.2
'''
pca=PCA(n_components=14)
pca.fit(pcadata)
print(pca.explained_variance_)
print(pca.explained_variance_ratio_)#保留11个主成分
variance=pca.explained_variance_
ratio=pca.explained_variance_ratio_

#%%
from VarSelec import Var_Select
#Var_Select(orgdata, k,alphaMin=10, alphaMax=20, alphastep=0.2)

train_X_rep_reduc = Var_Select(train_X_rep, k=11, alphaMin=0.1,alphaMax=200, alphastep=0.5)
train_X_rep_reduc.head()

#如果报best_alpha没有定义的错误,请扩大alphaMax的取值
# In[46]:
train_X_rep_reduc_corr=train_X_rep_reduc.corr()

# 最后选择的变量为
list(train_X_rep_reduc.columns)

#%%

# 添加清洗.6
DATA_CLEAN.append({"final_var":list(train_X_rep_reduc.columns)})

test_X_rep_reduc=test_X_rep[DATA_CLEAN[3]['final_var']]
#%%
train_X_rep_reduc.head()

#%%
# # 建立逻辑回归模型M(建模)阶段

# ## 模型训练
# - 使用全部变量进行logistic回归

from sklearn import preprocessing
min_max_scaler = preprocessing.MinMaxScaler()

train_X_sc = min_max_scaler.fit_transform(train_X_rep_reduc)
test_X_sc = min_max_scaler.fit_transform(test_X_rep_reduc)
train_X_sc


# In[53]:


import sklearn.linear_model as linear_model
logistic_model = linear_model.LogisticRegression(class_weight = None,
dual = False,
fit_intercept = True,
intercept_scaling = 1,
penalty = 'l1',
random_state = None,
tol = 0.001)


# In[54]:


from sklearn.model_selection import ParameterGrid, GridSearchCV

C = np.logspace(-3,0,20,base=10)

param_grid = {'C': C}

clf_cv = GridSearchCV(estimator=logistic_model,
param_grid=param_grid,
cv=5,
scoring='roc_auc')

clf_cv.fit(train_X_sc, train_Y)


# In[55]:


logistic_model = linear_model.LogisticRegression(C=clf_cv.best_params_["C"],
class_weight=None,
dual=False,
fit_intercept=True,
intercept_scaling=1,
penalty='l1',
random_state=None,
tol=0.001)
logistic_model.fit(train_X_sc, train_Y)


# In[59]:
logistic_model.coef_#0,1,2,7变量剔除
#coe=logistic_model.coef_

train_X_rep_reduc=train_X_rep_reduc.drop(['R1m_Trd3_Cns_Amt', 'Ovs_Cns_Cnt', 'R12m_Max_Csh_Amt', 'Age'],1)
test_X_rep_reduc=test_X_rep_reduc.drop(['R1m_Trd3_Cns_Amt', 'Ovs_Cns_Cnt', 'R12m_Max_Csh_Amt', 'Age'],1)

# In[60]:

import statsmodels.api as sm
import statsmodels.formula.api as smf

model=train_X_rep_reduc.join(train_Y)

formula = "Evt_Flg ~ " + "+".join(train_X_rep_reduc)
lg_m = smf.glm(formula=formula, data=model,
family=sm.families.Binomial(sm.families.links.logit)).fit()
lg_m.summary()

#%%

# # 模型验证A(验证)阶段

# ## 对逻辑回归模型进行评估

test_est = logistic_model.predict(test_X_sc)
train_est = logistic_model.predict(train_X_sc)


# In[59]:


test_est_p = logistic_model.predict_proba(test_X_sc)[:,1]
train_est_p = logistic_model.predict_proba(train_X_sc)[:,1]


# In[60]:
# - 目标样本和非目标样本的分数分布

import seaborn as sns
red, blue = sns.color_palette("Set1",2)

sns.kdeplot(test_est_p[test_Y==1], shade=True, color=red)
sns.kdeplot(test_est_p[test_Y==0], shade=True, color=blue)


# - ROC曲线

# In[66]:
from sklearn import metrics
import matplotlib.pyplot as plt
fpr_test, tpr_test, th_test = metrics.roc_curve(test_Y, test_est_p)
fpr_train, tpr_train, th_train = metrics.roc_curve(train_Y, train_est_p)
plt.figure(figsize=[6,6])
plt.plot(fpr_test, tpr_test, color=blue)
plt.plot(fpr_train, tpr_train, color=red)
plt.title('ROC curve')
print('AUC = %6.4f' %metrics.auc(fpr_test, tpr_test))


# In[67]:


test_x_axis = np.arange(len(fpr_test))/float(len(fpr_test))
train_x_axis = np.arange(len(fpr_train))/float(len(fpr_train))
plt.figure(figsize=[6,6])
plt.plot(fpr_test, test_x_axis, color=blue)
plt.plot(tpr_test, test_x_axis, color=blue)
plt.plot(fpr_train, train_x_axis, color=red)
plt.plot(tpr_train, train_x_axis, color=red)
plt.title('KS curve')

推荐 0
本文由 行者无疆01 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册