客户价值RFM模型
首先,RFM模型是市场营销中常用的一个模型,它帮助商家了解每个客户的价值。具体来说,RFM模型包含三个指标:
R(Recency):最近一次购买时间。这个指标反映了客户多久之前还在购买你的产品。
F(Frequency):购买频率。这个指标表示客户在过去一段时间内购买了多少次你的产品。
M(Monetary):购买金额。这个指标代表了客户每次购买时花了多少钱。
总结作用:此标签用以划分用户群体(低价值客户、高价值客户等)
KMeans聚类算法
KMeans是一种聚类算法,它的工作原理是:“人以群分,物以类聚”。
想象一下,你有一堆散落的物品,你想要将它们分成几堆。分类的方法有很多种,而KMeans算法会首先随机选择几个点作为“类簇中心点”,然后计算每个物品到这些中心点的距离,并将每个物品分配到离它最近的中心点所在的那一堆。之后,算法会重新计算每一堆的中心点,并重复这个过程,直到所有物品都稳定地分配到了各个堆中。
KMeans算法(K-均值算法):
- K:表示将数值聚类为K个类簇Cluster,划分K个类别,K值大于等于2
- Mean:平均值
使用聚类算法给用户打标签步骤:
- 特征数据提取
- 构建聚类KMeans模型
- 给用户打标签
KMeans与RFM的关系
首先,RFM模型是衡量用户价值的重要工具和方法,它由三个基础指标组成:最近一次消费(Recency)、消费频率(Frequency)和消费金额(Money)。这三个指标可以组合起来划分出不同类别的用户人群。
然后,KMeans聚类算法是一种常用的无监督学习算法,它可以将数据样本划分为不同的类别。该算法基于数据点之间的相似性度量进行聚类,通过最小化数据点与所属类别中心点之间的距离来实现聚类的目标。
现在,我们可以将客户的RFM数据看作是那堆散落的物品。每个客户都有一个R值、F值和M值,这可以看作是客户在三维空间中的一个点。
在客户价值分析中,可以使用KMeans聚类算法对RFM模型中的三个指标进行聚类,从而将用户划分为不同的价值类别。具体来说,可以将每个用户的RFM值作为数据点,使用KMeans算法将这些数据点划分为k个互不相交的类别。每个类别由一个中心点代表,该中心点是该类别内所有数据点的均值。这样,就可以根据用户所属的类别来判断其价值高低,并为其打上相应的RFM标签。
因此,KMeans聚类算法在客户价值RFM标签的生成中发挥了重要作用,使得我们可以更加准确地评估用户的价值并为其提供个性化的服务。
通俗解释两者关系
想象一下你是一家电商公司的市场经理。你手上有所有客户的购买数据,包括他们最近一次购买的时间、购买频率和每次购买的金额。你想要了解哪些客户对你的公司最有价值,以便你能更好地为他们服务。
这时,你可以使用RFM模型来描述每个客户的价值。然后,你可以使用KMeans聚类算法来将这些客户分成几类,比如“高价值客户”(经常购买且每次购买金额都很大)、“中价值客户”(购买频率中等,金额也中等)和“低价值客户”(很少购买或每次购买金额都很小)。
通过这种方式,你就可以根据客户的价值类别来为他们提供个性化的服务或优惠,从而提高客户满意度和忠诚度。
RFM模型
R、F、M
①衡量客户价值的,以便于对客户划分群体
②依据用户近期订单数据(近一个月订单数据、近三个月订单数据)计算得到:
R:消费周期标签值,最后一次订单距离今天
F:最近交易次数
M:最近交易金额
RFM作用直观图
RFM模型小结
一般情况下RFM模型可以说明以下几个事实:
1.最近购买的时间越近,用户对产品促销互动越大 -> R越小越好
2.客户购买的频率越高,客户就品牌的满意度就越大 -> F越大越好
3.购买金额(货币价值)将高消费客户和低消费客户区分开来 -> M 越大越好,以区分高低消费客户
用户划分
到目前为止,我们可以清楚的知道,RFM模型的根本作用就一句话:根据RFM的值,将用户进行权重划分。具体有哪些划分方法?
划分方法:
根据RFM模型(业务数据:订单数据,时间范围内订单数据),就可以统计在某一段时间内,用户最近的消费间隔,消费次数和消费金额,再根据使用 K-Means算法对用户进行聚类分群(不仅仅可以局限于这三个数据字段,还可以根据业务需求,加入其他字段,进行调整模型):
1、划分标准(手动划分)
选择RFM模型中的1-3个指标对客户进行细分,如下表所示。切记细分指标需要在自己可操控的合理范围内,并非越多越好,一旦用户细分群组过多,一来会给自己的营销方案执行带来较大的难度,而来可能会遗漏用户群或者对同个用户造成多次打扰。
最终选择多少个指标有两个参考标准:店铺的客户基数,店铺的商品和客户的结构。
2、客户评分(聚类算法划分)
除了直接用RFM模型对用户进行分组以外,还有一种常见的方法时利用RFM模型的三个属性对客户进行打分,通过打分确定每个用户的质量,最终筛选出自己的目标用户。
RFM模型评分主要有三个部分:
- 确定RFM三个指标的分段和每个分段的分值;
- 计算每个客户RFM三个指标的得分;
- 计算每个客户的总得分,并且根据总得分筛选出优质的客户
这里我们采用的是通过聚类算法划分用户,KMeans具体怎么实现下一章会讲,这里主要讲RFM的实现,想了解KMeans算法的请移步KMeans篇。
代码实现:
步骤分析
实施步骤
1.依据用户订单数据计算R、F、M值
按照用户memberId分组,统计R/F/M的值
2.按照规则给R、F、M打分
r_score,f_score,m_score
3.使用KMeans算法训练模型,K = 5
4.使用KMeansMode预测用户所属类簇
Prediction:标识KMeans模型中各类簇中心点下标
5.结合属性标签规则rule,给用户打标签值(难点)
具体代码
import cn.itcast.tags.models.{AbstractModel, ModelType}
import cn.itcast.tags.tools.TagTools
import org.apache.spark.ml.clustering.{KMeans, KMeansModel}
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.linalg
import org.apache.spark.sql.expressions.UserDefinedFunction
import org.apache.spark.sql.{Column, DataFrame, SparkSession}
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.{DataTypes, DecimalType}
import org.apache.spark.storage.StorageLevel
/**
* 挖掘类型标签模型开发:客户价值模型RFM
*/
class RfmModel extends AbstractModel("客户价值RFM", ModelType.ML){
/*
361 客户价值
362 高价值 0
363 中上价值 1
364 中价值 2
365 中下价值 3
366 超低价值 4
*/
override def doTag(businessDF: DataFrame, tagDF: DataFrame): DataFrame = {
val session: SparkSession = businessDF.sparkSession
import session.implicits._
/*
root
|-- memberid: string (nullable = true)
|-- ordersn: string (nullable = true)
|-- orderamount: string (nullable = true)
|-- finishtime: string (nullable = true)
*/
//businessDF.printSchema()
//businessDF.show(10, truncate = false)
/*
root
|-- id: long (nullable = false)
|-- name: string (nullable = true)
|-- rule: string (nullable = true)
|-- level: integer (nullable = true)
*/
//tagDF.printSchema()
/*
|id |name|rule|level|
+---+----+----+-----+
|362|高价值 |0 |5 |
|363|中上价值|1 |5 |
|364|中价值 |2 |5 |
|365|中下价值|3 |5 |
|366|超低价值|4 |5 |
+---+----+----+-----+
*/
//tagDF.filter($"level" === 5).show(10, truncate = false)
/*
TODO: 1、计算每个用户RFM值
按照用户memberid分组,然后进行聚合函数聚合统计
R:消费周期,finishtime
日期时间函数:current_timestamp、from_unixtimestamp、datediff
F: 消费次数 ordersn
count
M:消费金额 orderamount
sum
*/
val rfmDF: DataFrame = businessDF
// a. 按照memberid分组,对每个用户的订单数据句话操作
.groupBy($"memberid")
.agg(
max($"finishtime").as("max_finishtime"), //
count($"ordersn").as("frequency"), //
sum(
$"orderamount".cast(DataTypes.createDecimalType(10, 2))
).as("monetary") //
)
// 计算R值
.select(
$"memberid".as("userId"), //
// 计算R值:消费周期
datediff(
current_timestamp(), from_unixtime($"max_finishtime")
).as("recency"), //
$"frequency", //
$"monetary"
)
//rfmDF.printSchema()
//rfmDF.show(10, truncate = false)
/*
TODO: 2、按照规则给RFM进行打分(RFM_SCORE)
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分
使用CASE WHEN .. WHEN... ELSE .... END
*/
// R 打分条件表达式
val rWhen = when(col("recency").between(1, 3), 5.0) //
.when(col("recency").between(4, 6), 4.0) //
.when(col("recency").between(7, 9), 3.0) //
.when(col("recency").between(10, 15), 2.0) //
.when(col("recency").geq(16), 1.0) //
// F 打分条件表达式
val fWhen = when(col("frequency").between(1, 49), 1.0) //
.when(col("frequency").between(50, 99), 2.0) //
.when(col("frequency").between(100, 149), 3.0) //
.when(col("frequency").between(150, 199), 4.0) //
.when(col("frequency").geq(200), 5.0) //
// M 打分条件表达式
val mWhen = when(col("monetary").lt(10000), 1.0) //
.when(col("monetary").between(10000, 49999), 2.0) //
.when(col("monetary").between(50000, 99999), 3.0) //
.when(col("monetary").between(100000, 199999), 4.0) //
.when(col("monetary").geq(200000), 5.0) //
val rfmScoreDF: DataFrame = rfmDF.select(
$"userId", //
rWhen.as("r_score"), //
fWhen.as("f_score"), //
mWhen.as("m_score") //
)
//rfmScoreDF.printSchema()
//rfmScoreDF.show(50, truncate = false)
/*
TODO: 3、使用RFM_SCORE进行聚类,对用户进行分组
KMeans算法,其中K=5
*/
// 3.1 组合R\F\M列为特征值features
val assembler: VectorAssembler = new VectorAssembler()
.setInputCols(Array("r_score", "f_score", "m_score"))
.setOutputCol("features")
val featuresDF: DataFrame = assembler.transform(rfmScoreDF)
// 将训练数据缓存
featuresDF.persist(StorageLevel.MEMORY_AND_DISK)
// 3.2 使用KMeans算法聚类,训练模型
/*
val kMeansModel: KMeansModel = new KMeans()
.setFeaturesCol("features")
.setPredictionCol("prediction") // 由于K=5,所以预测值prediction范围:0,1,2,3,4
// K值设置,类簇个数
.setK(5)
.setMaxIter(20)
.setInitMode("k-means||")
// 训练模型
.fit(featuresDF)
// WSSSE = 0.9977375565642177
println(s"WSSSE = ${kMeansModel.computeCost(featuresDF)}")
*/
val kMeansModel: KMeansModel = trainModel(featuresDF)
// 3.3. 使用模型预测
val predictionDF: DataFrame = kMeansModel.transform(featuresDF)
/*
root
|-- userId: string (nullable = true)
|-- r_score: double (nullable = true)
|-- f_score: double (nullable = true)
|-- m_score: double (nullable = true)
|-- features: vector (nullable = true)
|-- prediction: integer (nullable = true)
*/
//predictionDF.printSchema()
//predictionDF.show(50, truncate = false)
// 3.4 获取类簇中心点
val centerIndexArray: Array[((Int, Double), Int)] = kMeansModel
.clusterCenters
// 返回值类型:: Array[(linalg.Vector, Int)]
.zipWithIndex // (vector1, 0), (vector2, 1), ....
// TODO: 对每个类簇向量进行累加和:R + F + M
.map{case(clusterVector, clusterIndex) =>
// rfm表示将R + F + M之和,越大表示客户价值越高
val rfm: Double = clusterVector.toArray.sum
clusterIndex -> rfm
}
// 按照rfm值进行降序排序
.sortBy(tuple => - tuple._2)
// 再次进行拉链操作
.zipWithIndex
//centerIndexArray.foreach(println)
// TODO: 4. 打标签
// 4.1 获取属性标签规则rule和名称tagName,放在Map集合中
val rulesMap: Map[String, String] = TagTools.convertMap(tagDF)
//rulesMap.foreach(println)
// 4.2 聚类类簇关联属性标签数据rule,对应聚类类簇与标签tagName
val indexTagMap: Map[Int, String] = centerIndexArray
.map{case((centerIndex, _), index) =>
val tagName = rulesMap(index.toString)
(centerIndex, tagName)
}
.toMap
//indexTagMap.foreach(println)
// 4.3 使用KMeansModel预测值prediction打标签
// a. 将索引标签Map集合 广播变量广播出去
val indexTagMapBroadcast = session.sparkContext.broadcast(indexTagMap)
// b. 自定义UDF函数,传递预测值prediction,返回标签名称tagName
val index_to_tag: UserDefinedFunction = udf(
(clusterIndex: Int) => indexTagMapBroadcast.value(clusterIndex)
)
// c. 打标签
val modelDF: DataFrame = predictionDF.select(
$"userId", // 用户ID
index_to_tag($"prediction").as("rfm")
)
//modelDF.printSchema()
//modelDF.show(100, truncate = false)
// 返回画像标签数据
modelDF
}
/**
* 使用KMeans算法训练模型
* @param dataframe 数据集
* @return KMeansModel模型
*/
def trainModel(dataframe: DataFrame): KMeansModel = {
// 使用KMeans聚类算法模型训练
val kMeansModel: KMeansModel = new KMeans()
.setFeaturesCol("features")
.setPredictionCol("prediction")
.setK(5) // 设置列簇个数:5
.setMaxIter(20) // 设置最大迭代次数
.fit(dataframe)
println(s"WSSSE = ${kMeansModel.computeCost(dataframe)}")
// 返回
kMeansModel
}
}
object RfmModel{
def main(args: Array[String]): Unit = {
val tagModel = new RfmModel()
tagModel.executeModel(361L)
}
}
(叠甲:大部分资料来源于黑马程序员,这里只是做一些自己的认识,分享经验,如果大家又不理解的部分请移步【黑马程序员_大数据实战之用户画像企业级项目】https://www.bilibili.com/video/BV1Mp4y1x7y7?p=201&vd_source=07930632bf702f026b5f12259522cb42,以上,大佬勿喷)