iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 2
1
AI & Data

一服見效的 AI 應用系列 第 2

Day 02:客戶分群(Customer Segmentation) -- 那些客戶是VIP?

  • 分享至 

  • xImage
  •  

前言

當公司對外行銷或提供服務時,總會希望對VIP客戶特別照顧(大小眼?),因為他們對公司的貢獻度特別大,那我們如何衡量貢獻度或『客戶終身價值』(Customer Lifetime Value, CLV)呢? 最簡單的方法就是統計每個客戶的購買金額,金額越高的就是VIP。真的是這樣嗎? 以筆者本身為例,我要結婚時,到朋友介紹的家具行購買全套的家具,購買金額不低,但後來就沒有再光顧該店了,因為離家太遠了。所以,我們應該加一點『特徵工程』(Feature Engineering),將單純的銷售記錄轉換成更有意義的指標。

https://ithelp.ithome.com.tw/upload/images/20190910/20001976rHJBQdB9FN.png
圖片來源:善用「終身價值類似受眾」,創造最大收益率

RFM

在CRM(客戶關係管理)系統中,通常會將銷售記錄轉化為RFM(Recency, Frequency, Monetary) 三個面向來衡量客戶的貢獻度,說明如下:

  1. 最近購買日期(Recency):從『時間』維度觀察,以筆者上述的例子,筆者就應該被排除在VIP之外(雖然我很想)。
  2. 購買頻率(Frequency):從『頻率』維度觀察,單次金額可能不高,但次數多,累積起來,可能也很可觀,同時,也表示他們的忠誠度也很高,常來光顧,可以帶動人氣。
  3. 購買金額(Monetary):從『金額』維度觀察,這是當然的指標。

綜合以上三個指標,計算分數,找出總分比較高的族群強力行銷,以攻佔他們的荷包(Wallet Share)。

One-button RFM analysis
圖片來源:One-button RFM analysis

傳統作法

如果以傳統的作法,我們可以將顧客終身價值以三個指標構成一個公式如下:
顧客終身價值(CLV or LTV)=平均貢獻區間 x(一年總週數 x 每次平均消費金額 x 一週平均造訪次數 x 每位顧客的平均毛利率)

詳細說明請參考『顧客終身價值是什麼?把握三種常見的LTV公式幫助預算評估』

這種作法簡單直覺,每一個指標的重要性都相等,不需要複雜的計算,但是,缺點也很明顯,如果把消費金額的單位由元改為萬元,或造訪次數由週改為年,計算的結果就大相逕庭了,因此,一般會使用集群(Clustering)演算法,以求得更完善的模型。

註: 『Understanding Customer Lifetime Value In Retail』 有更細緻的計算公式介紹,有興趣的讀者可自行參閱。

機器學習的作法

開始之前,先介紹機器學習的處理流程,共10個步驟如下:
https://ithelp.ithome.com.tw/upload/images/20190910/20001976w9Zwsqyfpr.png
圖片來源:Free Machine learning diagram,筆者作了局部的修改。

  1. 收集資料,建立資料集(Dataset)。
  2. 資料清理(Data Cleaning)、資料探索與分析(Exploratory Data Analysis, EDA)。
  3. 特徵工程(Feature Engineering)。
  4. 資料切割(Data Split):切割為訓練資料(Training Data)及測試資料(Test Data)。
  5. 選擇演算法(Learning Algorithms),以建立模型。
  6. 模型訓練(Model Training)。
  7. 模型計分(Score Model):計算準確度,衡量模型效能。
  8. 模型評估(Evaluate Model):比較多個模型優劣或參數調校。
  9. 新系統上線:移轉模型至正式環境。
  10. 新資料預測(Predict)。

之後的案例都會依據上述步驟,按部就班的進行。

案例實作

筆者參考『RFM-Analysis』一文,它使用的是 UCI 提供的一份線上零售記錄(Online Retail Data Set),為了便於說明,我將流程及資料簡化,以利讀者聚焦在RFM處理與客戶分群。

收集資料

首先,我們先要上述資料集簡化,只選取英國的銷售資料,且僅包括以下欄位:

  1. 顧客代碼
  2. 購買日期
  3. 發票金額

讀取檔案的程式如下,將日期轉成年月,以便作月統計:

