客户流失

客户流失

“流失率”是描述客户离开或停止支付产品或服务费率的业务术语。这在许多企业中是一个关键的数字,因为通常情况下,获取新客户的成本比保留现有成本(在某些情况下,贵5到20倍)。

因此,了解保持客户参与度是非常宝贵的,因为它是开发保留策略和推出旨在阻止客户走出门的运营实践的合理基础。因此,公司越来越感兴趣开发更好的流失检测技术,导致许多人寻求数据挖掘和机器学习以获得新的和创造性的方法。

这是一篇关于使用Python对客户流失进行建模的文章。 
下面开始介绍一下具体的实现步骤:

数据集

我将使用的数据集是一个长期的电信客户数据集,您可以在这里下载

数据很简单。 每行代表一个预订的电话用户。 每列包含客户属性,例如电话号码,在一天中不同时间使用的通话分钟,服务产生的费用,生命周期帐户持续时间以及客户是否仍然是客户。

from __future__ import division
import pandas as pd
import numpy as np

churn_df = pd.read_csv('churn.csv')
col_names = churn_df.columns.tolist()

print "Column names:"
print col_names

to_show = col_names[:6] + col_names[-6:]

print "\nSample data:"
churn_df[to_show].head(6)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

列名称:

  ['State', 'Account Length', 'Area Code', 'Phone', "Int'l Plan", 'VMail Plan', 'VMail Message', 'Day Mins', 'Day Calls', 'Day Charge', 'Eve Mins', 'Eve Calls', 'Eve Charge', 'Night Mins', 'Night Calls', 'Night Charge', 'Intl Mins', 'Intl Calls', 'Intl Charge', 'CustServ Calls', 'Churn?']
  • 1
    State   Account Length  Area Code   Phone   Int'l Plan  VMail Plan  Night Charge    Intl Mins   Intl Calls  Intl Charge CustServ Calls  Churn?
0   KS  128 415 382-4657    no  yes 11.01   10.0    3   2.70    1   False.
1   OH  107 415 371-7191    no  yes 11.45   13.7    3   3.70    1   False.
2   NJ  137 415 358-1921    no  no  7.32    12.2    5   3.29    0   False.
3   OH  84  408 375-9999    yes no  8.86    6.6 7   1.78    2   False.
4   OK  75  415 330-6626    yes no  8.41    10.1    3   2.73    3   False.
5   AL  118 510 391-8027    yes no  9.18    6.3 6   1.70    0   False.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

以下代码简单地删除不相关的列,并将字符串转换为布尔值(因为模型不处理“yes”和“no”非常好)。 其余的数字列保持不变。

# Isolate target data
churn_result = churn_df['Churn?']
y = np.where(churn_result == 'True.',1,0)

# We don't need these columns
to_drop = ['State','Area Code','Phone','Churn?']
churn_feat_space = churn_df.drop(to_drop,axis=1)

# 'yes'/'no' has to be converted to boolean values
# NumPy converts these from boolean to 1. and 0. later
yes_no_cols = ["Int'l Plan","VMail Plan"]
churn_feat_space[yes_no_cols] = churn_feat_space[yes_no_cols] == 'yes'

# Pull out features for future use
features = churn_feat_space.columns

X = churn_feat_space.as_matrix().astype(np.float)

# This is important
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X = scaler.fit_transform(X)

print "Feature space holds %d observations and %d features" % X.shape
print "Unique target labels:", np.unique(y)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

许多预测变量关心不同特征的相对大小,即使这些标度可能是任意的。 例如:篮球队每场比赛得分的分数自然比他们的胜率要大几个数量级。 但这并不意味着后者的重要性低100倍。 StandardScaler通过将每个特征归一化到大约1.0到-1.0的范围来修复这个问题,从而防止模型不正确。 嗯,至少出于这个原因。

很好,我现在有一个特征空间’X’和一组目标值’y’。

你的模型有多好?

快递,测试,循环。 机器学习管道应该是静态的。 总是有新的功能设计,新的数据使用,新的分类器考虑每个具有唯一的参数调整。 对于每一个变化,至关重要的是能够问,“新版本比上一版更好吗? 那么我该怎么做呢?

作为一个好的开始,交叉验证将在整个博客中使用。 交叉验证尝试避免过拟合(对同一数据点进行训练和预测),同时仍然为每个观测数据集产生预测。 这是通过在训练一组模型的同时系统地隐藏数据的不同子集来实现的。 在训练之后,每个模型对已隐藏的子集进行预测,模拟多个列车测试分裂。 当正确地完成时,每个观察将具有“公平”对应的预测。

下面是使用scikit-learn库的例子。 
这里写图片描述

from sklearn.cross_validation import KFold

def run_cv(X,y,clf_class,**kwargs):
    # Construct a kfolds object
    kf = KFold(len(y),n_folds=5,shuffle=True)
    y_pred = y.copy()

    # Iterate through folds
    for train_index, test_index in kf:
        X_train, X_test = X[train_index], X[test_index]
        y_train = y[train_index]
        # Initialize a classifier with key word arguments
        clf = clf_class(**kwargs)
        clf.fit(X_train,y_train)
        y_pred[test_index] = clf.predict(X_test)
    return y_pred
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

