天天看點

基于RFM模型實作的零售精準營銷響應預測系統

基于RFM模型實作的零售精準營銷響應預測系統

從交易資料中提取RFM特征,代碼:

◆ 1 導入資料
# Retail_Data_Transction.csv  #交易行為資料:customer_id,trans_date,tran_amount
# Retail_Data_Responce.csv    #營銷響應結果資料

#檢視交易資料表---營銷資料表
!head../資料/Retail_Data_Transction.csv
!head../資料/Retail_Data_Responce.csv

#1.1 導入相關包
import pandas as pd
import datetime as dt
import numpy as np

#檢視資料
df = pd.read_csv('head../資料/Retail_Data_Transction.csv',parse_dates = ['trans_date'])
df.head()

#1.2 提起R F M y原始特征值
#檢視交易最早-最晚日期
print(df['trans_date'].min())
print(df['trans_date'].max())

#1.3 設定目前時間
now = dt.datetime(2015,04,01)
#1.4 構造交易時間間隔變量 hist
df['hist'] = now - df['trans_date']
df['hist'].astype('timedelta64[D]')
df['hist'] = df['hist'] / np.timedelta64(1,'D')
df.head()

#假設隻分析近兩年的資料
#篩選 hist 變量值小于730 天的資料
df = df[df['hist'] < 730]

#1.5 接下來,對使用者進行彙總(groupby),生成R F M 特征變量   agg()分組函數
rfmTable = df.groupby('customer_id').agg(['hist':'min',             #Recency
                                          'customer_id':'count',    #Frequency
                                          'tran_amount':'sum'])     #Monetary
#1.6 對變量重命名
rfmTable.rename(columns = {'hist':'recency'
                           'customer_id':'frequency'
                           'tran_amount':'monetary'},inplace = True)
rfmTable.head()      
◆ 2 對 RFM 進行簡單探索性分析
rfmTable.describe()

 #客戶 RFM 分析
 #對客戶進行RFM原始值進行細分(離散化),得到RFM組合值,并與某營銷活動的響應結果關聯
#2.1 導入1-從交易資料中提取的RFM特征
%run'1-從交易資料中提取的RFM特征.ipynb'

#2.2 R F M 離散化(分成5等分,每等分20%客戶)
quantiles = rfmTable.quantile(q = [0.2,0.4,0.6,0.8])
quantiles

quantiles = quantile.to_dict()      #轉成字典,便于後續分段時調用
quantiles

'''
    定義RFM 分段函數
    x: 要分段的變量值
    P: 儲存rfm分位數的字典的 key 名稱,即 recency, frequency,monetary
    d: 儲存rfm分位數的字典
'''

#for recency
def Rclass(x,p,d):
    if x <= d[p][0.2]:
        return 5
    elif x <= d[p][0.4]:
        return 4
    elif x <= d[p][0.6]:
        return 3
    elif x <= d[p][0.8]:
        return 2
    else:
        return 1

#for frequency and monetary
def FMclass(x,p,d):
    if x <= d[p][0.2]:
        return 1
    elif x <= d[p][0.4]:
        return 2
    elif x <= d[p][0.6]:
        return 3
    elif x <= d[p][0.8]:
        return 4
    else:
        return 5

#對2.3 R,F,M 變量進行分段,結果儲存在 DataFrame 中(rfmSeg)分段後變量名: R_Seg,F_Seg,M_Seg
#對RFM進行分段離散化處理
rfmSeg = rfmTable
rfmSeg['R_Seg'] = rfmSeg['recency'].apply(Rclass,args = ('recency',quantiles))
rfmSeg['F_Seg'] = rfmSeg['frequency'].apply(FMclass,args = ('frequency',quantiles))
rfmSeg['M_Seg'] = rfmSeg['monetary'].apply(FMclass,args = ('monetary',quantiles))

#2.4 R,F,M組合成RFM(直接位數疊加)
rfmSeg['RFMScore'] = rfmSeg.R_Seg.map(str) + F_Seg.map(str) + M_Seg.map(str)
rfmSeg.head()

