用户画像第四章(企业级360°用户画像_标签开发_挖掘标签_用户活跃度模型-RFE)

用户活跃度模型-RFE
RFE模型引入
==RFE模型可以说是RFM模型的变体。 RFE模型基于用户的普通行为(非转化或交易行为)产生,==它跟RFM类似都是使用三个维度做价值评估。
RFE详解
RFE 模型是根据会员最近一次访问时间R( Recency)、访问频率 F(Frequency)和页面互动度 E(Engagements)计算得出的RFE得分。 其中:
a.最近一次访问时间 R( Recency): 会员最近一次访问或到达网站的时间。
b.访问频率 F( Frequency):用户在特定时间周期内访问或到达的频率。
c.页面互动度 E( Engagements):互动度的定义可以根据不同企业或行业的交互情况而定,例如可以定义为页面 浏览时间、浏览商品数量、视频播放数量、点赞数量、转发数量等
在RFE模型中,由于不要求用户发生交易,因此可以做未发生登录、 注册等匿名用户的行为价值分析, 也可以做实名用户分析。该模型常用来做用户活跃分群或价值区分, 也可用于内容型(例如论坛、新闻、资讯等)企业的会员分析。
RFM和 RFE模型的实现思路相同, 仅仅是计算指标发生变化。 对于RFE的数据来源, 可以从企业自己监控的用户行为日志获取,也可以从第三方网站分析工具获得。
基于RFE模型的实践应用
在得到用户的RFE得分之后, 跟 RFM 类似也可以有两种应用思路:
1:基于三个维度值做用户群体划分和解读,对用户的活跃度做分析。 RFE得分为 313 的会员说明其访问频率低, 但是每次访问时的交互都非常不错, 此时重点要做用户回访频率的提升,例如通过活动邀请、 精准广告投放、会员活动推荐等提升回访频率。
2:基于RFE的汇总得分评估所有会员的活跃度价值,并可以做活跃度排名; 同时,该得分还可以作为输入维 度跟其他维度一起作为其他数据分析和挖掘模型的输入变量,为分析建模提供基础。
比如:

  • 6忠诚 (1天内访问2次及以上,每次访问页面不重复)
  • 5活跃 (2天内访问至少1次)
  • 4回流 (3天内访问至少1次)
  • 3新增 (注册并访问)
  • 2不活跃 (7天内未访问)
  • 1流失 (7天以上无访问)
    代码实现:
import cn.itcast.up.base.BaseModel
import cn.itcast.up.common.HDFSUtils
import org.apache.spark.ml.clustering.{KMeans, KMeansModel}
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.sql._

import scala.collection.immutable


/**
  * Desc 用户活跃度模型-RFE
  * Recency:最近一次访问时间,用户最后一次访问距今时间
  * Frequency:访问频率,用户一段时间内访问的页面总数,
  * Engagements:页面互动度,用户一段时间内访问的独立页面数,也可以定义为页面 浏览量、下载量、 视频播放数量等
  */
object RFEModel extends BaseModel2{
  def main(args: Array[String]): Unit = {
    execute()
  }
  /**
    * 获取标签id(即模型id,该方法应该在编写不同模型时进行实现)
    * @return
    */
  override def getTagID(): Int = 45

