基於RFM的用戶價值度分析


 

案例完整代碼、數據見Github

 

1. 案例背景

用戶價值細分是了解用戶價值度的重要途徑,常用的細分模型包括:基於屬性的方法、ABC分類法、聚類法等。

1. 基於屬性的方法

常用的細分屬性包括:地域、產品類別、用戶類別(大客戶、普通客戶、VIP客戶等)、性別、消費等級等。這種細分方法可根據數據庫中數據直接得到。

2. ABC分類法

ABC法則是二八法則衍生出的一種法則。不同的是,二八法則強調是抓住關鍵,ABC法則強調分清主次,將管理對象划分為A、B、C三類。

在ABC法中先將目標數據列倒敘排序,然后做累積百分比統計,最后將百分比在0%-80%划分為A類,80%-90%划分為B類,90%-100%划分為C類。

例:

3. 聚類法

無需任何先驗經驗,只要指定要划分的群體數量即可。 

 

 

2. 案例主要應用技術

本案例沒有直接使用成熟模型包,而是通過 Python 代碼手動實現 RFM 模型。

RFM 模型是根據會員最近一次購買時間 R(Recency)、購買頻率 F(Frequency)、購買金額 M(Monetary)計算得出 RFM 得分。

RFM模型基本實現過程:

步驟1:設置截止時間節點(例如2020-6-28)。

步驟2:以今天為時間界限,向前推固定周期(例如1年)。

步驟3數據預計算。找出各個會員最近購買時間;以會員ID為維度統計每個用戶購買頻率,將用戶多個訂單的金額求和得到總訂單金額。由此得到R、F、M三個原始數據量。

步驟4R、F、M分區。對R、F、M分別使用五分位法(三分位也可以,分位數越多划分越詳細)做數據區分。需要注意的是,對 R 需要倒過來划分。因為對F、M來說,值越大代表購買頻率,訂單金額越高,對R來說,值越小代表離截止時間越近,我們需要倒過來划分,離截止時間越近的值划分越大。

步驟5將三個值組合或相加得到總的RFM得分。RFM總得分的兩種計算方式,一種直接將三個值拼接到一起,例如RFM得分為312、333、132;另一種將三個值相加得到一個新的匯總值,例如RFM得分為6、9、6。

 

根據步驟5產生的兩種結果有不同的應用思路:

思路1:基於三個維度值做用戶群體划分和解讀,對用戶的價值度做分析。例如得分為212會員購買頻率低,針對購買頻率低的客戶定期發送促銷活動郵件;針對得分為321的會員購買頻率高但訂單金額低,可以考慮通過關聯或搭配銷售方式提升金額。

思路2:基於 RFM 的匯總得分評估會員的價值度,並可以做價值度排名;同時,該得分還可以作為輸入維度跟其他維度一起作為其他數據分析或數據挖掘的輸入變量。

 

 

3. 案例數據

數據概況:

  • 特征變量數:4
  • 數據記錄數:86135
  • 是否有NA值:有
  • 是否有異常值:有

數據集的4個特征變量:

  • USERID:用戶ID
  • ORDERDATE:訂單日期
  • ORDERID:訂單ID
  • AMOUNTINFO:訂單金額

 

 

4. 案例過程

import time
import numpy as np
import pandas as pd
import pymysql


# 讀取原始數據
dtypes = {'ORDERDATE': object, 'ORDERID': object, 'AMOUNTINFO': np.float}
raw_data = pd.read_csv('sales.csv', dtype=dtypes, index_col='USERID')


# 數據概覽、缺失值審查
print(raw_data.describe())
""" 對DataFrame來說,describe()默認情況下,只返回數字字段。 describe(include='all')返回數據的所有列。 """

從結果看出,最大值30000元,最小值0.5元,經過溝通,最大值正常,為某客戶一次性購買多個大型電商品;0.5元訂單屬於促銷優惠券生成的訂單,這些訂單為用戶消費時的優惠券,沒有實際意義,因此可以去掉這些數據,所有低於1元的訂單均有這個問題。

 

 

na_cols = raw_data.isnull().any(axis=0)     # 查看每一列是否有缺失值
print(na_cols)
na_lines = raw_data.isnull().any(axis=1)    # 查看每一行是否有缺失值
print('總的NA行數:{}'.format(na_lines.sum()))
print(raw_data[na_lines])   # 查看具有缺失值的行信息

 

 

