python数据分析电信_基于Python的电信客户流失分析和预测⼀、项⽬背景
分析⼯具:本项⽬主要使⽤pandas, matplotlib, aborn, sklearn等Python数据分析库,并在Jupyter Lab环境下运⾏。为了节省篇幅,本⽂不展⽰详细代码,仅展⽰关键步骤的代码。
项⽬流程如下:
⼆、业务流程+分析⽬的
根据kaggle上的数据集介绍,该数据集共有21个字段,7043条记录。每条记录表⽰⼀个客户。
通过理解字段,将字段分为4类,包括客户的基本信息、开通的服务信息、付费信息以及流失标签。基本信息包括性别、年龄(以是否⽼年⼈来区分)、是否有合伙伙伴(可以理解为⼯作伙伴或者朋友之类的)以及家属。观察服务信息,可以分为两类基本服务:电话+⽹络服务,这两类基本服务⼜有各⾃的增值服务,如:多线电话、⽹络安全、在线备份等。此外还有⽀付⽅式、消费⾦额等付费信息。
虽然字段不多,但我们可以将这些字段提炼成⼀个业务流程,或者说⼀个“故事”,并依据业务流程提出问题,这是数据分析中基础⽽⼜核⼼的思维。
业务流程
根据业务流程,我们提出分析⽬的:
分析⽬的
1、构建客户画像,深⼊了解客户了解客户的性别、年龄等特征是如何分布的?
这些客户会选择什么类型的服务,合同期限以及⽀付⽅式?
不同类型的客户,他们消费能⼒有什么区别?
2、分析客户流失的原因,并建⽴客户流失预警模型哪些特征与客户是否流失息息相关?如何避免客户流失?
提出业务建议
三、数据清洗
导⼊数据
# 导⼊数据
df = pd.read_csv('datats_13996_18858_WA_Fn-UC_-Telco-Customer-Churn.csv')业的成语开头
df.info()
# 输出
RangeIndex: 7043 entries, 0 to 7042
Data columns (total 21 columns):
customerID 7043 non-null object
gender 7043 non-null object
SeniorCitizen 7043 non-null int64
Partner 7043 non-null object
Dependents 7043 non-null object
tenure 7043 non-null int64
PhoneService 7043 non-null object
MultipleLines 7043 non-null object
InternetService 7043 non-null object
OnlineSecurity 7043 non-null object
OnlineBackup 7043 non-null object
DeviceProtection 7043 non-null object
TechSupport 7043 non-null object
StreamingTV 7043 non-null object
StreamingMovies 7043 non-null object
Contract 7043 non-null object
PaperlessBilling 7043 non-null object
PaymentMethod 7043 non-null object
MonthlyCharges 7043 non-null float64
TotalCharges 7043 non-null object
Churn 7043 non-null object
dtypes: float64(1), int64(2), object(18)
memory usage: 1.1+ MB
导⼊数据后,发现这个数据集⼗分⼯整,乍⼀看似乎没有缺失值,但仔细查看会发现,TotalCharges总消费⾦额这⼀列,本应该和MonthlyCharges⼀样是float类型,但现在是object类型,说明有部分记录被pandas识别为字符串了。
出师表原文及译文df['TotalCharges'].value_counts()
# 输出
20.2 11
11
三角计算器
19.75 9
19.9 8
20.05 8
..
2431.95 1
1325.85 1
188.1 1
3058.15 1
191.35 1
梨汁的功效与作用
Name: TotalCharges, Length: 6531, dtype: int64
可以看到,有11⾏记录是空⽩字符串,pandas没有将其识别为缺失值,我们将其替换为空值。
# 处理TotalCharges女儿婚礼父亲致辞简短大气
df['TotalCharges'].replace(' ', np.nan, inplace=True)
df['TotalCharges'] = df['TotalCharges'].astype('float')
缺失值处理
# 缺失值处理
mis_val_df = df[df['TotalCharges'].isnull()]
mis_val_df.loc[:, ['tenure','MonthlyCharges','TotalCharges']]
观察这11个客户,发现有且只有他们的注册⽉数为0,由此猜测这部分客户由于是新注册⽤户,注册⽉数在1个⽉之内,所以导致TotalCharges没有记录,我们将其补充各⾃对应的MonthlyCharges。
df['TotalCharges'].fillna(df['MonthlyCharges'], inplace=True)
重复值处理
# 重复值处理
df.duplicated().sum()
# 输出
本数据集没有重复值。
异常值处理
# 异常值处理,查看描述性统计
df.describe([0.01,0.25,0.5,0.75,0.99]).T
查看数值型特征的描述性统计,没有发现异常值。
四、可视化分析总流失率
本数据集中,⼀共有27%的客户流失,即总流失率为27%,数据集没有给出明确的时间范围,但从直觉上来看,这个流失率不算低。
1、客户基本信息
穷开心歌词
左图为不同性别和年龄的⼈数,可以看出男⼥⼈数⼏乎五五开,⽽⽼年⼈与年轻⼈约为1:6。右图为不同性别和年龄组合的流失率,⽆论男性还是⼥性,⽼年⼈的流失率都达到了40%+,⼏乎是年轻⼈的2倍。猜测⽼年⼈由于对电信服务需求不⾼或者不熟悉电⼦设备的操作等原
因,相较年轻⼈更容易流失。
左图表⽰有⽆伙伴、家属的客户⼈数分布,右图表⽰对应的流失率。由图可知,⽆伙伴、⽆家属的客户⼈数⾼达3000+,近总⼈数的⼀半,⽽且其流失也⾼达34%。这是⼀个值得注意的信号:虽然⽆伙伴、⽆家属的客户,其通话需求⽐较少,但由于这部分客户⼈数占⽐近50%,若能挖缺其潜在需求(如⽹络需求),并降低他们的流失率,则可有效降低运营商的成本。
⼩结是否⽼年⼈、有⽆伙伴、有⽆亲属对客户的流失率有巨⼤的影响,接下来,我们将依据这些特征,对客户进⾏分层。从⽽分析不同类型的客户会选择什么服务,以及寻找与客户流失率相关的其他因素。
# 新增计算列:有⽆联系⼈
df['Contact'] = (df['Partner'] + df['Dependents']).map(lambda x: 'Yes' if 'Yes' in x el 'No')
# ⽤户分层:有⽆联系⼈+是否⽼年⼈
乾隆是几阿哥
def CustomerType(df):
need_df = df[['SeniorCitizen','Contact']]
df['CustomerType'] = [' ' for i in range(len(df))]
for row in need_df.iterrows():
index = row[0]
row_content = row[1]
if row_content[0] == 0 and row_content[1] == 'Yes':
df['CustomerType'][index] = 'young_has_contact'
elif row_content[0] == 0 and row_content[1] == 'No':
决心书格式df['CustomerType'][index] = 'young_no_contact'
elif row_content[0] == 1 and row_content[1] == 'Yes':
df['CustomerType'][index] = 'old_has_contact'
el:
df['CustomerType'][index] = 'old_no_contact'
return df
df = CustomerType(df)
为了简化分析,新增计算列:Contact(是否有联系⼈),只要客户有伙伴或者亲属,就算有联系⼈,否则算⽆。根据Contact和SeniorCitizen,对⽤户分层,再新增⼀个计算列CustomerType(客户类型)
2、客户服务信息
先看下这四类⼈群的流失率
和我们在上⼀步分析的⼀致,孤独⽼⼈的流失率是最⾼的,接近50%了!
2.1 基本服务+合同期限
通过观察数据集,会发现基本服务(电话+⽹络)中,客户必须⾄少开通1种,不妨增加计算列:服务类型,同时看看不同类型的客户会选择什么类型的服务。
# 新增计算列:服务类型
def ServiceType(df):
rvices = [*zip(df['PhoneService'],df['InternetService'])]
df['ServiceType'] = [' ' for i in range(len(df))]
for i, rvice in enumerate(rvices):
if rvice[0] == 'No' and rvice[1] != 'No':
df['ServiceType'][i] = 'Only Internet'
elif rvice[0] == 'Yes' and rvice[1] == 'No':
df['ServiceType'][i] = 'Only Phone'
el:
df['ServiceType'][i] = 'Both'
return df
df = ServiceType(df)
# 这些⽤户如何选择服务与合同期限
plt.figure(figsize=(10,8))
customer_types = ['young_has_contact','young_no_contact','old_no_contact','old_has_contact']
for i, customer_type in enumerate(customer_types):
plot_df = df[df['CustomerType'] == customer_type]
plot_ries = plot_df['ServiceType'].value_counts()
plt.subplot(2,2,i+1)
plt.pie(plot_ries.values, labels=plot_ries.index, autopct='%1.0f%%')
plt.title(customer_type)
由图可见,4种客户中,60-90%的客户会选择同时开通2种基本服务,⽽且出乎意料的是,86%以上的⽼年⼈,⽆论有⽆联系⼈,两种服务都开通了,这推翻了我们之前对“⽼年⼈对电信需求不⾼”的猜测。
接下来看看是否开通电话、⽹络服务与流失率的关系
可见,电话服务队流失率⼏乎没有影响,⽽⽹络服务中,选择Fiber optic(光纤)类型的客户,流失率⾼达40%,将这个特征加⼊关键因素吧!然后,我们向⾃⼰提出⼀个疑问:为什么光纤客户流失率这么⾼?让我们带着这个问题继续分析。
接下来看看,选择不同类型服务的客户中,他们如何选择套餐有效期(合同期限)?
由图可知,⽆论开通了电话还是⽹络服务,⼊门级别的包⽉套餐仍是⼈们的⾸选,有40-60%的客户会优先包⽉套餐。不可避免地,⼀旦包⽉套餐过期,客户就很容易流失,流失率⾼达42%。因此,合同期限也属于⼀个关键因素!
2.2 增值服务
电话增值服务:多线服务
由左图,42%的客户开通了多线服务,90%的客户开通了电话服务,由此计算得多线服务转化率 = 多线服务开通率 / 电话服务开通率 = 42/90% = 47%。由右图,是否开通多线服务,对流失率没有太⼤的影响,因此多线服务不算关键因素。
⽹络增值服务:⽹络安全、在线备份等
⽹络服务⼀共有两个类型:光纤和DSL,每个客户只能选择⼀种类型,同时有多达6种增值服务可以开通,我们先来了解各增值服务的转化率及流失率。
# 各项⽹络服务转化率&流失率
cols = ['OnlineSecurity','OnlineBackup','DeviceProtection','TechSupport','StreamingTV','StreamingMovies']
trans_pct, fo_trans_pct, dsl_trans_pct, lost_pct = [],[],[],[]
for col in cols:
is_count = sum(df['InternetService'] != 'No')
fo_count = sum(df['InternetService'] == 'Fiber optic')
dsl_count = sum(df['InternetService'] == 'DSL')
trans_pct.append(round(sum(df[col] == 'Yes') / is_count * 100, 1))
fo_trans_pct.append(round(len(df[(df[col] == 'Yes') & (df['InternetService'] =='Fiber optic')]) / fo_count * 100, 1))
dsl_trans_pct.append(round(len(df[(df[col] == 'Yes') & (df['InternetService'] =='DSL')]) / dsl_count * 100, 1))
lost_pct.append(round(df[df[col] == 'Yes']['Churn'].mean(), 2))
internet_df = pd.DataFrame(index=cols,
columns=['总⽹络服务转化率(%)','光纤⽹络转化率(%)','dsl⽹络转化率(%)','流失率(%)'],