我决定比较三个相当独特的算法支持向量机,随机森林和k最近邻。 来确定分类器预测正确率。

from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier as RF
from sklearn.neighbors import KNeighborsClassifier as KNN

def accuracy(y_true,y_pred):
    # NumPy interprets True and False as 1. and 0.
    return np.mean(y_true == y_pred)

print "Support vector machines:"
print "%.3f" % accuracy(y, run_cv(X,y,SVC))
print "Random forest:"
print "%.3f" % accuracy(y, run_cv(X,y,RF))
print "K-nearest-neighbors:"
print "%.3f" % accuracy(y, run_cv(X,y,KNN))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

比较结果:

        支持向量机:
           0.918
           随机森林:
           0.943
           K最近邻:
           0.896
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

精确度

测量不是黄金公式,总是为好的模型总是吐出大数字,而对坏模型总是吐出低数字。它们传达了对模型性能的一些情感,并且人类设计者的工作是确定每个数字的有效性。准确性的问题是结果不一定相等。如果我的分类器预测客户会流失,他们没有,这不是最好的,但它是可以原谅的。然而,如果我的分类器预测客户会返回,我没有行动,然后他们搅动…这是真的很糟糕。

我将使用另一个内置的scikit-learn函数来构造混淆矩阵。混淆矩阵是一种可视化由分类器进行的预测的方式,并且仅仅是一个表格,其示出了对于特定类的预测的分布。 x轴表示每个观察的真实类别(如果客户流失或不流失),而y轴对应于模型预测的类别(如果我的分类器表示客户会流失或不流失)。

from sklearn.metrics import confusion_matrix

y = np.array(y)
class_names = np.unique(y)

confusion_matrices = [
    ( "Support Vector Machines", confusion_matrix(y,run_cv(X,y,SVC)) ),
    ( "Random Forest", confusion_matrix(y,run_cv(X,y,RF)) ),
    ( "K-Nearest-Neighbors", confusion_matrix(y,run_cv(X,y,KNN)) ),
]

# Pyplot code not included to reduce clutter
from churn_display import draw_confusion_matrices
%matplotlib inline

draw_confusion_matrices(confusion_matrices,class_names)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

什么是最好的算法?

确定给出概率而不是类的预测变量有多困难。 如果我预测,明天有20%的可能性下雨,我不能消除宇宙的所有可能的结果。 它下雨或不下雨。

有什么帮助的是,预测变量没有做出一个预测,他们正在做3000+。 所以每次我预测一个事件发生20%的时间,我可以看到这些事件实际发生的频率。 这里是我使用熊猫帮助我比较随机森林做的预测和实际结果。

import warnings
warnings.filterwarnings('ignore')

# Use 10 estimators so predictions are all multiples of 0.1
pred_prob = run_prob_cv(X, y, RF, n_estimators=10)
pred_churn = pred_prob[:,1]
is_churn = y == 1

# Number of times a predicted probability is assigned to an observation
counts = pd.value_counts(pred_churn)

# calculate true probabilities
true_prob = {}
for prob in counts.index:
    true_prob[prob] = np.mean(is_churn[pred_churn == prob])
    true_prob = pd.Series(true_prob)

# pandas-fu
counts = pd.concat([counts,true_prob], axis=1).reset_index()
counts.columns = ['pred_prob', 'count', 'true_prob']
counts
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

输出结果: 
这里写图片描述
我们可以看到,随机森林预测75个人将有0.9的可能性的流失,而在现实中,该群体具有大约0.97的速率。

校准和鉴别

使用上面的DataFrame我可以绘制一个非常简单的图形,以帮助可视化概率测量。 x轴表示随机森林分配给一组个体的流失概率。 y轴是该组内实际的搅动速率,每个点相对于组的大小进行缩放。


from ggplot import *
%matplotlib inline

baseline = np.mean(is_churn)
ggplot(counts,aes(x='pred_prob',y='true_prob',size='count')) + \
    geom_point(color='blue') + \
    stat_function(fun = lambda x: x, color='red') + \
    stat_function(fun = lambda x: baseline, color='green') + \
    xlim(-0.05,  1.05) + ylim(-0.05,1.05) + \
    ggtitle("Random Forest") + \
    xlab("Predicted probability") + ylab("Relative frequency of outcome")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里写图片描述
你可能也注意到了我绘制的两行我使用stat_function()。

红线表示给定组的完美预测,或当预测的流失概率等于结果频率时。 绿线显示了基线的流失概率。 对于这个数据集,它约为0.15。

校准是一个相对简单的测量,可以总结如下:事件预计发生60%的时间应该发生60%的时间。 对于所有个人,我预测流失的风险在30%和40%之间,该群体的真实流失率应为约35%。 对于上面的图形,认为它,我的预测是多么接近红线?

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值