# 異常值處理
sales_data = raw_data.dropna()
sales_data = sales_data[sales_data['AMOUNTINFO'] > 1]   # 丟棄金額≤1
# 日期格式轉換
sales_data['ORDERDATE'] = pd.to_datetime(sales_data['ORDERDATE'], format='%Y-%m-%d')
""" format參數以原始數據字符串格式來寫,只有格式對應才能實現解析 """
print(sales_data.dtypes)

日期轉換的目的是計算時間間隔,算出 R 距離指定日期的天數。 

 

 

計算RFM得分

# 計算R、F、M
recency_value = sales_data['ORDERDATE'].groupby(sales_data.index).max()     # 計算最近一次訂單時間
frequency_value = sales_data['ORDERDATE'].groupby(sales_data.index).count() # 計算頻率
monetary_value = sales_data['AMOUNTINFO'].groupby(sales_data.index).sum()   # 計算總金額
"""
groupby() 函數可以進行數據的分組以及分組后的組內運算。
print(df["評分"].groupby([df["地區"],df["類型"]]).mean())
該條語句的功能:輸出數據中不同地區不同類型的評分的平均值。
"""

這三行代碼都是以原始數據框的索引為主鍵(以用戶 ID 為匯總維度)分別對 ORDERDATE 求最大值、對 ORDERDATE 做計數統計、對 AMOUNTINFO 求和,得到 R、F、M 的原始值。

 

 

# 計算R、F、M得分
deadline_date = pd.datetime(2017, 1, 1)     # 指定時間點,計算其他時間與該時間的距離
r_interval = (deadline_date - recency_value).dt.days            # 計算 R 間隔
r_score = pd.cut(r_interval, 5, labels=[5, 4, 3, 2, 1])         # 計算 R 得分
f_score = pd.cut(frequency_value, 5, labels=[1, 2, 3, 4, 5])    # 計算 F 得分
m_score = pd.cut(monetary_value, 5, labels=[1, 2, 3, 4, 5])     # 計算 M 得分

這里定義時間節點2017-1-1,通過數據框相減得到時間間隔天數對象,並對該對象使用 dt.days 方法獲得天數數值。

然后對 R、F、M 使用分位數法做間隔划分,這里使用 pd.cut(),默認設置為5份,通過 labels 標簽指定區間標簽(注意R的標簽與F、M標簽順序相反)。

 

 

# 合並數據框
rfm_list = [r_score, f_score, m_score]
rfm_cols = ['r_score', 'f_score', 'm_score']    # 設置R、F、M三個維度列名
rfm_pd = pd.DataFrame(np.array(rfm_list).T, dtype=np.int32, columns=rfm_cols, index=frequency_value.index)    # 建立R、F、M數據框
""" np.array().transpose()等價於np.array().T都表示數組的轉置 dtype=np.int32 等價於 dtype='int32' """

先建立R、F、M三個維度的值列表和名稱列表,用於生成數據框時指定數據和標簽。

然后使用 pd.DataFrame 建立數據框,設置列名並指定索引為用戶 ID(由於R、F、M三個Series的索引相同,這里隨便指定為 frequency_value.index)。

使用 np.array 將列表轉換為矩陣,此時矩陣形狀(3,59676),使用 transpose 轉置。

 

 

# 計算RFM總得分
# 方法一:加權得分
rfm_pd['rfm_wscore'] = rfm_pd['r_score']*0.6 + rfm_pd['f_score']*0.3 + rfm_pd['m_score']*0.1
# 方法二:RFM組合
rfm_pd_tmp = rfm_pd.copy()
rfm_pd_tmp['r_score'] = rfm_pd_tmp['r_score'].astype('str')
rfm_pd_tmp['f_score'] = rfm_pd_tmp['f_score'].astype('str')
rfm_pd_tmp['m_score'] = rfm_pd_tmp['m_score'].astype('str')
rfm_pd['rfm_comb'] = rfm_pd_tmp['r_score'].str.cat(rfm_pd_tmp['f_score']).str.cat(rfm_pd_tmp['m_score'])

print(rfm_pd.head())