#2.5 對RFM分數值降序排列
rfmSeg.sort_values(by = ['RFMScore','monetary'],ascending = [False,False]).head()      
◆ 3 R,F,M 與響應率的關系
#導入響應資料
response = pd.read_csv('../資料/Retail_Data_Responce.csv')
response.sort_values('customer_id',inplace = True)
response.head()

rfmSeg.reset_index(inplace = True)
rfm.head()

#把 rfmSeg 和 response 按照 customer_id 整合在一張表中
rfmSeg.sort_values('customer_id',inplace = True)
rfm_response = pd.merge(rfmSeg,response,on = 'customer_id')
rfm_response.head()

%matplotlib inline
#Recency VS Response
ax = rfm_response.groupby('R_Seg').agg('response').mean().plot(kind = 'bar',colormap = 'Blues_r')
ax.set_xlabel('R_Seg')
ax.set_ylabel('Proportion of Responders')

#Frequency VS Response
ax = rfm_response.groupby('F_Seg').agg('response').mean().plot(kind = 'bar',colormap = 'Blues_r')
ax.set_xlabel('F_Seg')
ax.set_ylabel('Proportion of Responders')

#Monetary VS Response
ax = rfm_response.groupby('M_Seg').agg('response').mean().plot(kind = 'bar',colormap = 'Blues_r')
ax.set_xlabel('M_Seg')
ax.set_ylabel('Proportion of Responders')      
◆ 4 響應預測模型訓練和選擇
%run'2-客戶RFM分析.ipynb'

#4.1 準備資料
#直接使用沒有離散化的 recency,frequency,monetary 三個特征作為 X 變量去預測變量 response

# 準備資料集
from sklearn.model_selection import train_test_split
X = rfm_response[['recency','frequency','monetary']]
y = rfm_response['response']
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_stats = 1)

#4.2 對比不同的分類算法
    # 邏輯回歸
    # 決策樹
    # 支援向量機
    # K近鄰
    # 樸樹貝葉斯
    # 随機森林
    # Adoboost
    # 梯度提升決策樹
    # 多層感覺器

import pandas as pd
import numpy as np
from sklearn.neighboer import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier,AdoboostClassifier,GridientBoostingClassifier
from sklearn.naive_bayes import GaussionNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_curve
from sklearn.metrics import anc

import matplotlib.pyplot as plt
%matplotlib inline

#循環對9個模型進行訓練
classifiers = [
    KNeighborsClassifier(n_neighbors = 5),
    LogisticRegression(),
    SVC(kernel = 'rbf',C = 0.01,probability = True)
    DecisionTreeClassifier(),
    RandomForestClassifier(),
    AdoboostClassifier(),
    GridientBoostingClassifier(),
    GaussionNB(),
    MLPClassifier()]

#計劃用 pandas 的 dataframe 存放每一個模型的 accuracy 和 AUC 值,結果儲存在 result 中
cols = ['Classifier','Accuracy','AUC']
result = pd.DataFrame(columns = cols)

#對9 個分類模型進行循環處理,依次輸出他們的 accuracy 和 AUC 值,結果儲存在 result 中
for clf in classifiers:
    clf.fit(X_train,y_train)
    name = clf.__class__.__name__

    print('='*30)
    print(name)

    print('*******Result**********')
    y_pred = clf.predict(X_test)
    acc = accuracy_score(y_test,y_pred)
    print('Accuracy:[:,2%]'.format(acc))

    y_pred = clf.predict_proba(X_test)
    fpr,ypr,thresholds = roc_curve(y_test,y_pred[:,1])
    auc_value = auc(fpr,tpr)
    print('AUC:{0:0.2f}'。format(auc_value))

    #把目前循環分類器結果儲存在 result_clf 的dataframe中
    result_clf = pd.DataFrame([name,acc*100,auc_value],columns = cols)
    #把 result_clf 合并在 result 中
    result = result.append(result_clf)

print('='*30)


#評估選擇最好的模型
#檢視不同模型的 auc 和 accuracy
result

#選擇最好的算法 GBDT 重新訓練模型
favorite_clf = GridientBoostingClassifier()
favorite_clf.fit(X_train,y_train)

