MyException - 我的异常网
当前位置:我的异常网» Perl/Python » 教你用Python发现快要流失的客户(附代码、安装教程

教你用Python发现快要流失的客户(附代码、安装教程、学习资源)

www.MyException.Cn  网友分享于:2013-12-18  浏览:0次
教你用Python发现即将流失的客户(附代码、安装教程、学习资源)

作为一名数据分析师,你来到这家跨国银行工作已经半年了。

今天上午,老板把你叫到办公室,面色凝重。

你心里直打鼓,以为自己捅了什么篓子。幸好老板的话让你很快打消了顾虑。

他发愁,是因为最近欧洲区的客户流失严重,许多客户都跑到了竞争对手那里接受服务了。老板问你该怎么办?

你脱口而出“做好客户关系管理啊!”

老板看了你一眼,缓慢地说“我们想知道哪些客户最可能在近期流失”。

没错,在有鱼的地方钓鱼,才是上策。

你明白了自己的任务——通过数据锁定即将流失的客户。这个工作,确实是你这个数据分析师分内的事儿。

你很庆幸,这半年做了很多的数据动态采集和整理工作,使得你手头就有一个比较完备的客户数据集。

下面你需要做的,就是如何从数据中“沙里淘金”,找到那些最可能流失的客户。

可是,该怎么做呢?

你拿出欧洲区客户的数据,端详起来。

客户主要分布在法国、德国和西班牙。

你手里掌握的信息,包括他们的年龄、性别、信用、办卡信息等。客户是否已流失的信息在最后一列(Exited)。

怎么用这些数据来判断顾客是否会流失呢?

以你的专业素养,很容易就判断出这是一个分类问题,属于机器学习中的监督式学习。但是,你之前并没有做过实际项目,该如何着手呢?

别发愁,我一步步给你演示如何用Python和深度神经网络(或者叫“深度学习”)来完成这个分类任务,帮你锁定那些即将流失的客户。

环境

工欲善其事,必先利其器。我们先来安装和搭建环境。

首先是安装Python。

请到这个网址下载Anaconda的最新版本。

请选择左侧的Python 3.6版本下载安装。

其次是新建文件夹,起名为demo-customer-churn-ann,并且从这个链接下载数据,放到该文件夹下。

(注:样例数据来自于匿名化处理后的真实数据集,下载自superdatascience官网。)

打开终端(或者命令行工具),进入demo-customer-churn-ann目录,执行以下命令:

jupyter notebook

浏览器中会显示如下界面:

点击界面右上方的New按钮,新建一个Python 3 Notebook,起名为customer-churn-ann。

准备工作结束,下面我们开始清理数据。

清理

首先,读入数据清理最常用的pandas和numpy包。

import numpy as npimport pandas as pd

从customer_churn.csv里读入数据:

df = pd.read_csv('customer_churn.csv')

看看读入效果如何:

df.head()

这里我们使用了head()函数,只显示前5行。

可以看到,数据完整无误读入。但是并非所有的列都对我们预测用户流失有作用。我们一一甄别一下:

RowNumber:行号,这个肯定没用,删除

CustomerID:用户编号,这个是顺序发放的,删除

Surname:用户姓名,对流失没有影响,删除

CreditScore:信用分数,这个很重要,保留

Geography:用户所在国家/地区,这个有影响,保留

Gender:用户性别,可能有影响,保留

Age:年龄,影响很大,年轻人更容易切换银行,保留

Tenure:当了本银行多少年用户,很重要,保留

Balance:存贷款情况,很重要,保留

NumOfProducts:使用产品数量,很重要,保留

HasCrCard:是否有本行信用卡,很重要,保留

IsActiveMember:是否活跃用户,很重要,保留

EstimatedSalary:估计收入,很重要,保留

Exited:是否已流失,这将作为我们的标签数据

上述数据列甄别过程,就叫做“特征工程”(Feature Engineering),这是机器学习里面最常用的数据预处理方法。如果我们的数据量足够大,机器学习模型足够复杂,是可以跳过这一步的。但是由于我们的数据只有10000条,还需要手动筛选特征。

选定了特征之后,我们来生成特征矩阵X,把刚才我们决定保留的特征都写进来。

X = df.loc[:,['CreditScore', 'Geography', 'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary']]

看看特征矩阵的前几行:

X.head()

显示结果如下:

特征矩阵构建准确无误,下面我们构建目标数据y,也就是用户是否流失。

y = df.Exited

y.head()

0 11 02 13 04 0Name: Exited, dtype: int64

此时我们需要的数据基本上齐全了。但是我们发现其中有几列数据还不符合我们的要求。

要做机器学习,只能给机器提供数值,而不能是字符串。可是看看我们的特征矩阵:

X.head()

显然其中的Geography和Gender两项数据都不符合要求。它们都是分类数据。我们需要做转换,把它们变成数值。

在Scikit-learn工具包里面,专门提供了方便的工具LabelEncoder,让我们可以方便地将类别信息变成数值。

from sklearn.preprocessing import LabelEncoder, OneHotEncoderlabelencoder1 = LabelEncoder()X.Geography= labelencoder1.fit_transform(X.Geography)labelencoder2 = LabelEncoder()X.Gender = labelencoder2.fit_transform(X.Gender)

我们需要转换两列,所以建立了两个不同的labelencoder。转换的函数叫做fit_transform。

经过转换,此时我们再来看看特征矩阵的样子:

X.head()

显然,Geography和Gender这两列都从原先描述类别的字符串,变成了数字。

这样是不是就完事大吉了呢?显然,Geography和Gender这两列都从原先描述类别的字符串,变成了数字。

不对,Gender还好说,只有两种取值方式,要么是男,要么是女。我们可以把“是男性”定义为1,那么女性就取值为0。两种取值只是描述类别不同,没有歧义。

而Geography就不同了。因为数据集里面可能的国家地区取值有3种,所以就转换成了0(法国)、1(德国)、2(西班牙)。问题是,这三者之间真的有序列(大小)关系吗?

答案自然是否定的。我们其实还是打算用数值描述分类而已。但是取值有数量的序列差异,就会给机器带来歧义。它并不清楚不同的取值只是某个国家的代码,可能会把这种大小关系带入模型计算,从而产生错误的结果。

解决这个问题,我们就需要引入OneHotEncoder。它也是Scikit-learn提供的一个类,可以帮助我们把类别的取值转变为多个变量组合表示。

咱们这个数据集里,可以把3个国家分别用3个数字组合来表示。例如法国从原先的0,变成(1, 0, 0),德国从1变成(0, 1, 0),而西班牙从2变成(0, 0, 1)。

这样,再也不会出现0和1之外的数字来描述类别,从而避免机器产生误会,错把类别数字当成大小来计算了。

特征矩阵里面,我们只需要转换国别这一列。因为它在第1列的位置(从0开始计数),因而categorical_features只填写它的位置信息。

onehotencoder = OneHotEncoder(categorical_features = [1])X = onehotencoder.fit_transform(X).toarray()

这时候,我们的特征矩阵数据框就被转换成了一个数组。注意所有被OneHotEncoder转换的列会排在最前面,然后才是那些保持原样的数据列。

我们只看转换后的第一行:

X[0]

array([ 1.00000000e+00, 0.00000000e+00, 0.00000000e+00, 6.19000000e+02, 0.00000000e+00, 4.20000000e+01, 2.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.01348880e+05])

这样,总算转换完毕了吧?

没有。

因为本例中,OneHotEncoder转换出来的3列数字,实际上是不独立的。给定其中两列的信息,你自己都可以计算出其中的第3列取值。

好比说,某一行的前两列数字是(0, 0),那么第三列肯定是1。因为这是转换规则决定的。3列里只能有1个是1,其余都是0。

如果你做过多元线性回归,应该知道这种情况下,我们是需要去掉其中一列,才能继续分析的。不然会落入“虚拟变量陷阱”(dummy variable trap)。

我们删掉第0列,避免掉进坑里。

X = np.delete(X, [0], 1)

再次打印第一行:

X[0]

array([ 0.00000000e+00, 0.00000000e+00, 6.19000000e+02, 0.00000000e+00, 4.20000000e+01, 2.00000000e+00, 0.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.01348880e+05])

检查完毕,现在咱们的特征矩阵处理基本完成。

但是监督式学习,最重要的是有标签(label)数据。本例中的标签就是用户是否流失。我们目前的标签数据框,是这个样子的。

y.head()

0 11 02 13 04 0Name: Exited, dtype: int64

它是一个行向量,我们需要把它先转换成为列向量。你可以想象成把它“竖过来”。

y = y[:, np.newaxis]y

array([[1], [0], [1], ..., [1], [1], [0]])

这样在后面训练的时候,他就可以和前面的特征矩阵一一对应来操作计算了。

既然标签代表了类别,我们也把它用OneHotEncoder转换,这样方便我们后面做分类学习。

onehotencoder = OneHotEncoder()y = onehotencoder.fit_transform(y).toarray()

此时的标签变成两列数据,一列代表顾客存留,一列代表顾客流失。

y

array([[ 0., 1.], [ 1., 0.], [ 0., 1.], ..., [ 0., 1.], [ 0., 1.], [ 1., 0.]])

总体的数据已经齐全了。但是我们不能把它们都用来训练。

这就好像老师不应该把考试题目拿来给学生做作业和练习一样。只有考学生没见过的题,才能区分学生是掌握了正确的解题方法,还是死记硬背了作业答案。

我们拿出20%的数据,放在一边,等着用来做测试。其余8000条数据用来训练机器学习模型。

from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)