方法二中,由於原始數據為了加權計算,設置為數值型,因此這里轉換為字符串型。 為不影響原始數據,通過 copy() 得到一個副本,然后將副本中的三列轉換為字符串型。

提示:設置數據類型時一般兩種思路:一種是創建數據框時通過 dtype 指定,第二種是在創建好的數據框中使用 astype() 轉換。

 

 

建立數據庫連接

# 連接mysql數據庫
# 設置要寫庫的數據庫連接信息
table_name = 'sales_rfm_score'      # 要寫庫的表名
# 數據庫基本信息
config = {'host': '127.0.0.1',
          'user': 'root',
          'password': '****',
          'port': 3306,
          'database': 'python_data',
          'charset': 'gb2312'}
con = pymysql.connect(**config)     # 建立mysql連接
cursor = con.cursor()               # 獲得游標
# 查找數據庫是否存在目標表,如果沒有則新建
cursor.execute('show tables')
table_object = cursor.fetchall()    # 通過fetchall方法獲取數據
table_list = []                     # 創建庫列表
for t in table_object:              # 循環讀出多有庫
    table_list.append(t[0])         # 每個庫追加到列表
if not table_name in table_list:    # 如果目標表沒有創建
    cursor.execute("""
    CREATE TABLE %s (
    userid      VARCHAR (20),
    r_score     int(2),
    f_score     int(2),
    m_score     int(2),
    rfm_wscore  DECIMAL (10, 2),
    rfm_comb    VARCHAR (10),
    insert_date VARCHAR (20)
    )ENGINE=InnoDB DEFAULT CHARSET=gb2312
    """ % table_name)               # 創建新表
# 將數據寫入數據庫
user_id = rfm_pd.index  # 索引列
rfm_wscore = rfm_pd['rfm_wscore']   # RFM加權得分列
rfm_comb = rfm_pd['rfm_comb']       # RFM組合得分列
timestamp = time.strftime('%Y-%m-%d', time.localtime(time.time()))      # 寫庫日期
print('Begin to insert data into table {0}...'.format(table_name))     # 輸出開始寫庫的提示信息
for i in range(rfm_pd.shape[0]):    # 設置循環次數並依次循環
    insert_sql = "INSERT INTO `%s` VALUES ('%s',%s,%s,%s,%s,'%s','%s')" % \
                 (table_name, user_id[i], r_score.iloc[i], f_score.iloc[i], m_score.iloc[i], rfm_wscore.iloc[i],
                  rfm_comb.iloc[i], timestamp)  # 寫庫SQL依據
    cursor.execute(insert_sql)                  # 執行SQL語句,execute函數里面要用雙引號
    con.commit()    # 提交命令
cursor.close()      # 關閉游標
con.close()         # 關閉數據庫連接
print('Finish inserting, total records is: %d' % (i + 1))  # 打印寫庫結果

 

使用 Navicat 打開對應的數據庫表,如圖:

 

 

5. 案例結論

在RFM划分時,將區間划分為5份,定義為:高、中、一般、差、非常差5個級別,分別對應R、F、M中的5/4/3/2/1。

基於RFM得分業務方得到這樣的結論:

1. 公司的會員99%以上的客戶消費狀態不容樂觀,主要體現在消費頻率低R、消費金額低M。——經過分析,主要由(ID為74270)的客戶消費金額非常高影響的,導致做5分位時收到最大值的影響,區間向大值域偏移。

2. 公司中一些典型客戶的整個貢獻特征明顯,重點是 RFM 為555的用戶(ID為74270)。

3. 本周表現處於一般水平以上的用戶的比例(RFM三個維度均在3以上)相對上周環比增長了1.3%。體現了活躍度的提升。

4. 本周低價值(RFM得分為111以上)用戶名單中,新增1221個新用戶。

 

結論的應用和部署:

1. ID為74270客戶給與重點關懷和管理

2. 新增客戶也需要重點關注和處理。最主要的還是通過會員渠道拉動會員,防止客戶流失,訂單金額是次要因素。

3. 每周一根據上周新數據運行一次該模型。

 

案例注意點:

1. 具體划分幾個區間,需要跟業務方確認。一般划分為3-5個區間。

2. 權重的確認,不同時期業務關注點不同,權重需要調整。

3. ID為74270的用戶,這種極值影響需要跟業務部門溝通確認才能進行處理,否則影響區間划分。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM