教你用Python發現即將流失的客戶(附代碼、安裝教程、學習資源)

做爲一名數據分析師,你來到這家跨國銀行工做已經半年了。數組

今天上午,老闆把你叫到辦公室,面色凝重。瀏覽器

你內心直打鼓,覺得本身捅了什麼簍子。幸虧老闆的話讓你很快打消了顧慮。網絡

他發愁,是由於最近歐洲區的客戶流失嚴重,許多客戶都跑到了競爭對手那裏接受服務了。老闆問你該怎麼辦?dom

你脫口而出「作好客戶關係管理啊!」機器學習

老闆看了你一眼,緩慢地說「咱們想知道哪些客戶最可能在近期流失」。函數

沒錯,在有魚的地方釣魚,纔是上策。工具

你明白了本身的任務——經過數據鎖定即將流失的客戶。這個工做,確實是你這個數據分析師份內的事兒。學習

你很慶幸,這半年作了不少的數據動態採集和整理工做,使得你手頭就有一個比較完備的客戶數據集。測試

下面你須要作的,就是如何從數據中「沙裏淘金」,找到那些最可能流失的客戶。命令行

但是,該怎麼作呢?

你拿出歐洲區客戶的數據,端詳起來。

客戶主要分佈在法國、德國和西班牙。

你手裏掌握的信息,包括他們的年齡、性別、信用、辦卡信息等。客戶是否已流失的信息在最後一列(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]])

你會發現,許多列的方差比原先小得多。機器學習起來,會更加方便。

數據清理和轉換工做至此完成。

 

閱讀全文

相關文章
相關標籤/搜索