【案例-數(shù)據(jù)挖掘】酒店預定客戶流失分析及預測

分析思路:

0、數(shù)據(jù)準備
1缩筛、數(shù)據(jù)探索
2消略、特征工程
3、建模
4瞎抛、RFM分析和用戶畫像

0艺演、數(shù)據(jù)準備

0.1 模塊及數(shù)據(jù)導入
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 解決坐標軸刻度負號亂碼
plt.rcParams['axes.unicode_minus'] = False
# 解決中文亂碼問題
plt.rcParams['font.sans-serif'] = ['Simhei']
%matplotlib inline
df=pd.read_csv(r'D:\Users\wuxiao\Desktop\userlostprob\userlostprob.txt',sep='\t')
df.head()

image.png

ps,如果仍然存在mac畫圖中文無法顯示的問題:https://blog.csdn.net/qq_21904665/article/details/78732696

0.2 數(shù)據(jù)項基本信息
image.png

數(shù)據(jù)維度為689945*51桐臊,label為標簽列胎撤,1為未流失,0為流失(等會兒再確認)断凶,samplied是id列伤提?因為沒有重復項,其余有49個特征項认烁,下面將針對部分關(guān)鍵特征進行分析飘弧。

  • d:預定日期
  • arrival:入住日期
  • h:訪問時間段
  • customer_value_profit:客戶近1年價值
  • ctrip_profits:客戶價值
  • consuming_capacity:消費能力指數(shù)
  • price_sensitive:價格敏感指數(shù)
  • avgprice:入住酒店平均價格
  • starprefer:酒店星級偏好
  • ordernum_oneyear:年訂單數(shù)
  • ordercanceledprecent:訂單取消率
  • lasthtlordergap:距離上次預定的時間
  • sid:新客老客特征
  • hotelcr:酒店cr值
  • hoteluv:酒店uv值
  • commentnums:酒店點評數(shù)
  • novoters:酒店點評人數(shù)
  • cancelrate:酒店取消率
  • lowestprice:酒店最低價
image.png

數(shù)據(jù)存在偏斜,但不平衡程度不大砚著。


image.png

數(shù)據(jù)缺失值較多,特別是historyvisit_7ordernum缺失達到88%痴昧。

一稽穆、數(shù)據(jù)探索

1.1 預定日期和入住日期
df_d=df.d.value_counts().to_frame().reset_index()
df_arrival=df.arrival.value_counts().to_frame().reset_index()
time_table=df_d.merge(df_arrival,how='outer',on='index')
time_table.fillna(0,inplace=True)
time_table.set_index('index',inplace=True)
time_table.sort_index(inplace=True)

x=time_table.index
y1=time_table.arrival
y2=time_table.d

plt.figure(figsize=(13,5))
plt.style.use('bmh')
plt.plot(x,y1,c="r",label='入住人數(shù)');
plt.bar(x,y2,align="center",label='預定人數(shù)');
plt.title('訪問和入住人數(shù)圖',fontsize=20)
plt.xticks(rotation=45,fontsize=13)
plt.xlabel('日期');
plt.ylabel('人數(shù)',fontsize=13);
plt.legend(fontsize=13)
image.png
  • 520那天預定人數(shù)和入住人數(shù)都達到峰值,因為情侶會出門“過節(jié)”赶撰。521之后入住人數(shù)就一路走低舌镶。后面有兩個小突起是周末。

1.2 訪問時間段

plt.figure(figsize=(15, 6))
plt.hist(df.h.dropna(), bins = 50, edgecolor = 'k');
#因為最多24個時段豪娜,所以bins再大的話餐胀,只是調(diào)整方塊的間距了
plt.title('訪問時間段',fontsize=20);
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.xlabel('訪問時間',fontsize=18); 
plt.ylabel('人數(shù)',fontsize=18); 
image.png
  • 5點是訪問人數(shù)最少的時點,這個時候大家都在睡覺瘤载。5點過后訪問人數(shù)開始上升否灾,在晚間9、10點的時間段鸣奔,訪問人數(shù)是最多的墨技。
1.3
plt.figure(figsize=(12, 4))

plt.subplot(121)
plt.plot(df.index,df.customer_value_profit,linewidth=0.5)
plt.title('客戶近1年價值')

plt.subplot(122)
plt.plot(df.index,df.ctrip_profits,linewidth=0.5)
plt.title('客戶價值')
image.png
  • 可以看到,“客戶近1年價值”和“客戶價值”兩個特征是非常相關(guān)的挎狸,都可以用來表示[客戶的價值]這么一個特征扣汪。同時可以看到,大部分的客戶價值都處在0-100這個范圍锨匆,但是有些客戶價值非常大崭别,設(shè)置達到了600,這些客戶都可以在以后的分析中重點觀察,因為他們是非常有“價值”的茅主。
1.4 消費能力指數(shù)
plt.figure(figsize=(12, 4))

plt.hist(df.consuming_capacity,bins=50,edgecolor='k')
plt.xlabel('消費能力指數(shù)')
plt.ylabel('人數(shù)')
plt.title('消費能力指數(shù)圖')
image.png
  • 可以看到舞痰,消費能力指數(shù)的值范圍是0-100,相當于對酒店客戶(及潛在客戶)的一個消費能力進行打分暗膜。指數(shù)值基本呈現(xiàn)一個正態(tài)分布的形狀匀奏,大部分人的消費能力在30附近。當然学搜,我們同時可以看到娃善,消費能力達到近100的人數(shù)也非常多,說明在我們酒店的訪問和入住客戶中瑞佩,有不在少數(shù)的群體是消費水平非常高的聚磺,土豪還是多啊。
