寫在前面: 博主是一名軟件工程系大數據應用開發專業大二的學生,暱稱來源於《愛麗絲夢遊仙境》中的Alice和自己的暱稱。作爲一名互聯網小白,
寫博客一方面是爲了記錄自己的學習歷程,一方面是希望能夠幫助到很多和自己一樣處於起步階段的萌新
。由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!個人小站:http://alices.ibilibili.xyz/ , 博客主頁:https://alice.blog.csdn.net/
儘管當前水平可能不及各位大佬,但我還是希望自己能夠做得更好,因爲一天的生活就是一生的縮影
。我希望在最美的年華,做最好的自己
!
在上一篇博客《一文帶你硬核踏入機器學習的大門》中,已經爲大家介紹了很多關於機器學習的基礎內容。本篇博客,我們將結合當前階段正在做的用戶畫像項目,爲大家介紹RFM模型和KMeans聚類算法。
先贊後看,養成習慣!
文章目錄
一、RFM模型引入
比如電商網站要做一次營銷活動,需要針對不同價值的客戶羣體進行分羣,對於高價值的用戶推薦手錶,珠寶等高端商品,對於低價值用戶推薦打折促銷的廉價商品,當然還有以下這些問題都是需要考慮的:
1.誰是我的最佳客戶?
2.誰即將要成爲流失客戶?
3.誰將有潛力成爲有價值的客戶
4.哪些客戶能夠留存?
5.哪些客戶會對你目前對活動有所反應?
那麼最終的問題是如何對客戶進行分羣,即如何建立客戶的價值模型呢?
在傳統企業和電商衆多的客戶細分模型中,RFM模型是被廣泛提到和使用的。
RFM模型是衡量當前用戶價值和客戶潛在價值的重要工具和手段。
RFM是Rencency(最近一次消費),Frequency(消費頻率)、Monetary(消費金額),三個指標首字母組合,如圖所示:
一般情況下RFM模型可以說明下列幾個事實:
1.最近購買的時間越近,用戶對產品促銷互動越大
2.客戶購買的頻率越高,客戶就品牌的滿意度就越大
3.貨幣價值將高消費客戶和低消費客戶區分開來
如圖所示,根據RFM模型,就可以統計在某一段時間內,用戶最近的消費間隔,消費次數和消費金額,再根據使用 k-means 算法對用戶進行聚類分羣。
注意一點,不僅僅可以侷限於這三個數據字段,還可以根據業務需求,加入其他字段,進行調整模型。
我們可以根據RFM模型計算出所有用戶的RFM值形成一個二維表:
userid | R值 | F值 | M值 |
---|---|---|---|
1 | 2019-11-01 | 5 | 10000 |
2 | 2019-10-01 | 4 | 800 |
對於以上數據的量綱不一致(單位不統一),所以要對數據進行歸一化
如何歸一化? 我們可以自定義歸一化的規則!即建立一個評分標準。
那如何建立評分標準?我們可以根據運營/產品的經驗,做一個標準,就像這樣:
R: 1-3天=5分,4-6天=4分,7-9天=3分,10-15天=2分,大於16天=1分
F: ≥200=5分,150-199=4分,100-149=3分,50-99=2分,1-49=1分
M: ≥20w=5分,10-19w=4分,5-9w=3分,1-4w=2分,<1w=1分
根據上面的打分規則就可以對數據進行自定義的歸一化,得到如下類似結果:
userid | R值 | F值 | M值 |
---|---|---|---|
1 | 5 | 1 | 2 |
2 | 1 | 1 | 1 |
那麼現在數據已經歸一化了,如何對數據進行分類?
肯定不能簡單的將數據直接丟到三維座標系,因爲座標系的原點不好確定,且三維座標系只能分爲8類。所以應該使用算法進行分類(聚類)。讓算法自動學習用戶之間的相似度,然後相似度高的用戶,自動聚成一類,最後完成聚類的劃分。
計算流程
1、首先對所有用戶的最近一次消費時間/總共消費次數/總共消費金額進行統計
2、再進行歸一化(運營/產品提供的打分規則)
3、再使用算法進行聚類(K-Means)
4、根據聚類結果給用戶打Tag(標籤)
1、RFM詳解
1.1 R值:最近一次消費(Recency)
消費指的是客戶在店鋪消費最近一次和上一次的時間間隔,理論上R值越小的客戶是價值越高的客戶,即對店鋪的回購幾次最有可能產生迴應。目前網購便利,顧客已經有了更多的購買選擇和更低的購買成本,去除地域的限制因素,客戶非常容易流失,因此CRM操盤手想要提高回購率和留存率,需要時刻警惕R值。
如下圖,某零食網店用戶最近一次消費R值分佈圖:
1、客戶R值呈規律性的“波浪形”分佈,時間越長,波浪越小;
2、最近一年內用戶佔比50%(真的很巧);
數據分析:這個數據根據向行業內專業人員請教,已經是比較理想了的。說明每引入2個客戶,就有一位用戶在持續購買。說明店鋪復購做的比較好。
1.2 F值:消費頻率(Frequency)
消費頻率是客戶在固定時間內的購買次數(一般是1年)。但是如果實操中實際店鋪由於受品類寬度的原因,比如賣3C產品,耐用品等即使是忠實粉絲用戶也很難在1年內購買多次。所以,有些店鋪在運營RFM模型時,會把F值的時間範圍去掉,替換成累計購買次數。
如下圖,某零食網店用戶購買頻次圖(如1個客戶在1天內購買多筆訂單,則自動合併爲1筆訂單):
1、購買1次(新客戶)佔比爲65.5%,產生重複購買(老客戶)的佔比爲34.4%;
2、購買3次及以上(成熟客戶)的佔比爲17%,購買5次及以上(忠實客戶)的佔比爲6%;
數據分析:影響復購的核心因素是商品,因此復購不適合做跨類目比較。比如食品類目和美妝類目:食品是屬於“半標品”,產品的標品化程度越高,客戶背叛的難度就越小,越難形成忠實用戶;但是相對美妝,食品又屬於易耗品,消耗週期短,購買頻率高,相對容易產生重複購買,因此跨類目復購併不具有可比性。
1.3 M值:消費金額(Monetary)
M值是RFM模型中相對於R值和F值最難使用,但最具有價值的指標。大家熟知的“二八定律”(又名“帕雷託法則”)曾作出過這樣的解釋:公司80%的收入來自於20%的用戶。
這個數據我在自己所從事的公司總都得到過驗證!可能有些店鋪不會那麼精確,一般也會在30%客戶貢獻70%收入,或者40%貢獻60%收入。
理論上M值和F值是一樣的,都帶有時間範圍,指的是一段時間(通常是1年)內的消費金額,在工作中我認爲對於一般店鋪的類目而言,產品的價格帶都是比較單一的,比如:同一品牌美妝類,價格浮動範圍基本在某個特定消費羣的可接受範圍內,加上單一品類購買頻次不高,所以對於一般店鋪而言,M值對客戶細分的作用相對較弱。
所以我認爲用店鋪的累計購買金額和平均客單價替代傳統的M值能更好的體現客戶消費金額的差異。
教大家一個特別簡單的累積金額劃分方法:將1/2的客單價作爲累積消費金額的分段,比如客單價是300元,則按照150元進行累計消費金額分段,得出十個分段。
現以國內某知名化妝品店鋪舉例,店鋪平均客單爲160元,因此以80元作爲間隔將累積消費金額分段,從表中可以很明顯發現,累計消費160元以下用戶佔比爲65.5%(近2/3),貢獻的店鋪收入比例只佔31.6%(近1/3),具體如下:
2、 基於RFM模型的實踐應用
主要有兩種方法來分析RFM模型的結果:用基於RFM模型的劃分標準來進行客戶細分,用基於RFM模型的客戶評分來進行客戶細分。
2.1 基於RFM模型進行客戶細分
CRM實操時可以選擇RFM模型中的1-3個指標進行客戶細分,如下表所示。切記細分指標需要在自己可操控的合理範圍內,並非越多越好,一旦用戶細分羣組過多,一來會給自己的營銷方案執行帶來較大的難度,而來可能會遺漏用戶羣或者對同個用戶造成多次打擾。
最終選擇多少個指標有兩個參考標準:店鋪的客戶基數,店鋪的商品和客戶結構。
店鋪的客戶基數:在店鋪客戶一定的情況下選擇的維度越多,細分出來每一組的用戶越少。對於店鋪基數不大(5萬以下客戶數)的店鋪而言,選擇1-2個維度進行細分即可。對於客戶超過50萬的大賣家而言可以選擇2-3個指標。
店鋪的商品和客戶結構:如果在店鋪的商品層次比較單一,客單價差異幅度不大的情況下,購買頻次(F值)和消費金額(M值)高度相關的情況下,可以只選擇比較容易操作的購買頻次(F值)代替消費金額(M值)。對於剛剛開店還沒形成客戶粘性的店鋪,則可以放棄購買頻次(F值),直接用最後一次消費(R值)或者消費金額(M值)。
2.2 通過RFM模型評分後輸出目標用戶
除了直接用RFM模型對用戶進行分組之外,還有一種常見的方法是利用RFM模型的三個屬性對客戶進行打分,通過打分確定每個用戶的質量,最終篩選出自己的目標用戶。
RFM模型評分主要有三個部分:
1、確定RFM三個指標的分段和每個分段的分值;
2、計算每個客戶RFM三個指標的得分;
3、計算每個客戶的總得分,並且根據總得分篩選出優質的客戶
比如,實操的過程中一般每個指標分爲3-5段,其中R值可以根據開店以來的時間和產品的回購週期來判定,F值根據現有店鋪的平均購買頻次,M值可參考上文客單價的分段指標。
舉個例子:
確認RFM的分段和對應分段的分值之後,就可以按照用戶情況對應進行打分。
這個時候可能有人會對此產生質疑,我如何驗證這個給予的分值就是合理的呢?一般使用經驗值或用算法模型進行驗證。
這裏提供一個段子,可謂是很形象了。
二、KMeans聚類算法
1、算法原理
在正式開始之前,我們可以先通過幾個網址來感受一下KMeans的魅力。
首先是 http://shabal.in/visuals/kmeans/3.html,我們可以通過刷新頁面多次,來觀察不同的KMeans聚類過程。下圖是我把四次不同的結果合併在一起的一個結果。
通過觀察,我們可以得到初步結論:
- 中心點數量4, 起始位置不相同。
- 中心點可以移動
- 中心點最後不移動
第二個網址是 https://www.naftaliharris.com/blog/visualizing-k-means-clustering/
這個網站更牛的一點是,可以自定義可視化K均值聚類。也就是,我們可以自定義K的數量,和初始位置,來查看不同的結果。
初始情況下,大家應該看到的是下面這種情況
接下來,我分別在圖中的四個位置設置質心的位置
然後不斷的點擊Go
最後可能會看到類似如下的結果:
再讓我們來看下其他的解釋
最後將不同種類的數據分到了不同的區域。有點類似班級位置的一個分佈😂
爲了節約碼字的時間,下面藉助一些PPT來爲大家說明
我們可以得出,KMeans的計算步驟
1、選擇 K 個點作爲初始聚類中心
2、計算其他的點到中心點的距離, 進行聚類, 使用歐式距離
3、重新計算每個聚類的中心點, 再次聚類
4、直到中心點不再變化, 或者達到迭代次數
2、快速體驗
接下來讓我們來感受一下KMeans的魅力。
2.1 數據集
IRIS數據集由Fisher在1936年整理的一個經典數據集,在統計學習和機器學習領域都經常被用作示例。
數據集內包含 3 類共 150 條記錄,每類各 50 個數據,每條數據包含4個特徵,都是浮點數,單位爲釐米。
Sepal.Length(花萼長度)
Sepal.Width(花萼寬度)
Petal.Length(花瓣長度)
Petal.Width(花瓣寬度))
目標值爲鳶尾花的分類:
Iris Setosa(山鳶尾)
Iris Versicolour(雜色鳶尾)
Iris Virginica(維吉尼亞鳶尾)
其中的數據分佈如下:
2.2 代碼演示
import org.apache.spark.ml.clustering.{KMeans, KMeansModel}
import org.apache.spark.ml.feature.{MinMaxScaler, MinMaxScalerModel}
import org.apache.spark.sql.{DataFrame, SparkSession}
/*
用於實現 使用 kmeans 爲鳶尾花數據分類
*/
object Iris {
def main(args: Array[String]): Unit = {
//1、創建sparlsession對象
val spark: SparkSession = SparkSession.builder().appName("Iris").master("local[*]").getOrCreate()
spark.sparkContext.setLogLevel("WARN")
//2、讀取libsvm 數據
val irisLibSvmDF: DataFrame = spark.read.format("libsvm")
.load("file:///E:\\數據集\\iris_kmeans.txt")
irisLibSvmDF.show(false)
/*
+-----+-------------------------------+
|label|features |
+-----+-------------------------------+
|1.0 |(4,[0,1,2,3],[5.1,3.5,1.4,0.2])|
|1.0 |(4,[0,1,2,3],[4.9,3.0,1.4,0.2])|
|1.0 |(4,[0,1,2,3],[4.7,3.2,1.3,0.2])|
|1.0 |(4,[0,1,2,3],[4.6,3.1,1.5,0.2])|
|1.0 |(4,[0,1,2,3],[5.0,3.6,1.4,0.2])|
*/
//3、數據歸一化(將數據歸一到0-1之間,計算速度快)
//數據歸一化
//把數據映射到0~1範圍之內處理,更加便捷快速
//MinMaxScaler 把有量綱表達式變成無量綱表達式,便於不同單位或量級的指標能夠進行比較和加權。
//x' = (x - X_min) / (X_max - X_min)
val scalerDatas: MinMaxScalerModel = new MinMaxScaler()
.setInputCol("features") //設置需要歸一化的列
.setOutputCol("featuresOut") //歸一化後的數據的列名字
.fit(irisLibSvmDF) //設置數據
val scalerDF: DataFrame = scalerDatas.transform(irisLibSvmDF)
scalerDF.show(false)
/*
+-----+-------------------------------+---------------------------------------------------------------------------------+
|label|features |featuresOut |
+-----+-------------------------------+---------------------------------------------------------------------------------+
|1.0 |(4,[0,1,2,3],[5.1,3.5,1.4,0.2])|[0.22222222222222213,0.6249999999999999,0.06779661016949151,0.04166666666666667] |
|1.0 |(4,[0,1,2,3],[4.9,3.0,1.4,0.2])|[0.1666666666666668,0.41666666666666663,0.06779661016949151,0.04166666666666667] |
|1.0 |(4,[0,1,2,3],[4.7,3.2,1.3,0.2])|[0.11111111111111119,0.5,0.05084745762711865,0.04166666666666667] |
|1.0 |(4,[0,1,2,3],[4.6,3.1,1.5,0.2])|[0.08333333333333327,0.4583333333333333,0.0847457627118644,0.04166666666666667] |
|1.0 |(4,[0,1,2,3],[5.0,3.6,1.4,0.2])|[0.19444444444444448,0.6666666666666666,0.06779661016949151,0.04166666666666667] |
*/
//4、使用kmeans進行計算
val prediction: KMeansModel = new KMeans()
.setK(3) //設置需要劃分類別的數量/個數
.setMaxIter(10) //設置最大計算次數
.setFeaturesCol("featuresOut") //設置特徵的列 歸一化後的列
.setPredictionCol("predictionValue") //設置最終預測後的結果列名
.setSeed(10) //設置隨機種子
.fit(scalerDF)
val predictionDF: DataFrame = prediction.transform(scalerDF)
predictionDF.show(false)
/*
+-----+-------------------------------+---------------------------------------------------------------------------------+---------------+
|label|features |featuresOut |predictionValue|
+-----+-------------------------------+---------------------------------------------------------------------------------+---------------+
|1.0 |(4,[0,1,2,3],[5.1,3.5,1.4,0.2])|[0.22222222222222213,0.6249999999999999,0.06779661016949151,0.04166666666666667] |0 |
|1.0 |(4,[0,1,2,3],[4.9,3.0,1.4,0.2])|[0.1666666666666668,0.41666666666666663,0.06779661016949151,0.04166666666666667] |0 |
|1.0 |(4,[0,1,2,3],[4.7,3.2,1.3,0.2])|[0.11111111111111119,0.5,0.05084745762711865,0.04166666666666667] |0 |
|1.0 |(4,[0,1,2,3],[4.6,3.1,1.5,0.2])|[0.08333333333333327,0.4583333333333333,0.0847457627118644,0.04166666666666667] |0 |
|1.0 |(4,[0,1,2,3],[5.0,3.6,1.4,0.2])|[0.19444444444444448,0.6666666666666666,0.06779661016949151,0.04166666666666667] |0 |
|1.0 |(4,[0,1,2,3],[5.4,3.9,1.7,0.4])|[0.30555555555555564,0.7916666666666665,0.11864406779661016,0.12500000000000003] |0 |
*/
//簡單驗證
predictionDF.groupBy("label","predictionValue").count().show()
/*
+-----+---------------+-----+
|label|predictionValue|count|
+-----+---------------+-----+
| 2.0| 1| 47|
| 1.0| 0| 50|
| 2.0| 2| 3|
| 3.0| 1| 14|
| 3.0| 2| 36|
+-----+---------------+-----+
*/
}
}
結語
本篇博客,主要爲大家簡單介紹了RFM模型和KMeans聚類算法,後續會將其與用戶畫像的項目結合起來,爲大家講解挖掘型標籤開發的過程,敬請期待😎
如果以上過程中出現了任何的紕漏錯誤,煩請大佬們指正😅
受益的朋友或對大數據技術感興趣的夥伴記得點贊關注支持一波🙏
希望我們都能在學習的道路上越走越遠😉