Kaggle数据集Telco-Customer-Churn.csv特征相关性分析(用LabelEncoder编码)
数据源:
https://www.kaggle.com/blastchar/telco-customer-churn
字段解释:
用户属性
0 customerID 客户编号
1 gender 性别
2 SeniorCitizen 是否是老年人
3 Partner 是否单身
4 Dependents 经济是否独立
5 tenure 已使用月份数
开通服务情况
6 PhoneService 电话业务
7 MultipleLines 多线业务
8 InternetService 网络服务业务
9 OnlineSecurity 网络安全业务
10 OnlineBackup 网络备份业务
11 DeviceProtection 设备保护业务
12 TechSupport 技术支持业务
13 StreamingTV 网络电视业务
14 StreamingMovies 网络电影业务
用户账户信息
15 Contract 合同方式
16 PaperlessBilling 电子账单
17 PaymentMethod 支付方式
18 MonthlyCharges 月费用
19 TotalCharges 总费用
研究目标
20 Churn 客户是否流失
相关性分析:
这里推荐用Spyder,因为Spyder的变量管理器中查看数据比较方便。当然用JupyterNotebook也很好。
初始设置:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid',font_scale=1.3)
plt.rcParams['font.family']='SimHei'
plt.rcParams['axes.unicode_minus']=False
import warnings
warnings.filterwarnings('ignore')
sns.set(style="whitegrid")
pd.set_option("display.max_columns", 36)
导入数据
df=pd.read_csv(r'E:\WA_Fn-UseC_-Telco-Customer-Churn.csv',header=0)
#查看数据大小
df.shape
#共有7043条记录,21个字段
#查看各字段属性
df.info()
#21个字段中有三个是数值型:SeniorCitizen、tenure 、MonthlyCharges
#其他均为字符型
#预览数据
df.head()
#这里从数据内容上看:TotalCharges表示总费用,应该是数值型,但实际却是字符型,所以后面应该做类型装换。
#查看缺失值
df.isnull().sum()
#并无缺失值
#查看是否有重复记录
df.duplicated().sum()
#并无重复记录
处理数据
#类型转换
df['TotalCharges']=df['TotalCharges'].apply(pd.to_numeric, errors='ignore')
df['TotalCharges'].dtype
#经过类型转换后再查看是否有空值或空格
df['TotalCharges'].str.isspace().sum()
#将空格转换为空值
df['TotalCharges'].replace(' ',np.nan,inplace =True)
df['TotalCharges'].isnull().sum()
类型转换之后TotalCharges列有11个缺失值。
#选择中位数填充
df.fillna({'TotalCharges':df['TotalCharges'].median()},inplace=True)
#再次确认是否还有空值
df.isnull().sum()
相关性分析
转换后的数据中有4个字段是数值型,还有17个字段是字符型(分类型),既然要做相关性分析,那么分类型数据一定要转换为数值型才好,这就需要对这些字段进行编码。
常见的编码有pd.factorize(x)[0],但这里要注意:pd.factorize是将数据字段中最先遇到的值排成0,之后依次增大。这就出现了一个问题:我们通常用0、1、2代表字母A、B、C。这就是编码。但用了pd.factorize,假设第一条数据是C那么他就将C编码为0,后面A和B编成了1,2,后续的数据都按此编码。这就造成编码值与实际值不能对应起来,产生“失真”,导致最后的相关性分析的结果完全是错误的。
第二种编码方式:LabelEncoder(按字母顺序编码)
能直接用吗?不能,因为按照字母顺序排的值不一定符合现实中的顺序。比如one、two、three按字母顺序编码是1,3,2,这完全是错误的。
那如何能按照实际意义来给分类值编码呢?
这里需要逐个分类字段分析下,先看一下各分类变量的值有哪些:
for i in df.columns:
if df[i].dtype == 'O' and i != 'customerID':
lst = list(df[i].unique())
print(i,'——————',lst)
gender — [‘Female’, ‘Male’]
Partner — [‘Yes’, ‘No’]
Dependents — [‘No’, ‘Yes’]
PhoneService — [‘No’, ‘Yes’]
MultipleLines — [‘No phone service’, ‘No’, ‘Yes’]
InternetService — [‘DSL’, ‘Fiber optic’, ‘No’]
OnlineSecurity — [‘No’, ‘Yes’, ‘No internet service’]
OnlineBackup — [‘Yes’, ‘No’, ‘No internet service’]
DeviceProtection — [‘No’, ‘Yes’, ‘No internet service’]
TechSupport — [‘No’, ‘Yes’, ‘No internet service’]
StreamingTV — [‘No’, ‘Yes’, ‘No internet service’]
StreamingMovies — [‘No’, ‘Yes’, ‘No internet service’]
Contract — [‘Month-to-month’, ‘One year’, ‘Two year’]
PaperlessBilling — [‘Yes’, ‘No’]
PaymentMethod — [‘Electronic check’, ‘Mailed check’, ‘Bank transfer (automatic)’, ‘Credit card (automatic)’]
Churn — [‘No’, ‘Yes’]
这里gender是无序分类值,所以直接按照首字母编码即可。
PaymentMethod我们并不知道这些不同支付方式之间的难易程度如何或成本高低所以直接按照首字母编码。
Partner、Dependents、PhoneService、PaperlessBilling、Churn字段内容只包含’No’和’Yes’,他们是有序分类值,实际生活中我们一般用0代表否,用1代表是,这里No和Yes恰好符合字母顺序,所以直接编码即可。
Contract 字段分为’Month-to-month’, ‘One year’, ‘Two year’,即月付、一年付、两年付,符合字母顺序,直接编码即可。
MultipleLines、
InternetService 、
OnlineSecurity 、
OnlineBackup 、
DeviceProtection、
TechSupport 、
StreamingTV、
StreamingMovies、
InternetService字段分为:‘DSL’, ‘Fiber optic’, ‘No’。
'No’代表无互联网服务,应该编为0
光纤比DSL高级一些,所以DSL编为1
光纤编为2
这里No字母顺序不符合编码,所以我们对其做修改让其编码排在另外二者之前,从ASCII表来看空格排在字母之前,所以我们给‘No’前加一个空格变为“ No”这样就能编码为0了。
MultipleLines字段分为:‘No phone service’, ‘No’, ‘Yes’
这里就要从业务逻辑上排个序了,首先这一项是多线业务,三个值分别代表无电话服务、无多线业务、有多线业务。三者按程度排名,很自然’No phone service’应该排首位,所以要在其之前加一个空格。
‘OnlineSecurity’,‘OnlineBackup’,‘DeviceProtection’,‘TechSupport’,‘StreamingTV’,‘StreamingMovies’这几个字段都包含’No internet service’,按照业务逻辑,这一项应该排首位。
综上:我们开始修改。
df_encode = df.copy()
df_encode['MultipleLines'].loc[df_encode['MultipleLines'] == 'No phone service'] = ' No phone service'
df_encode['InternetService'].loc[df_encode['InternetService'] == 'No'] = ' No'
name_list = ['OnlineSecurity','OnlineBackup','DeviceProtection','TechSupport','StreamingTV','StreamingMovies']
def repl_(name,v):
df_encode[name].loc[df_encode[name] == v] = ' '+v
for name in name_list:
repl_(name,'No internet service')
由于’customerID’只是一个编号没有实际意义,所以可以不用分析。
剩下的字段我们按照业逻辑自定义排个序:
new_order = ['SeniorCitizen', 'Dependents','Partner',
'PhoneService', 'MultipleLines', 'InternetService',
'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport',
'StreamingTV', 'StreamingMovies', 'Contract', 'PaymentMethod', 'PaperlessBilling',
'MonthlyCharges', 'TotalCharges', 'tenure','Churn']
df_encode = df_encode.loc[:,new_order]
编码并求相关性矩阵:
for x in df_encode.columns:
df_encode[x] = LabelEncoder().fit_transform(df_encode[x])
df_encode_corr = df_encode.corr()
生成热图:
plt.figure(figsize=(17,13))
ax = sns.heatmap(df_encode_corr, xticklabels=df_encode_corr.columns, yticklabels=df_encode_corr.columns,
linewidths=0.2, cmap="RdYlGn_r",annot=round(df_encode_corr,3)[df_encode_corr.abs()>=0.2].fillna('').astype('str'),fmt="s")
# 这里过滤掉了绝对值小于0.2的相关性系数
# ,fmt=".2f"
# ,mask = (df_encode_corr.abs()<=0.3)
plt.title("Correlation between variables")
结论:
1.与网络相关的特征之间基本呈现强正相关。
2.网络特征与费用呈正相关。
3.是否单身与经济是否独立呈正相关。
4.流失率和性别无关(这里在本图中没有加上gender,感兴趣的可以加上)
5.流失率和合同时间长短、使用月份数呈强负相关。
上一篇: PAT_乙_1004成绩排名
下一篇: 微信公众号测试账号自定义菜单的实例代码