我们看看训练集的长度:

len(X_train)

8000

再看看测试集的长度:

len(X_test)

2000

确认无误。

是不是可以开始机器学习了?

可以,但是下面这一步也很关键。我们需要把数据进行标准化处理。因为原先每一列数字的取值范围都各不相同,因此有的列方差要远远大于其他列。这样对机器来说,也是很困扰的。数据的标准化处理,可以在保持列内数据多样性的同时,尽量减少不同类别之间差异的影响,可以让机器公平对待全部特征。

我们调用Scikit-learn的StandardScaler类来完成这一过程。

from sklearn.preprocessing import StandardScalersc = StandardScaler()X_train = sc.fit_transform(X_train)X_test = sc.transform(X_test)

注意,我们只对特征矩阵做标准化,标签是不能动的。另外训练集和测试集需要按照统一的标准变化。所以你看,训练集上,我们用了fit_transform函数,先拟合后转换;而在测试集上,我们直接用训练集拟合的结果,只做转换。

X_train

array([[-0.5698444 , 1.74309049, 0.16958176, ..., 0.64259497, -1.03227043, 1.10643166], [ 1.75486502, -0.57369368, -2.30455945, ..., 0.64259497, 0.9687384 , -0.74866447], [-0.5698444 , -0.57369368, -1.19119591, ..., 0.64259497, -1.03227043, 1.48533467], ..., [-0.5698444 , -0.57369368, 0.9015152 , ..., 0.64259497, -1.03227043, 1.41231994], [-0.5698444 , 1.74309049, -0.62420521, ..., 0.64259497, 0.9687384 , 0.84432121], [ 1.75486502, -0.57369368, -0.28401079, ..., 0.64259497, -1.03227043, 0.32472465]])

