@目錄
- 一. 研究背景
- 二. 分析結論與建議
- 三. 任務與實作
- 四. 資料集決議
- 五. 資料分析套餐
- 1.準備作業
- 匯入相關的庫
- 匯入資料集
- 2.資料預處理
- 型別轉換
- 缺失值處理
- 重復值處理
- 3.查看流失情況
- 4.類別特征的描述性分析
- 5.連續型變數的分析
- 差異檢驗-兩樣本t檢驗
- 分箱離散化
- 6.機器學習
- 基模型的建立
- 基模型的初始評分
- 例外值處理
- 資料標準化處理
- 樣本不均衡處理&邏輯回歸
- 網格交叉驗證&KNN
- 決策樹
- 流水線&樸素貝葉斯
- 多層感知器
- 模型得分總結
- 1.準備作業
這是作者自己做的一個資料分析專案,閑暇時間陸陸續續耗時大約2周,通篇采用大量python原始碼撰寫,歡迎一起學習交流,提升自我,
我的CSDN地址:https://blog.csdn.net/weixin_46274061/article/details/107790605
轉載請標明出處,謝謝!
資料集下載地址:https://www.datafountain.cn/datasets/35guide
一. 研究背景
用戶流失預測在機器學習中算是一種比較典型的分類場景,做好用戶的流失預測可以降低營銷成本,留住用戶并且獲得更好的用戶體驗,在三大巨頭的瓜分下,做好營銷運營比重新獲取一個新用戶更節省成本,達到較好的運營回報,如果在傳統分類模式下,通常是通過人工對各個特征進行統計,然后分到合適的類別中,這樣不但會耗費大量的資源,且低效,
二. 分析結論與建議
- 增加套餐福利,解鎖更多權益,如贈送流量,免費看視頻,增設小游戲,貴族制度,
- 加強電話服務質量,設立評分反饋系統,及時跟蹤例外評分
- 加強光纖相關設施的建設,增強網路穩定性
- 鼓勵用戶開通各種服務,如在線安全,在線備份,設備保護,技術支持
- 增加充值返現,充值滿減,發放優惠券的方式,用戶消費達到一定金額解鎖特權
- 針對老年人建議贈送通話時長,提高活躍度
- 針對排名前十的職業根據相應的職業給予相應的優惠和福利,提高用戶的粘性,
三. 任務與實作
我們的任務在于:
1.分析出流失用戶有哪些顯著性特征?
2.找出哪些用戶容易流失?
具體實作內容包括:
能夠對資料進行資料預處理 包括缺失值,例外值,重復值
能夠描述性分析各個特征與流失用戶的占比是否顯著
能夠將連續型變數進行分箱離散化
能夠將離散型特征進行獨熱編碼
能夠建立基模型,將源資料進行標準化
能夠處理樣本不均衡
能夠熟練運用多種分類模型對電信用戶進行預測
分析模型有:邏輯回歸,KNN,樸素貝葉斯,決策樹,多層感知器,
四. 資料集決議
每行代表一個客戶,每列包含元資料列中描述的客戶屬性,
一共7043行資料,21個列,前20個為特征列,最后一個為研究物件,
1 customerID Integer :用戶ID
2 gender String:性別(Female or Male)
3 SeniorCitizen Integer: 老年人(1表示是,0表示不是)
4 Partner String:配偶(Yes or No)
5 Dependents String:家屬(Yes or No)
6 tenure Integer :職位(0~72,共73個職位)
7 PhoneService String:電話服務(Yes or No)
8 MultipleLines String:多線(Yes 、No or No phoneservice 三種)
9 InternetService String:互聯網服務(No, DSL數字網路,fiber optic光纖網路 三種)
10 OnlineSecurity String:在線安全(Yes,No,No internetserive 三種)
11 OnlineBackup String:在線備份(Yes,No,No internetserive 三種)
12 DeviceProtection String:設備保護(Yes,No,No internetserive 三種)
13 TechSupport String:技術支持(Yes,No,No internetserive 三種)
14 StreamingTV String:網路電視(Yes,No,No internetserive 三種)
15 StreamingMovies:網路電影 (Yes,No,No internetserive 三種)
16 Contract String:合同(Month-to-month,One year,Two year 三種)
17 PaperlessBilling String:賬單(Yes or No)
18 PaymentMethod String:付款方式(bank transfer,credit card,electronic check,mailed check 四種)
19 MonthlyCharges Integer :月費用
20 TotalCharges Integer :總費用
21 Churn String:流失(Yes or No)
五. 資料分析套餐
1.準備作業
本文資料源來自網上,資料源除了各大網站可以下載,還可以來自自家公司的資料庫,爬蟲等方式獲取,
匯入相關的庫
# 匯入庫
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from matplotlib import font_manager
import time
sns.set(style="darkgrid", font_scale=1.2)
# plt.rcParams["font.family"] = "SimHei"
plt.rcParams['font.family'] = ['Arial Unicode MS']
plt.rcParams["axes.unicode_minus"] = False
my_font=font_manager.FontProperties(fname=
'/System/Library/Fonts/PingFang.ttc',
size=15)
warnings.filterwarnings("ignore")
from scipy import stats #用于方差分析
# from sklearn.linear_model import LinearRegression #線性回歸模型
from sklearn.model_selection import train_test_split #切分訓練集 測驗集
from sklearn.linear_model import LogisticRegression #邏輯回歸模型
from sklearn.metrics import classification_report #混淆矩陣打分
from sklearn.model_selection import GridSearchCV #網格交叉驗證
from imblearn.over_sampling import SMOTE,ADASYN # 引入SMOTE和ADASYN處理樣本不均衡
from collections import Counter #查看每個類別出現的次數
from sklearn.pipeline import Pipeline #引入流水線
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 評估指標 --正確率 精準率 召回率 F1調和平均值
from sklearn.neighbors import KNeighborsClassifier #KNN分類模型
from sklearn.preprocessing import StandardScaler, MinMaxScaler # StandardScaler:均值標準差標準化 # MinMaxScaler:最小最大值標準化
from sklearn.neural_network import MLPClassifier #多層感知器
匯入資料集
data = https://www.cnblogs.com/lverkou/p/pd.read_csv("WA_Fn-UseC_-Telco-Customer-Churn.csv")
print(data.shape)
data.sample(10)