1.5 價格敏感指數(shù)
plt.figure(figsize=(12, 4))

plt.hist(df['price_sensitive'].dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('價格敏感指數(shù)'); 
plt.ylabel('人數(shù)'); 
plt.title('價格敏感指數(shù)圖');
image.png
  • 價格敏感指數(shù)炬丸,用來反映客戶對價格的一個在意程度瘫寝。可以看到稠炬,除去兩頭的極值現(xiàn)象焕阿,中間的分布屬于右偏的正太分布,大部分人的價格敏感指數(shù)比較低首启,也就是說暮屡,大部分客戶(及潛在客戶)是對價格不是很敏感的,并不會一味地去追求低價的酒店和房間毅桃,或許褒纲,酒店方面不需要在定價方面花費太多的腦筋。當然钥飞,我們也會發(fā)現(xiàn)莺掠,100處的人數(shù)也并不少,還是存在一部分的群體對價格極度敏感的读宙,如果是針對這一部分客戶彻秆,用一些打折優(yōu)惠的方式會有意想不到的成效。
1.6 入住酒店平均價格
plt.figure(figsize=(12, 4))
plt.subplot(121)
plt.hist(df.avgprice.dropna(),bins=50,edgecolor = 'k')
plt.xlabel('酒店價格'); 
plt.ylabel('偏好人數(shù)'); 
plt.title('酒店價格偏好');

plt.subplot(122)
plt.hist(df[df.avgprice<2000]['avgprice'].dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('酒店價格'); 
plt.ylabel('偏好人數(shù)'); 
plt.title('2000元以內(nèi)酒店偏好');
image.png
  • 從左圖中可以知道结闸,酒店平均價格范圍為1-6383元掖棉,但實際上酒店價格在1000以上的,選擇的人就非常少了膀估,價格在2000元以上的酒店就更加是沒有人去選擇了幔亥,所以右圖展示了價格為2000元以下的酒店情況。右圖表明察纯,消費者對酒店價格的選擇帕棉,基本是一個正偏態(tài)的分布针肥,大部分人會選擇的平均價格在300元左右(基本就是7天、如家這類吧)香伴。
1.7 酒店星級偏好
plt.figure(figsize=(10, 4))
plt.hist(df.starprefer.dropna(), bins = 50, edgecolor = 'k')
plt.xlabel('星級偏好程度')
plt.ylabel('選擇人數(shù)');
plt.title('酒店星級偏好')
image.png
  • 分布有點不規(guī)律慰枕,尤其是40、60即纲、80具帮、100的分段存在極值情況,剔除這幾個分段低斋,星級偏好主要集中在60~80之間蜂厅。
1.8 用戶年訂單數(shù)
plt.figure(figsize=(12, 4))
plt.subplot(121)
plt.hist(df.ordernum_oneyear.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('年訂單數(shù)'); 
plt.ylabel('人數(shù)'); 
plt.title('客戶年訂單數(shù)分布');

plt.subplot(122)
plt.hist(df[df.ordernum_oneyear<100].ordernum_oneyear.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('年訂單數(shù)'); 
plt.ylabel('人數(shù)'); 
plt.title('年訂單數(shù)100單內(nèi)的分布');
image.png
  • 用戶年訂單數(shù)最高可達700+,但是大部分用戶的你按訂單數(shù)集中在0~20之間膊畴。
1.9 訂單取消率
plt.figure(figsize=(10, 4))
plt.hist(df.ordercanceledprecent.dropna(),bins=50,edgecolor = 'k')
plt.xlabel('訂單取消率')
plt.ylabel('人數(shù)')
plt.title('訂單取消率')
image.png
  • 存在大量的用戶訂單取消率為0掘猿,也存在部分極端用戶訂單取消率為1。
1.10 舉例上一次預定的時間
plt.figure(figsize=(10, 4))
plt.hist(df.lasthtlordergap.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('間隔時長'); plt.ylabel('人數(shù)'); 
plt.title('距離上次預定的時間');

image.png
  • 從圖像上看唇跨,比較符合均值為0的正態(tài)分布稠通。但是并不知道間隔時長的單位是什么。
1.11 新老客流失率
s_table=df[['label','sid']]
s_table['sid']=np.where(s_table['sid']==1,1,0)
s_table['flag']=1
s=s_table.groupby('sid').sum().reset_index()
s['rate']=s['label']/s['flag']                       # flag求和剛好是sid為0和1的個數(shù)买猖,label求和剛好是流失人數(shù)改橘,相除則為流失率

plt.figure(figsize=(12, 4))
plt.subplot(121)
percent=[s['flag'][0]/s['flag'].sum(),s['flag'][1]/s['flag'].sum()]
color=['steelblue','lightskyblue']
label=['老客','新訪']
plt.pie(percent,autopct='%.2f%%',labels=label,colors=color)
plt.title('新老客戶占比')

plt.subplot(122)
plt.bar(s.sid,s.rate,align='center',tick_label=label,edgecolor = 'k')
plt.ylabel('流失率')
plt.title('新老客戶中的客戶流失率')
image.png
  • 新客大約占5.58%,老客的流失率較新客的流失率更高玉控。
1.12 酒店轉(zhuǎn)換率
plt.figure(figsize=(10, 4))
plt.hist(df.hotelcr.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('酒店cr值');  
plt.title('酒店轉(zhuǎn)換率');
image.png
  • CR本來是網(wǎng)站轉(zhuǎn)化率(conversion rate)是指用戶進行了相應(yīng)目標行動的訪問次數(shù)與總訪問次數(shù)的比率飞主,應(yīng)當是小于1的數(shù)據(jù),但這里酒店的cr處在1~2之間奸远,不知道是怎么定義的。
1.13 酒店獨立訪客
plt.figure(figsize=(10, 4))
plt.hist(df.hoteluv.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('酒店uv值');  
plt.title('酒店歷史獨立訪客量');
image.png
  • PV(訪問量): 即Page View, 即頁面瀏覽量或點擊量讽挟,用戶每次刷新即被計算一次懒叛。UV(獨立訪客):即Unique Visitor,訪問您網(wǎng)站的一臺電腦客戶端為一個訪客。00:00-24:00內(nèi)相同的客戶端只被計算一次耽梅。umm...這里的酒店uv值薛窥,不太清楚代表的是什么,還是數(shù)據(jù)源的問題眼姐。诅迷。
1.14 當前酒店點評數(shù)
plt.figure(figsize=(10, 4))
plt.hist(df.commentnums.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('點評數(shù)量');  
plt.title('酒店點評數(shù)');
image.png
1.15 當前酒店評分人數(shù)
plt.figure(figsize=(10, 4))
plt.hist(df.novoters.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('點評人數(shù)');  
plt.title('酒店評分人數(shù)');
image.png
1.16 當前酒店歷史訂單取消率
plt.figure(figsize=(10, 4))
plt.hist(df.cancelrate.dropna(), bins = 50, edgecolor = 'k');
plt.xlabel('訂單取消率');  
plt.title('酒店訂單取消率');
image.png
1.17 當前酒店可訂最低價
plt.figure(figsize=(10, 4))
plt.plot(df.lowestprice.dropna())
plt.xlabel('酒店最低價');  
plt.title('酒店最低價');
image.png

二、特征工程

# 為了避免在原數(shù)據(jù)集上進行修改操作众旗,我們將df復制一份
df1=df.copy()
2.1 字符串處理

原數(shù)據(jù)中罢杉,arrival和d都是字符串格式,可以將其詳見得到“提前預定的天數(shù)”贡歧,并轉(zhuǎn)化為新的數(shù)值特征滩租。

## 增加列
# 將兩個日期變量由字符串轉(zhuǎn)換為日期型格式
df1['arrival']=pd.to_datetime(df1['arrival'])
df1['d']=pd.to_datetime(df1['d'])
# 生成提前預定時間列
df1['day_advanced']=(df1['arrival']-df1['d']).dt.days

## 刪除列
df1=df1.drop(['d','arrival'],axis=1)

處理后:


image.png

ps:pd顯示的模式有數(shù)值模式/datetime/其他格式赋秀,所以是可以轉(zhuǎn)化成datetime格式再處理的。http://www.manongjc.com/article/3143.html律想,有空可以再研究

2.2 異常值處理

把用戶價值的兩個特征量customer_value_profit猎莲、ctrip_profits中的負值按0處理;把delta_price1、delta_price2技即、lowestprice中的負值按中位數(shù)處理著洼。我個人也不知道為什么這么處理,因為里面很多特征并不知道具體含義而叼。

filter1=['customer_value_profit','ctrip_profits']
filter2=['delta_price1','delta_price2','lowestprice']

for i in filter1:
    df1.loc[df1[i]<0,i]=0        ##用df1.loc[df1[i]<0][i]=0 會提示無法有點問題身笤,所以還是得用前面的用法
    
for i in filter2:
    temp=df.delta_price1.mean()
    df1.loc[df1[i]<0,i]=temp    ##用df1.loc[df1[i]<0][i]=0 會提示無法有點問題,所以還是得用前面的用法

處理后:


image.png
2.3 缺失值處理

原數(shù)據(jù)中只有iforderpv_24h澈歉、sid展鸡、h、day_advanced這四個是不存在缺失的埃难,其他的44個特征都是存在缺失值的莹弊,并且大部分的缺失值都挺多的,因此涡尘,我們接下來需要對缺失值進行處理忍弛。

2.3.1 空值刪除

首先設(shè)定rate為0.2,如果某行或某列的數(shù)據(jù)缺失率超過(1-0.2)=0.8考抄,則將其刪除:

# 刪除缺失值比例大于80%的行和列
print('刪除空值前數(shù)據(jù)維度是:{}'.format(df1.shape))
df1.dropna(axis=0,thresh=df1.shape[1]*0.2,inplace=True)
df1.dropna(axis=1,thresh=df1.shape[0]*0.2,inplace=True)
print('刪除空值后數(shù)據(jù)維度是:{}'.format(df1.shape))
image.png
  • 空值刪除操作后细疚,樣本數(shù)據(jù)減少了100條,不算多川梅,影響不大疯兼;特征值少了一個,進一步查看可以得知贫途,historyvisit_7ordernum這一列被刪除了吧彪,因為這一列的缺失值比例高達88%,數(shù)據(jù)缺失過多丢早,我們將其刪除姨裸。
2.4缺失項補足

數(shù)據(jù)項基本滿足正態(tài)分布或者右偏分布,對正態(tài)分布項怨酝,采用均值填充較合適傀缩,對右偏分布項,采用中位數(shù)填充更合適农猬。原數(shù)據(jù)中赡艰,businessrate_pre2、cancelrate_pre斤葱、businessrate_pre趨于正態(tài)分布瞄摊,齊豫趨于右偏分布(這一點沒有詳細考證)钾恢。

filter_mean=['businessrate_pre2','cancelrate_pre','businessrate_pre']
for i in df1.columns:
    if i in filter_mean:
        df1[i].fillna(df1[i].mean(),inplace=True)
    else:
        df1[i].fillna(df1[i].median(),inplace=True)

ps:偏態(tài)分布https://zhidao.baidu.com/question/361765236.html

image.png
2.5 極值處理

有些特征明顯有異常大和異常小的值没炒,這里分別用1%和99%分位數(shù)替換超過上下限的值。

for i in df1.columns:
    df1.loc[df1[i]<np.percentile(df1[i],1),i]=np.percentile(df1[i],1)
    df1.loc[df1[i]>np.percentile(df1[i],99),i]=np.percentile(df1[i],99)
2.3.4 相關(guān)性分析
# 用戶特征提取(分兩次提取,為了更好地顯示圖)
user_features=['visitnum_oneyear','starprefer','sid','price_sensitive','ordernum_oneyear','ordercanncelednum','ordercanceledprecent','lastpvgap',
               'lasthtlordergap','landhalfhours','iforderpv_24h','historyvisit_totalordernum','historyvisit_avghotelnum','h',
               'delta_price2','delta_price1','decisionhabit_user','customer_value_profit','ctrip_profits','cr','consuming_capacity','avgprice']
# 生成用戶特征的相關(guān)性矩陣
corr_mat=df1[user_features].corr()

# 繪制用戶特征的相關(guān)性矩陣熱度圖
plt.figure(figsize=(12,12))
sns.heatmap(corr_mat, xticklabels=True, yticklabels=True, square=False, linewidths=.5, annot=True, cmap='Blues')
image.png

可以看出茅信,不少特征存在強相關(guān)性:

  • ordernum_oneyear和historyvisit_totalordernum的相關(guān)性高達0.93枯芬,因為它們都是表示用戶1年內(nèi)的訂單數(shù)掘殴,我們選擇其中名字更好識別的ordernum_oneyear作為用戶年訂單數(shù)的特征匕荸。
  • decisionhabit_user和historyvisit_avghotelnum相關(guān)性達到了0.89,說明也是高度相關(guān)的祟牲,說明可能用戶的決策習慣就是根據(jù)用戶近3個月的日均訪問數(shù)來設(shè)定的隙畜,我們可以通過PCA提取一個主成分用來表示用戶近期的日均訪問量。
  • customer_value_profit和ctrip_profits這兩個特征之間相關(guān)性達到了0.85说贝,這兩個特征我們在上面的數(shù)據(jù)可視化中就有提到议惰,表示的是不同時間長度下衡量的客戶價值,必然是高度相關(guān)的乡恕,我們可以用PCA的方法提取出一個主成分來代表客戶價值這么一個信息言询。
  • avgprice和consuming_capacity之間的相關(guān)性達到了0.91,同時starprefer與consuming_capacity相關(guān)性0.71傲宜,starprefer與avgprice相關(guān)性0.66运杭,都比較高。這三個特征我們在數(shù)據(jù)可視化的部分也有提過函卒,它們都代表了消費者的一個消費水平辆憔,消費能力越大,愿意或者說是會去選擇的酒店的平均價格就會越高报嵌,對酒店的星級要求也會越高虱咧。可以考慮將這幾個變量進行PCA降維锚国。
  • delta_price1和delta_price2的相關(guān)性高達0.91腕巡,同時和avgprice的相關(guān)性也大于0.7,針對這幾個指標可以抽象出一個指標叫做“用戶偏好價格”跷叉。
# 用戶特征提取(分兩次提取)
user_features=['hotelcr','hoteluv','commentnums','novoters','cancelrate','lowestprice','cr_pre','uv_pre','uv_pre2','businessrate_pre',
                'businessrate_pre2','customereval_pre2','commentnums_pre','commentnums_pre2','cancelrate_pre','novoters_pre','novoters_pre2',
                'deltaprice_pre2_t1','lowestprice_pre','lowestprice_pre2','firstorder_bu','historyvisit_visit_detailpagenum']
# 生成用戶特征的相關(guān)性矩陣
corr_mat=df1[user_features].corr()

# 繪制用戶特征的相關(guān)性矩陣熱度圖
plt.figure(figsize=(12,12))
sns.heatmap(corr_mat, xticklabels=True, yticklabels=True, square=False, linewidths=.5, annot=True, cmap='Blues')
image.png
  • novoters和commentnums相關(guān)性高達0.99逸雹,前者是當前點評人數(shù)营搅,后者是當前點評數(shù)云挟,可以抽象出“酒店熱度”指標;
  • novoters_pre和commentnums_pre相關(guān)性高達0.99转质,可以抽象出“24小時內(nèi)瀏覽次數(shù)最多的酒店熱度”指標园欣;
  • novoters_pre2和commentnums_pre2相關(guān)性高達0.99,可以抽象出“24小時內(nèi)瀏覽酒店平均熱度”指標休蟹;
  • cancelrate和hoteluv相關(guān)性0.76沸枯,和commentnums相關(guān)性0.84日矫,和novoters相關(guān)性0.85,酒店的“人氣”高绑榴,說明訪問的頻繁哪轿,歷史取消率可能也會高一點。
  • uv_pre和uv_pre2相關(guān)性高達0.9翔怎;businessrate_pre和businessrate_pre2相關(guān)性高達0.84窃诉;commentnums_pre和commentnums_pre2相關(guān)性高達0.82;novoters_pre和novoters_pre2相關(guān)性高達0.83赤套。這些指標之間都是“瀏覽最多的酒店的數(shù)據(jù)”和“瀏覽酒店的平均數(shù)據(jù)”的關(guān)系飘痛,相關(guān)性高是正常的,暫時不用抽象出其他的指標容握。
2.6 降維
c_value=['customer_value_profit','ctrip_profits']                   # 用戶價值
consume_level=['avgprice','consuming_capacity']                     # 用戶消費水平
price_prefer=['delta_price1','delta_price2']                        # 用戶偏好價格
hotel_hot=['commentnums','novoters']                                # 酒店熱度
hotel_hot_pre=['commentnums_pre','novoters_pre']                    # 24小時內(nèi)瀏覽次數(shù)最多的酒店熱度
hotel_hot_pre2=['commentnums_pre2','novoters_pre2']                 # 24小時內(nèi)瀏覽酒店的平均熱度

from sklearn.decomposition import PCA
pca=PCA(n_components=1)
df1['c_value']=pca.fit_transform(df1[c_value])
df1['consume_level']=pca.fit_transform(df1[consume_level])
df1['price_prefer']=pca.fit_transform(df1[price_prefer])
df1['hotel_hot']=pca.fit_transform(df1[hotel_hot])
df1['hotel_hot_pre']=pca.fit_transform(df1[hotel_hot_pre])
df1['hotel_hot_pre2']=pca.fit_transform(df1[hotel_hot_pre2])

df1.drop(c_value,axis=1,inplace=True)
df1.drop(consume_level,axis=1,inplace=True)
df1.drop(price_prefer,axis=1,inplace=True)
df1.drop(hotel_hot,axis=1,inplace=True)
df1.drop(hotel_hot_pre,axis=1,inplace=True)
df1.drop(hotel_hot_pre2,axis=1,inplace=True)
df1.drop('historyvisit_totalordernum',axis=1,inplace=True)  ###把重復的一列刪了
df1.drop('sampleid',axis=1,inplace=True)   ###把id列刪了
image.png

ps: pca用法http://www.reibang.com/p/8642d5ea5389

2.7 數(shù)據(jù)標準化
# 數(shù)據(jù)標準化
from sklearn.preprocessing import StandardScaler

y=df1['label']
x=df1.drop('label',axis=1)

scaler = StandardScaler()
X= scaler.fit_transform(x)   #先用fit求得訓練數(shù)據(jù)的標準差和均值,再用transform將數(shù)據(jù)轉(zhuǎn)化成

sklearn中標準化模塊的用法:https://blog.csdn.net/u011734144/article/details/84066784
幾種標準化的方法:
http://www.reibang.com/p/fa73a07cd750
考慮到基本都是負荷正態(tài)分布或者偏態(tài)分布的宣脉,所以這里是標準化為標準正態(tài)分布,否則的話剔氏,采用min-max標準化等其他方法可能會更好塑猖。
標準化后的數(shù)據(jù)形態(tài):

image.png

三、建模

先拆分訓練集和數(shù)據(jù)集

from sklearn import model_selection

X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size= 0.2,random_state=1)

PS:random_state相當于添加隨機數(shù)種子進行標記http://www.reibang.com/p/4deb2cb2502f

3.1 邏輯回歸
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
from sklearn.metrics import classification_report

lr = LogisticRegression()                                        # 實例化一個LR模型
lr.fit(X_train,y_train)                                          # 訓練模型
y_prob = lr.predict_proba(X_test)[:,1]                           # 預測1類的概率
y_pred = lr.predict(X_test)                                      # 模型對測試集的預測結(jié)果
fpr_lr,tpr_lr,threshold_lr = metrics.roc_curve(y_test,y_prob)    # 獲取真陽率介蛉、偽陽率萌庆、閾值
auc_lr = metrics.auc(fpr_lr,tpr_lr)                              # AUC得分
score_lr = metrics.accuracy_score(y_test,y_pred)                 # 模型準確率


print('模型準確率為:{0},AUC得分為:{1}'.format(score_lr,auc_lr))
print('  ')
print(classification_report(y_test, y_pred, labels=None, target_names=None, sample_weight=None, digits=2))
image.png
  • 此時的模型準確率是以分為0類、1類的概率大小進行分類的币旧,所以理論上通過調(diào)整分類閾值践险,可以達到更高的精度。
  • 在不對閾值進行調(diào)整情況下吹菱,從混淆矩陣中可以看出巍虫,1類的recall偏小,表明更容易被分為0類鳍刷,這種情況對應(yīng)的是ROC曲線中的左下方低點占遥,分為0類的閾值應(yīng)該調(diào)大,分為1類的閾值應(yīng)該調(diào)低输瓜。
3.2 樸素貝葉斯
from sklearn.naive_bayes import GaussianNB
from sklearn import metrics
from sklearn.metrics import classification_report

gnb = GaussianNB()                                                # 實例化一個LR模型
gnb.fit(X_train,y_train)                                          # 訓練模型
y_prob = gnb.predict_proba(X_test)[:,1]                           # 預測1類的概率
y_pred = gnb.predict(X_test)                                      # 模型對測試集的預測結(jié)果
fpr_gnb,tpr_gnb,threshold_gnb = metrics.roc_curve(y_test,y_prob)    # 獲取真陽率瓦胎、偽陽率、閾值
auc_gnb = metrics.auc(fpr_gnb,tpr_gnb)                              # AUC得分
score_gnb = metrics.accuracy_score(y_test,y_pred)                 # 模型準確率


print('模型準確率為:{0},AUC得分為:{1}'.format(score_gnb,auc_gnb))
print('  ')
print(classification_report(y_test, y_pred, labels=None, target_names=None, sample_weight=None, digits=2))

image.png

查看了一下預測的分為1類和0類的概率:
image.png

所以這里的貝葉斯概率并不是真實概率(真實概率為所有獨立變量概率的成績尤揣,理論上是一個很小很小的值搔啊,而不會是一個mean值在0.5左右的值)。
有空可以研究下他的算法代碼

3.3 支持向量機
from sklearn.svm import SVC
from sklearn import metrics
from sklearn.metrics import classification_report

svc = SVC(kernel='rbf',C=1,max_iter=100).fit(X_train,y_train)
y_prob = svc.decision_function(X_test)                              # 決策邊界距離
y_pred = svc.predict(X_test)                                        # 模型對測試集的預測結(jié)果
fpr_svc,tpr_svc,threshold_svc = metrics.roc_curve(y_test,y_prob)     # 獲取真陽率北戏、偽陽率负芋、閾值
auc_svc = metrics.auc(fpr_svc,tpr_svc)                              # 模型準確率
score_svc = metrics.accuracy_score(y_test,y_pred)

print('模型準確率為:{0},AUC得分為:{1}'.format(score_gnb,auc_gnb))
print('  ')
print(classification_report(y_test, y_pred, labels=None, target_names=None, sample_weight=None, digits=2))
image.png
  • 不設(shè)置max_iter會陷入死循環(huán),說明一直無法找到最優(yōu)解平面嗜愈。而且無論是rbf核還是多項式核旧蛾,整體的預測精度都很低莽龟。
3.4 決策樹
from sklearn import tree
from sklearn import metrics
from sklearn.metrics import classification_report

dtc = tree.DecisionTreeClassifier()                              # 建立決策樹模型
dtc.fit(X_train,y_train)                                         # 訓練模型
y_prob = dtc.predict_proba(X_test)[:,1]                          # 預測1類的概率
y_pred = dtc.predict(X_test)                                     # 模型對測試集的預測結(jié)果 
fpr_dtc,tpr_dtc,threshod_dtc= metrics.roc_curve(y_test,y_prob)   # 獲取真陽率、偽陽率锨天、閾值               
auc_dtc = metrics.auc(fpr_dtc,tpr_dtc)                           # AUC得分
score_dtc = metrics.accuracy_score(y_test,y_pred)                # 模型準確率

print('模型準確率為:{0},AUC得分為:{1}'.format(score_dtc,auc_dtc))
print('  ')
print(classification_report(y_test, y_pred, labels=None, target_names=None, sample_weight=None, digits=2))
image.png
3.5 隨機森林
from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics
from sklearn.metrics import classification_report

rfc = RandomForestClassifier()                                     # 建立隨機森林分類器
rfc.fit(X_train,y_train)                                           # 訓練隨機森林模型
y_prob = rfc.predict_proba(X_test)[:,1]                            # 預測1類的概率
y_pred=rfc.predict(X_test)                                         # 模型對測試集的預測結(jié)果
fpr_rfc,tpr_rfc,threshold_rfc = metrics.roc_curve(y_test,y_prob)   # 獲取真陽率毯盈、偽陽率、閾值  
auc_rfc = metrics.auc(fpr_rfc,tpr_rfc)                             # AUC得分
score_rfc = metrics.accuracy_score(y_test,y_pred)                  # 模型準確率

print('模型準確率為:{0},AUC得分為:{1}'.format(score_rfc,auc_rfc))
print('  ')
print(classification_report(y_test, y_pred, labels=None, target_names=None, sample_weight=None, digits=2))
image.png
3.6 模型比較
plt.style.use('bmh')
plt.figure(figsize=(13,10))

plt.plot(fpr_lr,tpr_lr,label='lr: {0:.3f}'.format(score_lr))                             # 邏輯回歸
plt.plot(fpr_gnb,tpr_gnb,label='gnb:{0:.3f}'.format(score_gnb))                          # 樸素貝葉斯模型
plt.plot(fpr_svc,tpr_svc,label='svc:{0:.3f}'.format(score_svc))                                             # 支持向量機模型
plt.plot(fpr_dtc,tpr_dtc,label='dtc:{0:.3f}'.format(score_dtc))                          # 決策樹
plt.plot(fpr_rfc,tpr_rfc,label='rfc:{0:.3f}'.format(score_rfc))                          # 隨機森林

plt.legend(loc='lower right',prop={'size':25})
plt.xlabel('誤診率')
plt.ylabel('靈敏度')
plt.title('ROC曲線')
image.png

隨機森林勝出病袄!

調(diào)用決策樹畫圖:
http://ywtail.github.io/2017/06/08/sklearn%E5%86%B3%E7%AD%96%E6%A0%91%E5%8F%AF%E8%A7%86%E5%8C%96/
本來想畫圖奶镶,但是又卡了,可能是因為變量太多了陪拘,想想也是厂镇。。

四左刽、RFM分析和用戶畫像

4.1 RFM分析

RFM模型捺信,即為:
R(Rencency):最近一次消費
F(Frequency):消費頻率
M(Monetary):消費金額


image.png

在本案例中,我們選擇lasthtlordergap(距離上次下單的時長)欠痴、ordernum_oneyear(用戶年訂單數(shù))迄靠、consume_level(用戶消費水平)分別作為R、F喇辽、M的值掌挚,對我們的用戶群體進行聚類。

rfm = df1[['lasthtlordergap','ordernum_oneyear','consume_level']]  #consume_level是PCA后的特征變量

#歸一化
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(rfm)
rfm = pd.DataFrame(scaler.transform(rfm),columns=['recency','frequency','monetary'])

# 分箱
rfm['R']=pd.qcut(rfm["recency"], 2)
rfm['F']=pd.qcut(rfm["frequency"], 2)
rfm['M']=pd.qcut(rfm["monetary"], 2)

# 根據(jù)分箱情況編碼
from sklearn.preprocessing import LabelEncoder

#從0開始編碼的菩咨,所以這里直接編碼是可以的
rfm['R']=LabelEncoder().fit(rfm['R']).transform(rfm['R'])      #這里需要注意吠式,R為距離上次下單的市場,越小則代表價值越高抽米,所以這一點是反的
rfm['F']=LabelEncoder().fit(rfm['F']).transform(rfm['F'])
rfm['M']=LabelEncoder().fit(rfm['M']).transform(rfm['M'])

def get_label(r,f,m):
    if (r==0)&(f==1)&(m==1):
        return '高價值客戶'
    if (r==1)&(f==1)&(m==1):
        return '重點保持客戶'
    if((r==0)&(f==0)&(m==1)):
        return '重點發(fā)展客戶'
    if (r==1)&(f==0)&(m==1):
        return '重點挽留客戶'
    if (r==0)&(f==1)&(m==0):
        return '一般價值客戶'
    if (r==1)&(f==1)&(m==0):
        return '一般保持客戶'
    if (r==0)&(f==0)&(m==0):
        return '一般發(fā)展客戶'
    if (r==1)&(f==0)&(m==0):
        return '潛在客戶'

def RFM_convert(df):
    df['Label of Customer']=df.apply(lambda x:get_label(x['R'],x['F'],x['M']),axis=1)
    
    df['R']=np.where(df['R']==0,'高','低')
    df['F']=np.where(df['F']==1,'高','低')
    df['M']=np.where(df['M']==1,'高','低')
    
    return df[['R','F','M','Label of Customer']]

rfm0=RFM_convert(rfm)
rfm0.head(10)
image.png

各類客戶的占比:

temp=rfm0.groupby('Label of Customer').size()

plt.figure(figsize=(12,12))
colors=['deepskyblue','steelblue','lightskyblue','aliceblue','skyblue','cadetblue','cornflowerblue','dodgerblue']
plt.pie(temp,radius=1,autopct='%.1f%%',pctdistance=0.75,colors=colors)
plt.pie([1],radius=0.6,colors='w')   ##可以用這種方式畫空心
plt.title('客戶細分情況')
plt.legend(temp.index)
image.png
  • 潛在客戶占比達12特占。3%,這類客戶是rmf指標均不是很好的客戶云茸,有待開發(fā)是目;
  • 高價值客戶11%,重點保持客戶10.1%,重點發(fā)展客戶7%标捺,這是要重點關(guān)注的客戶群體懊纳。
4.2 用戶畫像

其實我們并不想將用戶分的這么細,并且我們其實有挺多的用戶行為特征數(shù)據(jù)亡容,所以也并不想僅用RFM這3個指標進行分析嗤疯。所以,我們接下來用K-Means聚類的方法將用戶分為3類萍倡,觀察不同類別客戶的特征身弊。

# 選取出幾個刻畫用戶的重要指標
user_feature = ['decisionhabit_user','ordercanncelednum','ordercanceledprecent','consume_level','starprefer','lasthtlordergap','lastpvgap','h','sid',
                'c_value','landhalfhours','price_sensitive','price_prefer','day_advanced','historyvisit_avghotelnum','ordernum_oneyear']
user_attributes = df1[user_feature]
user_attributes.head()

# 數(shù)據(jù)標準化
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaler.fit(user_attributes)

user_attributes = scaler.transform(user_attributes)
from sklearn.cluster import KMeans

Kmeans=KMeans(n_clusters=3)                                                     # 建立KMean模型
Kmeans.fit(user_attributes)                                                     # 訓練模型
k_char=Kmeans.cluster_centers_                                                  # 得到每個分類的質(zhì)心
personas=pd.DataFrame(k_char.T,index=user_feature,columns=['0類','1類','2類'])  # 用戶畫像表

plt.figure(figsize=(5,10))
sns.heatmap(personas, xticklabels=True, yticklabels=True, square=False, linewidths=.5, annot=True, cmap='Blues')
image.png
  • 2類用戶的R(lasthtlordergap)為-0.17非常斜偬(R越小越好列敲,這里是反的)阱佛,F(xiàn)(ordernum_oneyear)為1.1比較高了,M(consume_level)為1.3也幾乎是最高的戴而。很明顯凑术,2類客戶為我們的“高價值客戶”;而0類中幾乎都是白格子所意,無論是客戶價值還是消費水平值都是最低的淮逊,很明顯扶踊,這一類我們將其歸為“低價值客戶”;剩下的1類我們將其稱為“中等群體”。
plt.figure(figsize=(9,9))

class_k=list(Kmeans.labels_)                          # 每個類別的用戶個數(shù)
percent=[class_k.count(1)/len(user_attributes),class_k.count(0)/len(user_attributes),class_k.count(2)/len(user_attributes)]   # 每個類別用戶個數(shù)占比

fig, ax = plt.subplots(figsize=(10,10))
colors=['aliceblue','steelblue','lightskyblue']
types=['中等群體','低價值用戶','高價值用戶']
ax.pie(percent,radius=1,autopct='%.2f%%',pctdistance=0.75,colors=colors,labels=types)
ax.pie([1], radius=0.6,colors='w')
image.png
  • 可以看到伏嗜,“低價值客戶”的占比非常之大弧械,中等人群占比最小。
4.3 用戶畫像分析
  • 高價值用戶分析(2類用戶)
    消費水平高抖甘,客戶價值大,追求高品質(zhì),對酒店星級要求高狱意,訪問頻率和預定頻率都較高,提前預定的時間都較短,決策一般都較快(日均訪問數(shù)少),訂單取消率較高,可以分析出這類客戶商務(wù)屬性偏重卵皂,可能隨時要出差,因此都不會提前預定刃泌,可能出差隨時會取消,因此酒店取消率也會更高一點狐援。sid的值較大,說明高價值客戶群體多集中在老客戶中。價格敏感度較高滓走,說明可能比較要求性價比。h值非常小帽馋,可能訪問和預定時間多在半夜或是清晨搅方。
    這部分客戶對于我們而言是非常重要的,因此我們需要對其實施個性化的營銷:
    1绽族、為客戶提供更多差旅酒店信息姨涡。
    2、多推薦口碑好吧慢、性價比高的商務(wù)酒店涛漂。
    3、推薦時間集中在半夜或是清晨检诗。
  • 中等價值用戶分析(1類用戶)
    消費水平和客戶價值都偏低怖喻,對酒店品質(zhì)也不太追求,訪問和預定頻率也都較高岁诉,提前預定的時間是三類中最長的锚沸,最值得注意的是,0類客戶中有兩個顏色非常深的藍色格子涕癣,是用戶決策和近3個月的日均訪問數(shù)哗蜈∏氨辏可以看出,這類客戶通常很喜歡逛酒店界面距潘,在決定要訂哪家酒店前通常會花費非常多的時間進行瀏覽才能做出選擇炼列,并且一般都會提前很久訂好房。我們可以給這類客戶打上“謹慎”的標簽音比。我們可以合理推斷俭尖,這一類客戶,可能預定酒店的目的多為出門旅行洞翩。
    針對這部分客戶稽犁,我們需要:
    1、盡可能多地進行推送骚亿,因為此類客戶通常比較喜歡瀏覽已亥。
    2、推送當?shù)芈糜钨Y訊来屠,因為這類客戶旅游出行的概率較大虑椎。
    3、多推薦價格相對實惠的酒店俱笛。
  • 低價值用戶分析(0類用戶)
    消費水平和客戶價值極低捆姜,對酒店品質(zhì)不追求,偏好價格較低迎膜,決策時間很短娇未,訪問和預定頻率很低,sid值很低星虹,說明新客戶居多零抬。
    針對這部分客戶,我們需要:
    1宽涌、不建議花費過多營銷成本平夜,但因為新用戶居多,屬于潛在客戶卸亮,可以維持服務(wù)推送忽妒。
    2、推送的內(nèi)容應(yīng)多為大減價兼贸、大酬賓段直、跳樓價之類的。
    3溶诞、此類用戶占比居多鸯檬,可進一步進行下沉分析,開拓新的市場螺垢。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喧务,一起剝皮案震驚了整個濱河市赖歌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌功茴,老刑警劉巖庐冯,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坎穿,居然都是意外死亡展父,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門玲昧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栖茉,“玉大人,你說我怎么就攤上這事酌呆『庠兀” “怎么了搔耕?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵隙袁,是天一觀的道長。 經(jīng)常有香客問我弃榨,道長菩收,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任鲸睛,我火速辦了婚禮娜饵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘官辈。我一直安慰自己箱舞,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布拳亿。 她就那樣靜靜地躺著晴股,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肺魁。 梳的紋絲不亂的頭發(fā)上电湘,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音鹅经,去河邊找鬼寂呛。 笑死,一個胖子當著我的面吹牛瘾晃,可吹牛的內(nèi)容都是我干的贷痪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼蹦误,長吁一口氣:“原來是場噩夢啊……” “哼呢诬!你這毒婦竟也來了涌哲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤尚镰,失蹤者是張志新(化名)和其女友劉穎阀圾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狗唉,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡初烘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了分俯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肾筐。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖缸剪,靈堂內(nèi)的尸體忽然破棺而出吗铐,到底是詐尸還是另有隱情,我是刑警寧澤杏节,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布唬渗,位于F島的核電站,受9級特大地震影響奋渔,放射性物質(zhì)發(fā)生泄漏镊逝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一嫉鲸、第九天 我趴在偏房一處隱蔽的房頂上張望撑蒜。 院中可真熱鬧,春花似錦玄渗、人聲如沸座菠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浴滴。三九已至,卻和暖如春也榄,著一層夾襖步出監(jiān)牢的瞬間巡莹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工甜紫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留降宅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓囚霸,卻偏偏與公主長得像腰根,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拓型,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容