一文看懂機器學習流程(客戶流失率預測)
點擊上方「
Python開發
」,選擇「置頂公眾號」
關鍵時刻,第一時間送達!
定義問題
客戶流失率問題是電信運營商面臨得一項重要課題,也是一個較為流行的案例。根據測算,招攬新的客戶比保留住既有客戶的花費大得多(通常5-20倍的差距)。因此,如何保留住現在的客戶對運營商而言是一項非常有意義的事情。 本文希望通過一個公開數據的客戶流失率問題分析,能夠帶著大家理解如何應用機器學習預測演算法到實際應用中。
當然, 實際的場景比本文例子複雜的多,如果想具體應用到項目, 還需要針對不同的場景和數據進行具體的分析。
從機器學習的分類來講, 這是一個監督問題中的分類問題。 具體來說, 是一個二分類問題。 所有的數據中包括一些特徵, 最後就是它的分類:流失或者在網。接下來我們就開始具體的處理。
分析數據
首先我們來導入數據, 然後查看數據的基本情況。
數據導入
通過pandas來導入csv, 然後我們來查看一下數據的基本情況
from
__future__
import
divisionimport
pandasas
pdimport
numpyas
npds = pd.read_csv(
"./churn.csv"
)col_names = ds.columns.tolist()
"Column names:"
print(ds.shape)
輸出:
Column "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?"
](
3333
,21
)可以看到, 整個數據集有3333條數據, 20個維度, 最後一項是分類。
基本信息以及類型
我們可以列印一些數據, 對數據和取值有一個基本的理解。
peek data .head(5)
輸出:
State
Account Length Area Code Phone Int
"l Plan VMail Plan
0 KS 128 415 382-4657 no yes
1 OH 107 415 371-7191 no yes
2 NJ 137 415 358-1921 no no
3 OH 84 408 375-9999 yes no
4 OK 75 415 330-6626 yes no
Eve Charge Night Mins Night Calls Night Charge Intl Mins Intl Calls
0 16.78 244.7 91 11.01 10.0 3
1 16.62 254.4 103 11.45 13.7 3
2 10.30 162.6 104 7.32 12.2 5
3 5.26 196.9 89 8.86 6.6 7
4 12.61 186.9 121 8.41 10.1 3
Intl Charge CustServ Calls Churn?
0 2.70 1 False.
1 3.70 1 False.
2 3.29 0 False.
3 1.78 2 False.
4 2.73 3 False.
我們可以看到, 數據集有20項特徵,分別是州名, 賬戶長度, 區號, 電話號碼, 國際計劃,語音郵箱, 白天通話分鐘數, 白天電話個數, 白天收費, 晚間通話分鐘數,晚間電話個數, 晚間收費, 夜間通話分鐘數,夜間電話個數, 夜間收費, 國際分鐘數, 國際電話個數, 國際收費, 客服電話數,流失與否.
可以看到這裡面有個人信息,應該可以看到有些信息與流失與否關係不大。 州名, 區號可以指明客戶的位置, 和流失有關係么, 不知道, 具體位置如果不分類, 應該完全沒有關係。 而州名, 也許某個州有了某個強勁的競爭對手? 這也是瞎猜, 暫時意義不大, 刪除。
賬號長度, 電話號碼, 不需要
國際計劃, 語音郵箱。 可能有關係, 先留著吧。
分別統計了白天, 晚間, 夜間的通話分鐘, 電話個數, 收費情況。 這是重要信息保留
客服電話, 客戶打電話投訴多那流失率可能會大。 這個是重要信息保留。
流失與否。 這是分類結果。
然後我們可以看一下數據的類型, 如下:
infods.
輸出:
RangeIndex 3333 0 3332 Data 21 State 3333 null object Account Length 3333 null Area Code 3333 null Phone 3333 null object Int "l Plan 3333 null object VMail Plan 3333 null object VMail Message 3333 null Day Mins 3333 null Day Calls 3333 null Day Charge 3333 null Eve Mins 3333 null Eve Calls 3333 null Eve Charge 3333 null Night Mins 3333 null Night Calls 3333 null Night Charge 3333 null Intl Mins 3333 null Intl Calls 3333 null Intl Charge 3333 null CustServ Calls 3333 null Churn 3333 null object 8 8 object ( 5
546.9
+KB
看見, 有int, float, object。 對於不是數據型的數據, 後面除非決策樹等演算法, 否則應該會轉化成數據行。 所以我們把churn? 結果轉化, 以及"Int"l Plan","VMail Plan", 這兩個參數只有yes, no 兩種, 所以也進行轉化成01值。
描述性統計
describe() 可以返回具體的結果, 對於每一列。
數量 平均值 標準差 25% 分位 50% 分位數 75% 分位數 最大值 很多時候你可以得到NA的數量和比例。
TODO 對於非數據性的是沒有返回的的
Account Length Area Code VMail Message Day Mins Day Calls count .000000 .000000 .000000 .000000 .000000 mean .064806 .182418 .099010 .775098 .435644 std .822106 .371290 .688365 .467389 .069084 min .000000 .000000 .000000 .000000 .000000 .000000 .000000 .000000 .700000 .000000 .000000 .000000 .000000 .400000 .000000 .000000 .000000 .000000 .400000 .000000 max .000000 .000000 .000000 .800000 .000000
25% 74
50% 101
75% 127
Day
Charge
Eve
Mins
Eve
Calls
Eve
Charge
Night
Mins
count
3333.000000
3333.000000
3333.000000
3333.000000
3333.000000
mean
30.562307
200.980348
100.114311
17.083540
200.872037
std
9.259435
50.713844
19.922625
4.310668
50.573847
min
0.000000
0.000000
0.000000
0.000000
23.200000
25% 24
.430000
166.600000
87.000000
14.160000
167.000000
50% 30
.500000
201.400000
100.000000
17.120000
201.200000
75% 36
.790000
235.300000
114.000000
20.000000
235.300000
max
59.640000
363.700000
170.000000
30.910000
395.000000
Night
Calls
Night
Charge
Intl
Mins
Intl
Calls
Intl
Charge
count
3333.000000
3333.000000
3333.000000
3333.000000
3333.000000
mean
100.107711
9.039325
10.237294
4.479448
2.764581
std
19.568609
2.275873
2.791840
2.461214
0.753773
min
33.000000
1.040000
0.000000
0.000000
0.000000
25% 87
.000000
7.520000
8.500000
3.000000
2.300000
50% 100
.000000
9.050000
10.300000
4.000000
2.780000
75% 113
.000000
10.590000
12.100000
6.000000
3.270000
max
175.000000
17.770000
20.000000
20.000000
5.400000
CustServ
Calls
count
3333.000000
mean
1.562856
std
1.315491
min
0.000000
25% 1
.000000
50% 1
.000000
75% 2
.000000
max
9.000000
圖形化理解你的數據
之前的一些信息, 只是一些很初步的理解, 但是對於機器學習演算法來講是不夠的。 下面我們從幾個維度去進一步理解你的數據。工具可以用數字表格, 也可以用圖形(matplotlib) 這裡畫圖較多。
特徵自己的信息
特徵和分類之間的關係
特徵和特徵之間的關係 -- 這裡鑒於時間的關係, 有些關係並沒有直接應用於演算法本身, 但是在進一步的演算法提升中是很有意義的, 這裡更多的是一種展示。
特徵本身的信息
我們先來看一下流失比例, 以及關於打客戶電話的個數分布
import as 0.2 # 設定圖表顏色alpha參數 2 3 0 0 # 在一張大圖裡分列幾個小圖 "Churn?" "bar" # plots a bar graph of those who surived vs those who did not. u"stat for churn" # puts a title on our graph u"number"
%matplotlib inline
fig = plt.figure()
fig.set(alpha=
plt.subplot2grid((
2
,3
),(0
,2
))ds[
"CustServ Calls"
].value_counts().plot(kind="bar"
)# plots a bar graph of those who surived vs those who did not.
u"stat for cusServCalls"
)# puts a title on our graph
u"number"
)plt.show()
很容易理解。
然後呢, 我們的數據的特點是對白天, 晚上, 夜間,國際都有分鐘數, 電話數, 收費三種維度。 那麼我們拿白天的來舉例。
import as 0.2 # 設定圖表顏色alpha參數 2 5 0 0 # 在一張大圖裡分列幾個小圖 "Day Mins" "kde" # plots a kernel desnsity estimate of customer u"Mins" # plots an axis lable u"density" u"dis for day mins"
%matplotlib inline
fig = plt.figure()
fig.set(alpha=
plt.title(
plt.subplot2grid((
2
,5
),(0
,2
))ds[
"Day Calls"
].plot(kind="kde"
)# plots a kernel desnsity estimate of customer
u"call"
)# plots an axis lable
u"density"
)plt.title(
u"dis for day calls"
)plt.subplot2grid((
2
,5
),(0
,4
))ds[
"Day Charge"
].plot(kind="kde"
)# plots a kernel desnsity estimate of customer
u"Charge"
)# plots an axis lable
u"density"
)plt.title(
u"dis for day charge"
)plt.show()
可以看到分布基本上都是高斯分布, 這也符合我們的預期, 而高斯分布對於我們後續的一些演算法處理是個好消息。
特徵和分類的關聯
我們來看一下一些特徵和分類之間的關聯。 比如下面int plan
import as 0.2 # 設定圖表顏色alpha參數 "Churn?" "Int"l Plan" "yes" "Churn?" "Int"l Plan" "no" u"int plan" u"no int plan" "bar" True u"statistic between int plan and churn" u"int or not" u"number"
fig = plt.figure()
fig.set(alpha=
int_no = ds[
df_int=pd.DataFrame({
df_int.plot(kind=
plt.title(
plt.xlabel(
plt.ylabel(
plt.show()
我們可以看到, 有國際電話的流失率較高。 猜測也許他們有更多的選擇, 或者對服務有更多的要求。 需要特別對待。 也許你需要電話多收集一下意見了。
再來看一下
#查看客戶服務電話和結果的關聯 0.2 # 設定圖表顏色alpha參數 "CustServ Calls" "Churn?" "False." "CustServ Calls" "Churn?" "True." u"churn" u"retain" "bar" True u"Static between customer service call and churn" u"Call service" u"Num"
fig.set(alpha=
cus_1 = ds[
df=pd.DataFrame({
df.plot(kind=
plt.title(
plt.xlabel(
plt.ylabel(
plt.show()
基本上可以看出, 打客戶電話的多少和最終的分類是強相關的, 打電話3次以上的流失率比例急速升高。 這是一個非常關鍵的指標。
準備數據
好的, 我們已經看了很多,對數據有了一定的理解。 下面我們開始具體對數據進行操作。
去除無關列
首先, 根據對問題的分析, 我們做第一件事情, 去除三列無關列。 州名, 電話, 區號。
我們和下一步一起做
轉化成數值類型
對於有些特徵, 本身不是數值類型的, 這些數據是不能被演算法直接使用的, 所以我們來處理一下
"Churn where "True 1 0# Isolate target data
ds_result = ds[
Y = np.
dummies_int = pd.get_dummies(ds[
"Int
"l
Plan"], prefix="_int
"l
Plan")dummies_voice = pd.get_dummies(ds[
"VMail
Plan"], prefix="VMail
")ds_tmp=pd.concat([ds, dummies_int, dummies_voice], axis=
1
)# We don
"t
need these columnsto_drop = [
"State
","
Area Code","
Phone","
Churn?","Int
"l
Plan","VMail
Plan"]df = ds_tmp.
drop
(to_drop,axis=1
)"after convert "
print df.head(5
)輸出:
01 0 128 25 265.1 110 45.07 197.4 1 107 26 161.6 123 27.47 195.5 2 137 0 243.4 114 41.38 121.2 3 84 0 299.4 71 50.90 61.9 4 75 0 166.7 113 28.34 148.3after convert
Account Length VMail Message Day Mins Day Calls Day Charge Eve Mins
Eve Calls Eve Charge Night Mins Night Calls Night Charge
Intl
Mins0
99
16.78
244.7
91
11.01
10.0
1
103
16.62
254.4
103
11.45
13.7
2
110
10.30
162.6
104
7.32
12.2
3
88
5.26
196.9
89
8.86
6.6
4
122
12.61
186.9
121
8.41
10.1
Intl
CallsIntl
Charge CustServ Calls _int"l Plan_no _int"
l Plan_yes0
3
2.70
1
1
0
1
3
3.70
1
1
0
2
5
3.29
0
1
0
3
7
1.78
2
0
1
4
3
2.73
3
0
1
VMail_no VMail_yes
0
0
1
1
0
1
2
1
0
3
1
0
4
1
0
我們可以看到結果, 所有的數據都是數值型的, 而且除去了對我們沒有意義的列。
scale 數據範圍
我們需要做一些scale的工作。 就是有些屬性的scale 太大了。
對於邏輯回歸和梯度下降來說, 個屬性的scale 差距太大, 會對收斂速度有很大的影響。
我們這裡對所有的都做, 其實可以對一些突出的特徵做這種處理。
#scale
# This is important
from
sklearn.preprocessingimport
StandardScalerscaler = StandardScaler()
X = scaler.fit_transform(X)
"Feature space holds %d observations and %d features"
% X.shape"Unique target labels:"
, np.unique(y)輸出:
3333 and 19 0 1Feature space holds
Unique target labels: [
其他的呢, 還可以考慮降維等各種方式。 但是再實際使用中, 我們往往首先做出一個模型, 得到一個參考結果, 然後逐步優化。 所以我們準備數據就到這裡。
評估演算法
我們會使用多個演算法來計算結果, 然後選擇較好的。 如下
# prepare models append "LR" append "LDA" append "KNN" append "CART" append "NB" append "SVM" # evaluate each model in turn "accuracy" for 10 7 append append "%s: %f (%f)" # boxplot algorithm comparison "Algorithm Comparison" 111
models.
models.
models.
models.
models.
models.
names = []
scoring =
kfold = KFold(n_splits=
cv_results = cross_val_score(model, X, Y, cv=kfold, scoring=scoring)
results.
names.
msg =
print(msg)
fig.suptitle(
ax = fig.add_subplot(
pyplot.boxplot(results)
ax.set_xticklabels(names)
pyplot.show()
LR .860769 .021660 LDA .852972 .021163 KNN .896184 .016646 CART .920491 .012471 NB .857179 .015487 SVM .921091 .016828
可以看到什麼呢, 看到SVM 和 CART 效果相對較好。
提升結果
提升的部分, 如何使用提升演算法。 比如隨機森林。 xgboost
# 0.954696013379from sklearn.ensemble import RandomForestClassifier
num_trees = 100
max_features = 3
kfold = KFold(n_splits=10, random_state=7)
model = RandomForestClassifier(n_estimators=num_trees, max_features=max_features)
results = cross_val_score(model, X, Y, cv=kfold)
print(results.mean())
# 0.953197209185from sklearn.ensemble import GradientBoostingClassifier
seed = 7
num_trees = 100
kfold = KFold(n_splits=10, random_state=seed)
model = GradientBoostingClassifier(n_estimators=num_trees, random_state=seed)
results = cross_val_score(model, X, Y, cv=kfold)
print(results.mean())
可以看到, 這兩種演算法對單個演算法的提升還是很明顯的。 進一步的, 也可以繼續調整tree的數目, 但是效果應該差不多了。
展示結果
這裡展示了如何保存這個演算法, 以及如何取出然後應用。
#store from import from import from import
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=
0.33
, random_state=7
)from
sklearn.ensembleimport
GradientBoostingClassifierseed =
7
100
10
, random_state=seed)model = GradientBoostingClassifier(n_estimators=num_trees, random_state=seed)
model.fit(X_train, Y_train)
# save the model to disk
"finalized_model.sav"
"wb"
))# some time later...
# load the model from disk
"rb"
))result = loaded_model.score(X_test, Y_test)
print(result)
後記
本文展示了通過用戶流失率問題, 如何把機器學習的預測過程應用到實際項目中。
從業務的角度, 這個只是一個demo性質的應用, 實際場景可能複雜的多。
從流程的角度, 通過對數據的分析可以進一步提升演算法的性能, 對於某些的特徵, 可以採取不同的處理方式。 比如缺失值的處理, 這裡很完整, 就省去了這個步驟。
來自:開源中國
https://my.oschina.net/sizhe/blog/1594791
Python開發整理髮布,轉載請聯繫作者獲得授權
【點擊成為程序員大咖】
TAG:Python開發 |