2.資料預處理
查看資料整體情況
data.info()

型別轉換
我們發現TotalCharges本應該是float64型別,這里卻是object型別,那么需要轉換,
data['TotalCharges'].astype(np.float64)
會報錯 無法轉換,這里用到一個函式:DataFrame.convert_objects( convert_dates = True,convert_numeric = False,convert_timedeltas = True,copy = True )
data["TotalCharges"]=data["TotalCharges"].convert_objects(convert_numeric=True)
data['TotalCharges'].dtype
輸出:dtype('float64') ,轉換完成,
缺失值處理
data.isnull().sum(axis=0)

TotalCharges有11個缺失值,查看資料分布 再確定是洗掉還是填充
print(data["TotalCharges"].skew())
sns.distplot(data["TotalCharges"].dropna())

結果大于0, 屬于右偏 ,當然 ,也能一眼從圖形看出,
缺失值的處理方式有:洗掉,中位數填充,均值填充,眾數填充,
右偏資料我們用中位數填充,兩種方式計算中位數,計算中位數時會剔除缺失值
data["TotalCharges"].median()
或者
np.median(data["TotalCharges"].dropna().values)
輸出為:1397.475
填充缺失值
data.fillna({"TotalCharges":data["TotalCharges"].median()},inplace=True)
data.isnull().sum(axis=0)

