赞
踩
首先,RFM模型是市场营销中常用的一个模型,它帮助商家了解每个客户的价值。具体来说,RFM模型包含三个指标:
R(Recency):最近一次购买时间。这个指标反映了客户多久之前还在购买你的产品。
F(Frequency):购买频率。这个指标表示客户在过去一段时间内购买了多少次你的产品。
M(Monetary):购买金额。这个指标代表了客户每次购买时花了多少钱。
总结作用:此标签用以划分用户群体(低价值客户、高价值客户等)
KMeans是一种聚类算法,它的工作原理是:“人以群分,物以类聚”。
想象一下,你有一堆散落的物品,你想要将它们分成几堆。分类的方法有很多种,而KMeans算法会首先随机选择几个点作为“类簇中心点”,然后计算每个物品到这些中心点的距离,并将每个物品分配到离它最近的中心点所在的那一堆。之后,算法会重新计算每一堆的中心点,并重复这个过程,直到所有物品都稳定地分配到了各个堆中。
KMeans算法(K-均值算法):
使用聚类算法给用户打标签步骤:
首先,RFM模型是衡量用户价值的重要工具和方法,它由三个基础指标组成:最近一次消费(Recency)、消费频率(Frequency)和消费金额(Money)。这三个指标可以组合起来划分出不同类别的用户人群。
然后,KMeans聚类算法是一种常用的无监督学习算法,它可以将数据样本划分为不同的类别。该算法基于数据点之间的相似性度量进行聚类,通过最小化数据点与所属类别中心点之间的距离来实现聚类的目标。
现在,我们可以将客户的RFM数据看作是那堆散落的物品。每个客户都有一个R值、F值和M值,这可以看作是客户在三维空间中的一个点。
在客户价值分析中,可以使用KMeans聚类算法对RFM模型中的三个指标进行聚类,从而将用户划分为不同的价值类别。具体来说,可以将每个用户的RFM值作为数据点,使用KMeans算法将这些数据点划分为k个互不相交的类别。每个类别由一个中心点代表,该中心点是该类别内所有数据点的均值。这样,就可以根据用户所属的类别来判断其价值高低,并为其打上相应的RFM标签。
因此,KMeans聚类算法在客户价值RFM标签的生成中发挥了重要作用,使得我们可以更加准确地评估用户的价值并为其提供个性化的服务。
想象一下你是一家电商公司的市场经理。你手上有所有客户的购买数据,包括他们最近一次购买的时间、购买频率和每次购买的金额。你想要了解哪些客户对你的公司最有价值,以便你能更好地为他们服务。
这时,你可以使用RFM模型来描述每个客户的价值。然后,你可以使用KMeans聚类算法来将这些客户分成几类,比如“高价值客户”(经常购买且每次购买金额都很大)、“中价值客户”(购买频率中等,金额也中等)和“低价值客户”(很少购买或每次购买金额都很小)。
通过这种方式,你就可以根据客户的价值类别来为他们提供个性化的服务或优惠,从而提高客户满意度和忠诚度。
①衡量客户价值的,以便于对客户划分群体
②依据用户近期订单数据(近一个月订单数据、近三个月订单数据)计算得到:
R:消费周期标签值,最后一次订单距离今天
F:最近交易次数
M:最近交易金额
一般情况下RFM模型可以说明以下几个事实:
1.最近购买的时间越近,用户对产品促销互动越大 -> R越小越好
2.客户购买的频率越高,客户就品牌的满意度就越大 -> F越大越好
3.购买金额(货币价值)将高消费客户和低消费客户区分开来 -> M 越大越好,以区分高低消费客户
到目前为止,我们可以清楚的知道,RFM模型的根本作用就一句话:根据RFM的值,将用户进行权重划分。具体有哪些划分方法?
根据RFM模型(业务数据:订单数据,时间范围内订单数据),就可以统计在某一段时间内,用户最近的消费间隔,消费次数和消费金额,再根据使用 K-Means算法对用户进行聚类分群(不仅仅可以局限于这三个数据字段,还可以根据业务需求,加入其他字段,进行调整模型):
选择RFM模型中的1-3个指标对客户进行细分,如下表所示。切记细分指标需要在自己可操控的合理范围内,并非越多越好,一旦用户细分群组过多,一来会给自己的营销方案执行带来较大的难度,而来可能会遗漏用户群或者对同个用户造成多次打扰。
最终选择多少个指标有两个参考标准:店铺的客户基数,店铺的商品和客户的结构。
除了直接用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,以上,大佬勿喷)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。