你会发现,许多列的方差比原先小得多。机器学习起来,会更加方便。

数据清理和转换工作至此完成。

 

阅读全文

文章评论

编程语言是女人
编程语言是女人
那些争议最大的编程观点
那些争议最大的编程观点
10个调试和排错的小建议
10个调试和排错的小建议
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
程序员应该关注的一些事儿
程序员应该关注的一些事儿
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员的鄙视链
程序员的鄙视链
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
我是如何打败拖延症的
我是如何打败拖延症的
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
旅行,写作,编程
旅行,写作,编程
Java程序员必看电影
Java程序员必看电影
程序员和编码员之间的区别
程序员和编码员之间的区别
每天工作4小时的程序员
每天工作4小时的程序员
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
代码女神横空出世
代码女神横空出世
中美印日四国程序员比较
中美印日四国程序员比较
我的丈夫是个程序员
我的丈夫是个程序员
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
如何成为一名黑客
如何成为一名黑客
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
为什么程序员都是夜猫子
为什么程序员都是夜猫子
总结2014中国互联网十大段子
总结2014中国互联网十大段子
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员都该阅读的书
程序员都该阅读的书
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员必看的十大电影
程序员必看的十大电影
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
一个程序员的时间管理
一个程序员的时间管理
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
漫画:程序员的工作
漫画:程序员的工作
鲜为人知的编程真相
鲜为人知的编程真相
 程序员的样子
程序员的样子
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
老程序员的下场
老程序员的下场
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有