  /**
    * 开始计算
    * inType=HBase##zkHosts=192.168.10.20##zkPort=2181##
    * hbaseTable=tbl_logs##family=detail##selectFields=global_user_id,loc_url,log_time
    * @param fiveDF  MySQL中的5级规则 id,rule
    * @param hbaseDF 根据selectFields查询出来的HBase中的数据
    * @return userid,tagIds
    */
  override def compute(fiveDF: DataFrame, hbaseDF: DataFrame): DataFrame = {
    import spark.implicits._
    import scala.collection.JavaConversions._
    import org.apache.spark.sql.functions._
    //fiveDF.show()
    //fiveDF.printSchema()

    //hbaseDF.show(10,false)
    //hbaseDF.printSchema()

    //0.定义常量字符串,避免后续拼写错误
    val recencyStr = "recency"
    val frequencyStr = "frequency"
    val engagementsStr = "engagements"
    val featureStr = "feature"
    val scaleFeatureStr = "scaleFeature"
    val predictStr = "predict"

    //1.按用户id进行聚合获取用户活跃度-RFE
    //Recency:最近一次访问时间,用户最后一次访问距今时间,当前时间 - log_time
    //Frequency:访问频率,用户一段时间内访问的页面总数,count(loc_url)
    //Engagements:页面互动度,用户一段时间内访问的独立页面数,也可以定义为页面 浏览量、下载量、 视频播放数量等,distinct count(loc_url)

    val recencyAggColumn: Column = datediff(date_sub(current_timestamp(),60), max('log_time)) as recencyStr
    val frequencyAggColumn: Column = count('loc_url) as frequencyStr
    val engagementsAggColumn: Column = countDistinct('loc_url) as engagementsStr

    val RFEResult = hbaseDF.groupBy('global_user_id)
      .agg(recencyAggColumn, frequencyAggColumn, engagementsAggColumn)
    //RFEResult.show(10)

    // 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分
    val recencyScore: Column = when(col(recencyStr).between(0, 15), 5)
      .when(col(recencyStr).between(16, 30), 4)
      .when(col(recencyStr).between(31, 45), 3)
      .when(col(recencyStr).between(46, 60), 2)
      .when(col(recencyStr).gt(60), 1)
      .as(recencyStr)

    val frequencyScore: Column = when(col(frequencyStr).geq(400), 5)
      .when(col(frequencyStr).between(300, 399), 4)
      .when(col(frequencyStr).between(200, 299), 3)
      .when(col(frequencyStr).between(100, 199), 2)
      .when(col(frequencyStr).leq(99), 1)
      .as(frequencyStr)

    val engagementsScore: Column = when(col(engagementsStr).geq(250), 5)
      .when(col(engagementsStr).between(200, 249), 4)
      .when(col(engagementsStr).between(150, 199), 3)
      .when(col(engagementsStr).between(50, 149), 2)
      .when(col(engagementsStr).leq(49), 1)
      .as(engagementsStr)

    val RFEScoreResult =RFEResult.select('global_user_id as "userId", recencyScore, frequencyScore, engagementsScore)
      .where('userId.isNotNull and col(recencyStr).isNotNull and col(frequencyStr).isNotNull and col(engagementsStr).isNotNull)
    //RFEScoreResult.show(10)


    //3.聚类
    //为方便后续模型进行特征输入,需要部分列的数据转换为特征向量,并统一命名,VectorAssembler类就可以完成这一任务。
    //VectorAssembler是一个transformer,将多列数据转化为单列的向量列
    val vectorDF: DataFrame = new VectorAssembler()
      .setInputCols(Array(recencyStr, frequencyStr, engagementsStr))
      .setOutputCol(featureStr)
      .transform(RFEScoreResult)//.na.drop
    //vectorDF.show(10)
   

    //https://cloud.tencent.com/document/product/851/35146
    //最小最大归一化将每个特征调整到一个特定的范围,通常是(0,1)
    //(X - X.min)/(X.max - X.min)
    //归一化数据可以使各个特征维度对目标函数的影响权重一致,提高迭代的求解的收敛速度
    val kMeans: KMeans = new KMeans()
      .setK(4)
      .setSeed(10) //可重复的随机
      .setMaxIter(2)//最大迭代次数
      .setFeaturesCol(featureStr) //特征列
      .setPredictionCol(predictStr)//预测结果列

    //4.训练模型
    var model: KMeansModel = null
    val path = "/model/RFEModel/"
    if (HDFSUtils.getInstance().exists(path)){
      model = KMeansModel.load(path)
    }else{
      model = kMeans.fit(vectorDF)
      model.save(path)
    }

    //5.预测
    val result: DataFrame = model.transform(vectorDF)
    result.show(10)

    //6.测试时看下聚类效果
    val ds: Dataset[Row] = result
      .groupBy(predictStr)
      .agg(max(col(recencyStr) + col(frequencyStr) + col(engagementsStr)), min(col(recencyStr) + col(frequencyStr) + col(engagementsStr)))
      .sort(col(predictStr).asc)
    ds.show()
 

    //问题: 每一个簇的ID是无序的,但是我们将分类簇和rule进行对应的时候,需要有序
    //7.按质心排序,质心大,该类用户价值大
    //[(质心id, 质心值)]
    val center: immutable.IndexedSeq[(Int, Double)] = for(i <- model.clusterCenters.indices) yield (i, model.clusterCenters(i).toArray.sum)
    val sortedCenter: immutable.IndexedSeq[(Int, Double)] = center.sortBy(_._2).reverse
    sortedCenter.foreach(println)
   
    //[(质心id, rule值)]
    val centerIdAndRule: immutable.IndexedSeq[(Int, Int)] = for(i <- sortedCenter.indices) yield (sortedCenter(i)._1, i+1)
    centerIdAndRule.foreach(println)
   
    val predictRuleDF: DataFrame = centerIdAndRule.toDF(predictStr,"rule")
    predictRuleDF.show()
  

    //8.将rule和5级规则进行匹配
    val ruleTagDF: DataFrame = fiveDF.as[(Long,String)].map(t=>(t._2,t._1)).toDF("rule","tag")
    ruleTagDF.show()
   
    val predictRuleTagDF: DataFrame = predictRuleDF.join(ruleTagDF,"rule")
    predictRuleTagDF.show()
  
    //Map[predict, tag]
    val map: Map[Long, Long] = predictRuleTagDF.as[(String,Long,Long)].map(t=>(t._2,t._3)).collect().toMap
    val predict2tag = functions.udf((predict:Long)=>{
      val tag = map(predict)
      tag
    })

    //8.predict->tag
    val newDF: DataFrame = result.select($"userId",predict2tag($"predict").as("tagIds"))
    newDF.show(10)

    newDF
  }
}

你可能感兴趣的:(用户画像)