#用收益曲線來評估模型效果
#計算收益表
def GainTable(y_true_int,y_pred_prob):
    #y_true_int: 真實值,正例1,負例0
    #y_pred_prob:正例預測機率
    data = pd.DataFrame({'y_true':y_true_int,'prob':y_pred_prob})
    data['prob'] = 1-data['prob']
    #計算十分位數
    data['percentile_gp'] = pd.qcut(data['prob'],q = 10,labels = range(10))
    #按十分位分組
    deciles = data.groupby('percentile_gp',sort = True)
    #定義函數計算執行個體總數
    def total_count(x):return len(x)
    #定義函數計算實正例數
    def pos_count(x):return np.sum(x)
    #定義函數計算實正例率
    def pos_rate(x):return np.sum(x) / float(len(x))
    out = deciles['y_true'].agg([total_count,pos_count,pos_rate])
    #計算負例數
    out['neg_count'] = out['tatol_count'] - out['pos_count']
    #計算累計正例數
    out['pos_cumsum'] = out['pos_count'].columns()
    total_sum = out['pos_cumsum'] / float(total_sum)
    out['percentile'] = (out.index.astype(int) + 1) / 10.0
    return out[['percentile','total_count','pos_count','neg_count','pos_rate','pos_cover_rate']]

#繪制收益曲線
from matplotlib import pyplot as plt
%matplotlib inline
def plotGainChart(GainTable):
    plt.plot(GainTable['percentile'],GainTable['pos_cover_rate'],'g-')
    plt.plot(GainTable['percentile'],GainTable['pos_cover_rate'],'r--')
    plt.legend(['model','random'])
    plt.show()

#對測試集 X_test 進行預測,得到正例的預測機率
y_prob = favarite_clf.predict_proba(X_test)[:,1]        #取标簽等于1的機率
#計算收益表
gaintable = GainTable(y_test,y_prob)
gaintable

#繪制收益曲線      
◆ 5 儲存模型
    #有兩種持久化的方法:
    # 通過pickle.dump() 方法把模型儲存為檔案
    # 通過pickle.load() 方法把模型讀取(加載)為檔案

import pickle
with open('response_model.pickle','wb') as fw:
    pickle.dump(favorite_clf,fw)
#調用系統命名 ls 檢視目前目錄下的檔案      

————————–部署和應用———————–

◆ 1 讀取模型檔案
 #檢視目前目錄下的檔案
!ls

 #response_model.pickle 為儲存模型檔案
import pickle
with open('response_model.pickle','rb') as fr:
    model = pickle.load(fr)

◆ 2 預測并儲存結果
 #2.1 對新資料集提取RFM特征值

import pandas as pd
import datetime as dt
import numpy as np

 #導入新資料
df = .pd.read_csv('../資料/Retail_Data_Transction。csv',parse_dates = ['trans_date'])
df.head(5)

 #設定目前時間 now = 2015-04-01
now = dt.datetime(2015,4,1)
df['hist'] = now - df['trans_date']
df['hist'].astype('timedelta64')
df['hist'] = df['hist'] / np.timedelta64(1,'D')
df.head()

 #2.2 模型預測
使用model對處理好的資料進行預測,預測結果傳回0 或1 機率
我們隻取第二列的機率值(即1的機率)
prediction = model.predict_proba(rfmTable)[:,1]
prediction = pd.DataFrame(prediction,columns = ['response_proba'])
prediction.head()

 #把預測結果合并到rfmTable中去
prediction = pd.concat([rfmTable,reset_index(drop = False),prediction],axis = 1)
prediction = prediction.set_index('customer_id')    #customer_id作為索引
prediction = prediction.sort_values(by = ['response_proba'],ascending = False)  
            #按照 response_proba排序
prediction.head()

 #2.3 篩選目标客戶
 #基于預測結果,我們按照 response_proba 降序排列之後,
  我們可以篩選一定比例的目标客戶作為精準營銷對象
 #篩選 top20% 的客戶作為目标客戶
records = len(prediction)           #擷取資料集的總行數
rarget_records = int(0.2*records)   #目标客戶的records
target_customer = prediction.iloc[:rarget_records,] #篩選 rarget_records 行
target_customer.head()

 #到處 csv 檔案
target_customer.to_csv('target_customer.csv')
 #檢視導出的 target_customer.csv 檔案