重復值處理
如果有重復值:可直接洗掉
# data.drop_duplicates(inplace=True)
data.duplicated().sum()
輸出:0
說明沒有重復值
3.查看流失情況
整體流失情況 條形圖查看
Churn_value=https://www.cnblogs.com/lverkou/p/data["Churn"].value_counts()
display(Churn_value)
sns.countplot(x="Churn",data=https://www.cnblogs.com/lverkou/p/data)

也可餅狀圖查看
size=Churn_value.values
label_list=Churn_value.index
color=["#009999","#FF7400"]
explode=[0,0.1]
plt.figure(figsize=(8,8),dpi=80)
patches,l_text,p_text=plt.pie(size,
explode=explode,
colors=color,
labels=label_list,
labeldistance=1.1,
autopct='%1.1f%%',
shadow=True,
startangle=90,
pctdistance=0.6,
)
plt.show()

餅狀圖可以內部直接算出百分比,從圖中可以看出流失比例26.5%,占比較高,也處于樣本不均衡問題,后面可以采用過采樣來解決,過采樣相比欠采樣較穩定,
對于研究物件:Churn,我們用pandas中的map函式實作數字化處理,我們通常將關注的類別設為1,
data["Churn"]=data["Churn"].map({"Yes":1,"No":0})
data.head()

可以看出已經成功的將Churn做了離散化處理,
4.類別特征的描述性分析
特征列主要分兩大陣容,類別變數和連續型變數,我們有兩種方式把他們分離出來,第一種是直接一個一個drop掉,第二種方法是采用pandas里面提供的判斷類別型別和數值型的方法,這里我們采用第二種方式,
data_columns=[]
for col in data.columns.drop(["customerID","Churn"]):
# is_object_dtype:查看特征是否為類別型別,是的話往下執行;
# is_numeric_dtype:查看類別是否是數值型別的,是的話就繼續往下
if pd.api.types.is_object_dtype(data[col]):
data_columns.append(col)
print(data_columns)
data_object_lens=len(data_columns)
plt.figure(figsize=(20,100))#,dpi=80)
for col,k in zip(data_columns,range(data_object_lens)):
# 子圖第一列
plt.subplot(data_object_lens,2,2*k+1)
plt.title("Churn by "+col)
t=sns.countplot(x=col,hue="Churn",data=https://www.cnblogs.com/lverkou/p/data) #內部可統計數量
t.set_ylabel('數量')
# 子圖第二列
plt.subplot(data_object_lens,2,2*k+2)
plt.title("Churn rate by "+col)
#內部可計算均值 相當于所有1相加再除以總數就等于流失率
sns.barplot(x=col,y="Churn",data=https://www.cnblogs.com/lverkou/p/data).set_ylabel("流失率")
# 分組計算流失率
print(data.groupby(by=col)["Churn"].mean())
輸出如下:




從圖中可以看出:
gender性別對于流失占比分布較均衡,無顯著性差異;
Partner無配偶的流失率相對有配偶的流失率高13%;
Dependents無家屬相對有家屬的流失率高16%;
PhoneService有電話服務的用戶量非常巨大,流失率占到了接近三成;
MultipleLines多線業務對流失率無顯著性差異;
InternetService互聯網服務中fiber optic光纖網路用戶群體約占所有用戶的1/3,流失率占比卻超過了光纖用戶的40%;
OnlineSecurity無在線安全的用戶量不僅巨大,流失率也超過40%;
OnlineBackup無在線備份功能的用戶中,40%會流失;
DeviceProtection無設備保護的用戶比有保護的用戶流失率高17%;
TechSupport無技術支持的用戶中 有4成會流失;
StreamingTV網路電視的有無對流失率無顯著性差異;
Streaming網路電影的有無對流失率無顯著性差異;
Contract合同按月的用戶占比最多,其中,流失率達到了42%,一年簽的用戶中流失率只占1成,兩年簽的幾乎不會流失;
PaperlessBilling有賬單的用戶流失率高于無賬單的17%;
PaymentMethod付款方式中電子支票的用戶中,流失率快達到一半人數,
運營建議:
- 針對單身用戶和無家屬用戶,他們最大的相同點就是容易產生孤獨感,社交較薄弱,可以給這類人群增加套餐福利,如單身貴族等級制度,贈送流量刷劇看視頻,小游戲等方式可提高會員等級,達到一定等級解鎖新權益,讓用戶心理有賺到的感覺,
- 電話服務質量是否存在一定問題,客服人員服務態度是否親和,若沒有,定期做相關培訓,是否真實的幫助用戶解決了,設立電話服務后的評分反饋系統,若評分較低再次跟進直到真實的幫助到了用戶,
- 現在用戶很大部分愿意選擇用光纖,說明都有意識到它的快速便捷,但是真實情況是經常網路不穩定,所以可以加大力度對這方面設施設備的建設,
- 鼓勵用戶開通在線安全,在線備份,設備保護,技術支持,
- 有賬單和電子支票的用戶極大部分是對價格比價敏感,考慮到有的用戶經濟不獨立,比如學生,而且合同中按月支付容易流失極大可能是無法承擔費用造成的,我們可以采取鼓勵按年簽約,校園套餐,每月返現的方式,充值滿減,發放優惠券,消費達到一定金額提升會員等級解鎖特權,
除了以上特征,發現SeniorCitizen老年人和tenure職業還有遺漏,
對于老年人代碼分析如下:
plt.figure(figsize=(20,13))#,dpi=80)
plt.subplot(2,2,1)
sns.countplot(x="SeniorCitizen",hue="Churn",data=https://www.cnblogs.com/lverkou/p/data)
plt.subplot(2,2,2)
t=sns.barplot(x="SeniorCitizen",y="Churn",data=data)
t.set_ylabel("流失率")
t.set_title("老年人與流失率的關系")
print(data.groupby(by="SeniorCitizen")["Churn"].mean())

老年人數量雖然占比不高,但是流失率卻高達41%,但是其他用戶都會向這個群體邁入,很多老年人只會用打電話這一功能,所以我們可以采取啟用親友電話卡系結,提高老年人群體的免費撥打時長,增加短信提示每月剩余通話時長,提高活躍度,
對于職業,代碼分析如下:
plt.figure(figsize=(100,40))
sns.countplot(x="tenure",hue="Churn",data=https://www.cnblogs.com/lverkou/p/data)

職業中:不同的數字代表不同的職業,挑選出了流失率排前十的職業,
plt.figure(figsize=(100,20))
t=sns.barplot(x="tenure",y="Churn",data=https://www.cnblogs.com/lverkou/p/data)
t.set_title("不同職業與流失率的關系",size=100,color="#009999")
data.groupby(by="tenure")["Churn"].mean().sort_values(ascending=False).iloc[:10,]

從兩個圖結合可以看出,不同職業跟流失率也會存在一定的關系,職業為’0‘的雖然人數少,但是流失率幾乎為0,職業為’1‘的人數不僅最多,流失率也超過了該職業的50%,流失率排名前十的職業分別為:1,2,5,4,3,7,10,9,15,6,
辦法建議:可以采取獎勵機制,
5.連續型變數的分析
連續型變數:MonthlyCharges(月消費)和TotalCharges(總消費)
從主觀上來看,用戶的消費價格都是比較敏感的,所有我們可以重點關注一下消費價格,
查看月消費和總消費的各分位數 資料分布情況:
data.loc[:,["MonthlyCharges","TotalCharges"]].describe()

在7043條資料中,用戶月消費的平均水平在65元,中位數在70元,最小消費為18元,最大消費為118元;
總消費的的平均水平在2282元,中位數在1397元,最小消費為18元,最大消費為8684元,
其中,月消費的平均值比中位數略小,主要受極小值的影響;總消費的平均值高于中位數885元,主要受到一些極大值影響,
我們可以具體分別查看月消費,總消費各排名前十的用戶資訊,
首先查看月消費最低的10名用戶:
data.sort_values(by="MonthlyCharges",ascending=True).iloc[:10]

查看月消費最大的10名用戶:
data.sort_values(by="MonthlyCharges",ascending=True).iloc[-10:]

總消費最小的10名用戶:
data.sort_values(by="TotalCharges",ascending=True).iloc[:10]

查看總消費最高的10名用戶:
data.sort_values(by="TotalCharges",ascending=True).iloc[-10:]

高消費的明顯特征是他們都有這些服務的需求,不易流失;
低消費的明顯特征恰恰相反,他們沒有這些需求,猜測與養號有關,
用散點圖查看流失用戶整體分布
plt.figure(figsize=(20,8),dpi=80)
sns.scatterplot(x="MonthlyCharges",y="TotalCharges",color="g",data=https://www.cnblogs.com/lverkou/p/data[data["Churn"]==0])
sns.scatterplot(x="MonthlyCharges",y="TotalCharges",color="r",data=data[data["Churn"]==1])

紅色代表流失用戶,從圖中可以看出流失用戶主要分布在總消費偏低以及月消費偏高的地方,
嘗試用條形的散點圖查看分布情況:
fig=plt.figure(figsize=(20,10),dpi=80)
# 子圖1
fig.add_subplot(2,2,1)
sns.stripplot(x="Churn",y="MonthlyCharges",data=https://www.cnblogs.com/lverkou/p/data)
plt.title('月消費與流失的關系',fontproperties=my_font,color='red')
# 子圖2
fig.add_subplot(2,2,2)
sns.stripplot(x="Churn",y="TotalCharges",data=https://www.cnblogs.com/lverkou/p/data )
plt.title("總消費與流失的關系")
plt.show()

似乎不是很好看,哈哈~
蜂群圖:本來想展示一下蜂群圖查看分布情況,但是太丑了就略過,
條形圖:
fig=plt.figure(figsize=(20,10),dpi=80)
# 子圖1
fig.add_subplot(2,2,1)
# 分組計算流失與否的均值
display(data.groupby("Churn")["MonthlyCharges"].mean())
# barplot內部會自己求均值
sns.barplot(x="Churn",y="MonthlyCharges",data=https://www.cnblogs.com/lverkou/p/data)
# 圖中那條線代表總體均值所在的置信區間 默認為95%的置信度
# 子圖2
fig.add_subplot(2,2,2)
display(data.groupby("Churn")["TotalCharges"].mean())
sns.barplot(x="Churn",y="TotalCharges",data=data)
plt.show()

樣本中流失用戶的月消費平均值為74元,未流失用戶的月消費均值為61元;流失用戶的總消費均值為1531元,未流失用戶的均值為2552元,
箱線圖:
fig=plt.figure(figsize=(20,10),dpi=80)
# 子圖1
fig.add_subplot(2,2,1)
sns.boxplot(x="Churn",y="MonthlyCharges",data=https://www.cnblogs.com/lverkou/p/data)
# 子圖2
fig.add_subplot(2,2,2)
sns.boxplot(x="Churn",y="TotalCharges",data=data)
plt.show()

可以看出總消費中,流失用戶存在個別例外值,我們可以采取洗掉,視為缺失值處理,取對數轉換,邊界值替換等方式處理,
月消費的用戶流失資料:
data.groupby("Churn")["MonthlyCharges"].describe().T

總消費的用戶流失資料:
data.groupby("Churn")["TotalCharges"].describe().T

小提琴圖:
fig=plt.figure(figsize=(20,10),dpi=100)
# 子圖1
fig.add_subplot(2,2,1)
sns.violinplot(x="Churn",y="MonthlyCharges",data=https://www.cnblogs.com/lverkou/p/data)
# 子圖2
fig.add_subplot(2,2,2)
sns.violinplot(x="Churn",y="TotalCharges",data=data)
plt.show()

從以上圖中可以看出樣本中, 對于月消費來說,月消費高的用戶似乎容易流失;對于總消費來說,總消費低的用戶似乎容易失去,
差異檢驗-兩樣本t檢驗
以上都是對于樣本的結論,那么對于總體來說,是否也符合上述規律呢還是說我們抽樣出來的只是湊巧總體并不是這樣分布的,那么我們需要差異檢驗來驗證上述結論
我們用兩樣本t檢驗,來查看流失用戶與未流失用戶對于消費來說,他們的均值差異是否顯著,
差異檢驗 --月消費 "MonthlyCharges"
原假設:流失用戶的月消費與未流失用戶的月消費均值是一致的
總共分位兩步
第一步 :方差齊性檢驗
churn_1=data[data["Churn"]==1]["MonthlyCharges"]
churn_0=data[data["Churn"]==0]["MonthlyCharges"]
# 進行方差齊性檢驗-levene檢驗, 為后續的兩樣本t檢驗服務, 方差一致就叫齊性
stats.levene(churn_0,churn_1)

第二步: p值為1.026>=0.05,說明是支持原假設的方差是一致的,equal_var=True
進行兩樣本t檢驗-雙邊檢驗,方法用的是stats.ttest_ind,注意:兩樣本的方差相同與不相同,取得的結果是不同的,
r = stats.ttest_ind(churn_0,churn_1,equal_var=True)
print(r)
p值2.7>=0.05,支持原假設,所以我們還不能認為流失用戶的月均消費高于未流失的用戶,
同樣對于總消費,原假設:總消費均值都是一致的
第一步:方差齊性檢驗
churn_1=data[data["Churn"]==1]["TotalCharges"]
churn_0=data[data["Churn"]==0]["TotalCharges"]
stats.levene(churn_0,churn_1)

第二步:P值為3.38>0.05,說明是支持原假設的方差是一致的,齊性的,equal_var=True
r = stats.ttest_ind(churn_1,churn_0,equal_var=True)
print(r)

P值7.5>0.05,所以我們也不能認為總體的流失用戶的總均消費低于未流失的用戶,
下一步,劃分消費等級,
分箱離散化
定義消費等級 ,按照各分位數分為低消費 中低消費 中高消費 高消費
def charge_to_level(charge):
if charge<=da.loc["25%"]:
return "低消費"
elif charge<=da.loc["50%"] and charge>da.loc["25%"]:
return "中低消費"
elif charge<=da.loc["75%"] and charge>da.loc["50%"]:
return "中高消費"
else:
return "高消費"
da=data["MonthlyCharges"].describe()
data["level_MonthlyCharges"] = data["MonthlyCharges"].apply(charge_to_level)
da=data["TotalCharges"].describe()
data["level_TotalCharges"] = data["TotalCharges"].apply(charge_to_level)
display(data["level_MonthlyCharges"].value_counts())
data.sample(5)

條形圖查看:
fig=plt.figure(figsize=(20,10),dpi=80)
# 子圖1
fig.add_subplot(2,2,1)
sns.countplot(x="level_MonthlyCharges",hue="Churn",data=https://www.cnblogs.com/lverkou/p/data,order=["低消費","中低消費","中高消費","高消費"])
# 子圖2
fig.add_subplot(2,2,2)
sns.countplot(x="level_TotalCharges",hue="Churn",data=data,order=["低消費","中低消費","中高消費","高消費"])
plt.show()

可以看出對于月消費來說,流失用戶主要集中在中高消費以及高消費;對于總消費來說,流失用戶主要集中在低消費和中低消費中,那么我們可以對這部分用戶進行精細化運營以最大程度留住用戶,
6.機器學習
基模型的建立
將類別特征離散化處理之前,首先洗掉不需要離散化處理的特征,作者花了部分時間比較按照分位數分箱離散化和未分箱離散的資料預測得分,發現未分箱的效果好那么一點點,還有個原因是分箱的分界點的選擇,如果可以找到最合適的分界點,那么分箱離散化是一個相當不錯的選擇,這里就只演示未分箱的操作,
洗掉列MonthlyCharges,TotalCharges,Churn:
y=data["Churn"]
data.drop(['customerID','level_MonthlyCharges','level_TotalCharges',"Churn"],axis=1,inplace=True)
data.head()

使用pandas.get_dummies( )進行one-hot 獨熱編碼,
data_onehot=pd.get_dummies(data)
print(data_onehot.shape)
data_onehot.head()

職業雖然已經是數字,但是在數字中他們有大小的關系,實際職業之間是沒有大小比較的,所以也需要進行獨熱編碼,
data_base=pd.get_dummies(data_onehot,columns=['tenure'])
print(data_base.sample(5))
data_base.info()

進行獨熱編碼后占用的記憶體變少,因為獨熱編碼使用的是稀疏矩陣,
基模型的初始評分
x_train,x_test,y_train,y_test=train_test_split(data_base,y,test_size=0.25,random_state=0)
lr=LogisticRegression()
lr.fit(x_train,y_train)
print("訓練集:",lr.score(x_train,y_train))
print("測驗集:",lr.score(x_test,y_test))
y_hat = lr.predict(x_test)
# 測驗集的混淆矩陣得分值
print(classification_report(y_true=y_test, y_pred=y_hat))

如果只查看正確率accuracy,分值還是蠻高的達到了0.79,但是我們更多關注的是流失用戶,所以我們的評估指標選擇f1-score調和平均值會更符合氣質,
f1分值為0.57,
例外值處理
我們從之前的箱線圖可以看出總消費中存在個別的極大值,那么我們現在需要處理一下,
# 從箱線圖可以看出只有總消費存在較大的例外值 先計算分位數 IQR
quartile = np.quantile(x_train[y_train==1]['TotalCharges'],[0.25, 0.75])
IQR = quartile[1] - quartile[0]
upper = quartile[1] + 1.5 * IQR
print("IQR:{},upper:{}".format(IQR,upper))
def func(x):
if x >= upper:
return upper
else:
return x
x_train.loc[:,'TotalCharges'][y_train==1]= x_train.loc[:,'TotalCharges'][y_train==1].apply(lambda x: func(x))
# x_train['TotalCharges'][y_train==1]= x_train['TotalCharges'][y_train==1].apply(fun)
x_test['TotalCharges'][y_test==1]= x_test['TotalCharges'][y_test==1].apply(lambda x: func(x))
# 查看是否還存在例外值
print(x_train['TotalCharges'][y_train==1][x_train['TotalCharges']>upper])
print(x_test['TotalCharges'][y_test==1][x_test['TotalCharges']>upper])

這里很重要的一點:我們替換例外值應該是從測驗集中計算出,而不要把測驗集的資料也作為標準來計算出例外值,不然這就毫無意義了,
使用箱線圖查看訓練集中是否還存在例外值
sns.boxplot(x="Churn",y="TotalCharges",data=https://www.cnblogs.com/lverkou/p/pd.concat([x_train,y_train],axis=1))

使用箱線圖查看測驗集中是否還存在例外值
sns.boxplot(x="Churn",y="TotalCharges",data=https://www.cnblogs.com/lverkou/p/pd.concat([x_test,y_test],axis=1))

資料標準化處理
雖然邏輯回歸中w可以調節由于資料量綱的不同造成的模型不準確,但像KNN這樣的模型就會受到量綱在數量級上的不同,從而影響計算距離,所以我們可以先統一進行資料的標準化處理,
資料標準化主要有:均值標準差標準化和最小最大值標準化,
這里兩種方式嘗試之后 這里采用均值標準差標準化:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# StandardScaler:均值標準差標準化
# MinMaxScaler:最小最大值標準化
scaler,desc=[StandardScaler()],["均值標準差標準化"]
# scaler,desc=[MinMaxScaler()],["最小最大值標準化"]
for s, d in zip(scaler, desc):
# fit_transform:將訓練集和測驗集都進行標準化
x_train.loc[:,['MonthlyCharges','TotalCharges']] = s.fit_transform(x_train.loc[:,['MonthlyCharges','TotalCharges']])
x_test.loc[:,['MonthlyCharges','TotalCharges']] = s.transform(x_test.loc[:,['MonthlyCharges','TotalCharges']])
# 再代入KNN回歸演算法里
knn = KNeighborsClassifier(n_neighbors=3, weights="uniform")
knn.fit(x_train, y_train)
y_hat=knn.predict(x_test)
print(d, knn.score(x_test, y_test))
print(classification_report(y_true=y_test, y_pred=y_hat))

采用邏輯回歸查看標準化處理后的評分:
lr = LogisticRegression(multi_class="ovr", solver="liblinear")
lr.fit(x_train,y_train)
print("訓練集:",lr.score(x_train,y_train))
print("測驗集:",lr.score(x_test,y_test))
y_hat = lr.predict(x_test)
print(classification_report(y_true=y_test, y_pred=y_hat))

f1調和平均值:0.58
樣本不均衡處理&邏輯回歸
未處理之前,跑出來的評分模型最高評分有0.60,處理之后有所提升,
處理樣本不均衡方法有升采樣(上采樣),降采樣(下采樣),升采樣的主要方法有SMOTE和ADASYN,降采樣不推薦因為樣本數量減少對模型是有不良影響的,
這里采用的是上采樣,SMOTE和ADASYN對比使用之后,采用SMOTE方法
seed=0
# 鄰居數量k測驗出來為10有較好的得分
smote=SMOTE(random_state=seed,k_neighbors=10)
x_resample,y_resample=smote.fit_resample(x_train,y_train)
print(Counter(y_resample))
lr=LogisticRegression()
lr.fit(x_resample,y_resample)
y_hat=lr.predict(x_test)
print(classification_report(y_test,y_hat))
print(f1_score(y_test, y_hat))

升采用之后類別的次數基本相同,達到樣本均衡的效果,分值提升到了0.61,
網格交叉驗證&KNN
param = {"n_neighbors": range(3,13),
"weights": ["uniform", "distance"],
}
gs = GridSearchCV(estimator=KNeighborsClassifier(), param_grid=param,
cv=2, scoring="f1", n_jobs=-1, verbose=10)
gs.fit(x_resample, y_resample)
print(gs.best_params_)
y_hat = gs.best_estimator_.predict(x_test)
print(classification_report(y_test, y_hat))
# 最好的分值,訓練集的分值
print("分值:",gs.best_score_)
# 最好的超引陣列合,
print("超引陣列合:",gs.best_params_)
# 使用最好的超引數訓練好的模型,
print("模型:",gs.best_estimator_)
# {'n_neighbors': 12, 'weights': 'distance'} fi: 0.55

決策樹
from sklearn.tree import DecisionTreeClassifier
param = {"criterion": ["gini", "entropy"],
"max_depth": range(1,10)
}
gs = GridSearchCV(estimator=DecisionTreeClassifier(), param_grid=param,
cv=2, scoring="f1", n_jobs=-1, verbose=10)
gs.fit(x_resample, y_resample)
print(gs.best_params_)
y_hat = gs.best_estimator_.predict(x_test)
print(classification_report(y_test, y_hat))
# 最好的分值,
print("分值:",gs.best_score_)
# 最好的超引陣列合,
print("超引陣列合:",gs.best_params_)
# 使用最好的超引數訓練好的模型,
print("模型:",gs.best_estimator_)

流水線&樸素貝葉斯
# 高斯樸素貝葉斯 伯努利樸素貝葉斯 多項式樸素貝葉斯 補充樸素貝葉斯
from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB, ComplementNB
from sklearn.pipeline import Pipeline
steps=[("model", None)]
pipe = Pipeline(steps=steps)
# ComplementNB(): 適用于樣本不均衡的情況
# param里面的模型也可是決策樹,KNN演算法,可自行調整
param = {"model": [GaussianNB(), BernoulliNB(), MultinomialNB(), ComplementNB()]}
# 因為是稠密矩陣,因此比較消耗記憶體空間,記憶體小的,這里建議改成少的并發數量,
gs = GridSearchCV(estimator=pipe, param_grid=param,
cv=2, scoring="f1", n_jobs=-1, verbose=10)
gs.fit(x_train, y_train)
print(gs.best_params_)
y_hat = gs.best_estimator_.predict(x_test)
print(classification_report(y_test, y_hat))

多層感知器
神經網路當中存在隱藏層
當神經網路只有一層就是我們的邏輯回歸
from sklearn.neural_network import MLPClassifier
# 這兒只畫了兩個隱藏層,第一個隱藏層5個神經元,第二個隱藏層4個神經元
# for i in range(1,5):
# for j in range(5,7):
param = {"hidden_layer_sizes": [(5,), (4,)],
}
gs = GridSearchCV(estimator=MLPClassifier(), param_grid=param,
cv=2, scoring="f1", n_jobs=-1, verbose=10)
gs.fit(x_resample, y_resample)
print(gs.best_params_)
y_hat = gs.best_estimator_.predict(x_test)
print(classification_report(y_test, y_hat))
f1=f1_score(y_test, y_hat)

模型得分總結
在經過了資料標準化,樣本不均衡處理之后,帶入多種模型中,雖然accuracy得分都差不多能達到0.8,但是我們的關注點在于流失用戶,所以評估指標采用f1,最后決策樹的得分最高為0.63,比初始0.57分有所提升,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/65148.html
標籤:Python
