RFM 用户价值模型
1 需求
- 假设我是一个市场营销者, 在做一次活动之前, 我可能会思考如下问题
- 谁是我比较有价值的客户?
- 谁是比较有潜力成为有价值的客户?
- 谁快要流失了?
- 谁能够留下来?
- 谁会关心这次活动?
- 其实上面这些思考, 都围绕一个主题 价值
-
RFM
是一个最常见的用来评估价值的和潜在价值的工具
2 RFM 是什么RFM
- 通过最后一次消费距今时间, 单位时间内的消费频率, 平均消费金额来评估一个人对公司的价值, 可以理解为 RFM 是一个集成的值, 如下
RFM = Rencency(最后一次消费时间), Frequency(消费频率), Monetary(消费金额)
- RFM 模型可以说明如下事实:
- 最近一次购买时间越近, 用户对促销越有感
- 购买频率越高, 对我们满意度就越高
- 消费金额越大, 越有钱, 越是高消费人群
RFM模型
3 RFM的实际应用
RFM实际应用
4 高维空间模型
5 通过打分统一量纲
- 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分
val rScore: Column = when('r.>=(1).and('r.<=(3)), 5)
.when('r >= 4 and 'r <= 6, 4)
.when('r >= 7 and 'r <= 9, 3)
.when('r >= 10 and 'r <= 15, 2)
.when('r >= 16, 1)
.as("r_score")
val fScore: Column = when('f >= 200, 5)
.when(('f >= 150) && ('f <= 199), 4)
.when((col("f") >= 100) && (col("f") <= 149), 3)
.when((col("f") >= 50) && (col("f") <= 99), 2)
.when((col("f") >= 1) && (col("f") <= 49), 1)
.as("f_score")
val mScore: Column = when(col("m") >= 200000, 5)
.when(col("m").between(100000, 199999), 4)
.when(col("m").between(50000, 99999), 3)
.when(col("m").between(10000, 49999), 2)
.when(col("m") <= 9999, 1)
.as("m_score")
6 模型训练与预测
- RFMTrainModel 训练模型, 保存模型到 HDFS 中, 调度周期, 一个月执行一次
- RFMPredictModel 预测模型, 从 HDFS 中读取聚类模型, 对整个数据集进行预测, 每天一次
def process(source: DataFrame): DataFrame = {
val assembled = assembleDataFrame(source)
val regressor = new KMeans()
.setK(7)
.setSeed(10)
.setMaxIter(10)
.setFeaturesCol("features")
.setPredictionCol("predict")
regressor.fit(assembled).save(MODEL_PATH)
null
}
val assembled = RFMModel.assembleDataFrame(source)
val kmeans = KMeansModel.load(RFMModel.MODEL_PATH)
val predicted = kmeans.transform(assembled)
// 找到 kmeans 生成的组号和 rule 之间的关系
val sortedCenters: IndexedSeq[(Int, Double)] = kmeans.clusterCenters.indices // IndexedSeq
.map(i => (i, kmeans.clusterCenters(i).toArray.sum))
.sortBy(c => c._2).reverse
val sortedDF = sortedCenters.toDF("index", "totalScore")
RFE 活跃度
- 类似 RFM, 我们使用 RFE 计算用户的活跃度
- RFE = R (最近一次访问时间) + F (特定时间内访问频率) + E (活动数量)
- R = datediff(date_sub(current_timestamp(),60), max('log_time))
- F = count('loc_url)
- E = countDistinct('loc_url)
- R:0-15天=5分,16-30天=4分,31-45天=3分,46-60天=2分,大于61天=1分
- F:≥400=5分,300-399=4分,200-299=3分,100-199=2分,≤99=1分
- E:≥250=5分,230-249=4分,210-229=3分,200-209=2分,1=1分
PSM 价格敏感度模型
- PSM 用于统计用户的价格敏感度
- 对于不同级别价格敏感的用户可以实行不同程度的营销
1 PSM计算公式
- PSM Score = 优惠订单占比 + (平均优惠金额 / 平均每单应收) + 优惠金额占比
- 优惠订单占比
- 优惠订单 / 总单数
- 优惠订单 = 优惠的订单数量 / 总单数
- 未优惠订单 = 未优惠的订单数量 / 总单数
- 平均优惠金额
- 总优惠金额 / 优惠单数
- 平均每单应收
- 总应收 / 总单数
- 优惠金额占比
- 总优惠金额 / 总应收金额
// 应收金额
val receivableAmount = ('couponCodeValue + 'orderAmount).cast(DoubleType) as "receivableAmount"
// 优惠金额
val discountAmount = 'couponCodeValue.cast(DoubleType) as "discountAmount"
// 实收金额
val practicalAmount = 'orderAmount.cast(DoubleType) as "practicalAmount"
// 是否优惠
val state = when(discountAmount =!= 0.0d, 1) // =!=是column的方法
.when(discountAmount === 0.0d, 0)
.as("state")
// 优惠订单数
val discountCount = sum('state) as "discountCount"
// 订单总数
val totalCount = count('state) as "totalCount"
// 优惠总额
val totalDiscountAmount = sum('discountAmount) as "totalDiscountAmount"
// 应收总额
val totalReceivableAmount = sum('receivableAmount) as "totalReceivableAmount"
// 平均优惠金额
val avgDiscountAmount = ('totalDiscountAmount / 'discountCount) as "avgDiscountAmount"
// 平均每单应收
val avgReceivableAmount = ('totalReceivableAmount / 'totalCount) as "avgReceivableAmount"
// 优惠订单占比
val discountPercent = ('discountCount / 'totalCount) as "discountPercent"
// 平均优惠金额占比
val avgDiscountPercent = (avgDiscountAmount / avgReceivableAmount) as "avgDiscountPercent"
// 优惠金额占比
val discountAmountPercent = ('totalDiscountAmount / 'totalReceivableAmount) as "discountAmountPercent"
// 优惠订单占比 + (平均优惠金额 / 平均每单应收) + 优惠金额占比
val psmScore = (discountPercent + (avgDiscountPercent / avgReceivableAmount) + discountAmountPercent) as "psm"
2 聚类算法原理
- 选择 K 个点作为初始中点
- 计算每个中点到相近点的距离, 将相近的点聚在一类(簇)
- 欧式距离
- 重新计算每个簇的中点
-
重复迭代上面步骤, 直至不再发生变化
聚类算法原理
3 确定K - 肘部法则
- 根据损失函数, 计算每一个 K 的情况下, 总体上的损失
-
绘制图形, 找到拐点, 就是合适的 K
肘部法则
4 模型训练与迭代计算
val kArray = Array(2, 3, 4, 5, 6, 7, 8)
val wssseMap = kArray.map(f = k => {
val kmeans = new KMeans()
.setK(k)
.setMaxIter(10)
.setPredictionCol("prediction")
.setFeaturesCol("features")
val model: KMeansModel = kmeans.fit(vectored)
import spark.implicits._
// mlLib计算损失函数
val vestors: Array[OldVector] = model.clusterCenters.map(v => OldVectors.fromML(v))
val libModel: LibKMeansModel = new LibKMeansModel(vestors)
val features = vectored.rdd.map(row => {
val ve = row.getAs[Vector]("features")
val oldVe: OldVector = OldVectors.fromML(ve)
oldVe
})
val wssse: Double = libModel.computeCost(features)
(k, wssse)
}).toMap
分类模型-预测性别
- 购物性别模型的意义有两种:
- 通过用户购物的行为, 预测用户性别
- 通过用户购物的行为, 判定用户的购物性别偏好
1 预置标签,量化属性
|memberId| color|productType|gender|colorIndex| color| productType|gender|productTypeIndex| features|featuresIndex|
+--------+------+-----------+------+----------+------------------+---------------+------+----------------+-----------+-------------+
| 4|樱花粉| 智能电视| 1| 14.0|樱花粉| 智能电视| 1| 13.0|[14.0,13.0]| [14.0,13.0]|
| 4|樱花粉| 智能电视| 1| 14.0| 蓝色| Haier/海尔冰箱| 0| 1.0| [14.0,1.0]| [14.0,1.0]|
val label = when('ogColor.equalTo("樱花粉")
.or('ogColor.equalTo("白色"))
.or('ogColor.equalTo("香槟色"))
.or('ogColor.equalTo("香槟金"))
.or('productType.equalTo("料理机"))
.or('productType.equalTo("挂烫机"))
.or('productType.equalTo("吸尘器/除螨仪")), 1)
.otherwise(0)
.alias("gender")
2 决策树算法
-
决策树是一个监督学习算法, 需要先对数据集人工打上标签, 此处简化整体流程, 通过简单的匹配, 先预置所需要的标签
决策树算法模型
3 算法工程与模型评估
val featureVectorIndexer = new VectorIndexer()
.setInputCol("features")
.setOutputCol("featuresIndex")
.setMaxCategories(3)
val decisionTreeClassifier = new DecisionTreeClassifier()
.setFeaturesCol("featuresIndex")
.setLabelCol("gender")
.setPredictionCol("predict")
.setMaxDepth(5)
.setImpurity("gini")
val pipeline = new Pipeline()
.setStages(Array(colorIndexer, productTypeIndexer, featureAssembler, featureVectorIndexer, decisionTreeClassifier))
val Array(trainData, testData) = source.randomSplit(Array(0.8, 0.2))
val model: PipelineModel = pipeline.fit(trainData)
val pTrain = model.transform(trainData)
val tTrain = model.transform(testData)
val accEvaluator = new MulticlassClassificationEvaluator()
.setPredictionCol("predict")
.setLabelCol("gender")
.setMetricName("accuracy")//精准度
网友评论