import pandas as pd
import numpy as np
df = pd.read_csv("./data.csv",converters={'CustomerID':str})
# 轉成年月
df['date']=df.InvoiceDate.astype(np.str).str.slice(0,8).str.replace('-','')
df['date'] = pd.to_numeric(df['date'], errors='coerce')

特徵工程

我事先已完成資料清理步驟,直接進行特徵工程,依據購買日期、發票金額,分別統計 -- 最近購買日期(Recency)、購買頻率(Frequency)及購買金額(Monetary)。

  1. 計算最近購買日期(Recency):將日期分成五個時段,找到每個客戶最近購買的日期。
# 計算最近購買日期(Recency)
def f(row):
    if row['date'] > 201110:
        val = 5
    elif row['date'] <= 201110 and row['date'] > 201108:
        val = 4
    elif row['date'] <= 201108 and row['date'] > 201106:
        val = 3
    elif row['date'] <= 201106 and row['date'] > 201104:
        val = 2
    else:
        val = 1
    return val
df_recency=df[['CustomerID','date']].drop_duplicates()
df_recency['Recency_Flag'] = df_recency.apply(f, axis=1)
df_recency = df_recency.groupby('CustomerID',as_index=False)['Recency_Flag'].max()
  1. 計算購買頻率(Frequency)。
Cust_freq=df[['InvoiceNo','CustomerID']].drop_duplicates()
#Calculating the count of unique purchase for each customer
Cust_freq_count=Cust_freq.groupby(['CustomerID'])['InvoiceNo'].aggregate('count').\
reset_index().sort_values('InvoiceNo', ascending=False, axis=0)

# Dividing in 5 equal parts
unique_invoice=Cust_freq_count[['InvoiceNo']]
unique_invoice['Freqency_Band'] = pd.qcut(unique_invoice['InvoiceNo'], 5)
unique_invoice=unique_invoice[['Freqency_Band']].drop_duplicates()

將頻率分成五個區間:

def f2(row):
    if row['InvoiceNo'] < 1:
        val = 1
    elif row['InvoiceNo'] <= 2:
        val = 2
    elif row['InvoiceNo'] <= 3:
        val = 3
    elif row['InvoiceNo'] <= 6:
        val = 4
    else:
        val = 5
    return val
    
Cust_freq_count['Freq_Flag'] = Cust_freq_count.apply(f2, axis=1)
  1. 計算購買金額(Monetary)。
#Calculating the Sum of total monetary purchase for each customer
Cust_monetary = df.groupby(['CustomerID'])['Total_Price'].aggregate('sum').\
reset_index().sort_values('Total_Price', ascending=False)
# splitting Total price in 5 parts
unique_price=Cust_monetary[['Total_Price']].drop_duplicates()
unique_price=unique_price[unique_price['Total_Price'] > 0]
unique_price['monetary_Band'] = pd.qcut(unique_price['Total_Price'], 5)
unique_price=unique_price[['monetary_Band']].drop_duplicates()

將購買金額分成五個區間

def f3(row):
    if row['Total_Price'] <= 243:
        val = 1
    elif row['Total_Price'] > 243 and row['Total_Price'] <= 463:
        val = 2
    elif row['Total_Price'] > 463 and row['Total_Price'] <= 892:
        val = 3
    elif row['Total_Price'] > 892 and row['Total_Price'] <= 1932:
        val = 4
    else:
        val = 5
    return val
Cust_monetary['Monetary_Flag'] = Cust_monetary.apply(f3, axis=1)
  1. 合併RFM欄位
Cust_All=pd.merge(df_recency,Cust_freq[['CustomerID','Freq_Flag']], on=['CustomerID'],how='left')
Cust_All=pd.merge(Cust_All,Cust_monetary[['CustomerID','Monetary_Flag']], on=['CustomerID'],how='left')
Cust_All.head(10)

待續

為免篇幅過長,造成讀者消化不良,明天再續。

相關程式碼放在這裡 的 Day02 Customer Segmentation 目錄。


上一篇
Day 01:一服見效的 AI 應用
下一篇
Day 03:客戶分群(Customer Segmentation) -- 那些客戶是我的VIP? (續)
系列文
一服見效的 AI 應用14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言