spark:计算LTV3

需求

某游戏公司的笔试题,上班的时候偷偷做的。。。昨天审题没审清,给做错了,回去重新做了发一下。

 

以下是hive表,请通过Spark Code于每日凌晨计算注册用户的LTV3,需要考虑查看单区服维度的LTV3。最终报表将以BI展示。

LTV3定义:注册角色在注册3天(含当天)的总充值金额除以当天注册用户数。

注册用户表:dwd_reg

server_idrole_idlog_timelog_date
intstrtimestampdate
区服ID角色ID注册时间分区键、注册日期

用户充值表:dwd_tran

server_idrole_idamountlog_timelog_date
intstrfloattimestampdate
区服ID角色ID充值金额注册时间分区键、注册日期

思路

  1. 先求出每天注册总数  
  2. 求出用户在注册后第三天时所有充值记录的总值  
  3. 用2关联1,在结果中将总值除以注册总数
     

实现

测试数据

dwd_reg:

1 a 2020-09-01 2020-09-01
1 b 2020-09-01 2020-09-01
1 c 2020-09-02 2020-09-02
1 d 2020-09-02 2020-09-02
1 e 2020-09-03 2020-09-03
1 f 2020-09-03 2020-09-03
1 g 2020-09-04 2020-09-04
1 h 2020-09-04 2020-09-04

dwd_tran:

1 a 10.0 2020-09-01 2020-09-01
1 b 19.0 2020-09-01 2020-09-01
1 a 648.0 2020-09-02 2020-09-02
1 c 648.0 2020-09-02 2020-09-02
1 c 648.0 2020-09-02 2020-09-02
1 d 198.0 2020-09-02 2020-09-02
1 d 648.0 2020-09-03 2020-09-03
1 e 648.0 2020-09-03 2020-09-03
1 f 1000.0 2020-09-03 2020-09-03
1 g 128.0 2020-09-04 2020-09-04
1 h 6.0 2020-09-04 2020-09-04

主要逻辑

读取数据

    val dwd_reg =
      context.textFile("C:\\Users\\zixuan.wu\\Desktop\\sparkTest\\DWD_REG.txt")
      .map(s=>s.split(" "))
      .map(strings=>DWD_REG(strings(0).toInt ,strings(1),strings(2),strings(3)))
      .toDF()

    val dwd_tran =
      context.textFile("C:\\Users\\zixuan.wu\\Desktop\\sparkTest\\DWD_TRAN.txt")
        .map(s=>s.split(" "))
        .map(strings=>DWD_TRAN(strings(0).toInt ,strings(1),strings(2).toFloat,strings(3),strings(4)))
        .toDF()

1.统计各区服每天注册的用户数

    var dwd_reg_count = dwd_reg.groupBy("log_date","server_id").count()
    dwd_reg_count.show()

2.统计用户在注册后三天内所有充值记录的总值

    //先用充值表关联注册表,并过滤掉充值记录在注册时间三天后,或注册时间在当前日期前三天内的数据;然后再求每个用户的充值总值
    import org.apache.spark.sql.functions._
    val dwd_tran_sum = dwd_tran.toDF("server_id","role_id","amount","dwd_tran_log_time","dwd_tran_log_date")
      .join(dwd_reg.toDF("server_id","role_id","dwd_reg_log_time","dwd_reg_log_date"),Seq("role_id","server_id"),"left")
      .where("datediff(dwd_tran_log_date,dwd_reg_log_date)<3 and dwd_reg_log_date<=date_sub(current_date(),3)")
      .groupBy("server_id","role_id","dwd_reg_log_date")
      .agg(sum("amount").alias("amount_sum"))
    dwd_tran_sum.show()

3.将每日注册总数和用户三日充值总额表关联

关联后删除重复的server_id 字段

  val dwd_tran_reg = dwd_tran_sum.join(dwd_reg_count,
      date_sub(dwd_reg_count.col("log_date"), 3) === dwd_tran_sum.col("dwd_reg_log_date")
        && dwd_reg_count.col("server_id") === dwd_tran_sum.col("server_id")
      , "left")
        .drop(dwd_reg_count("server_id"))

  dwd_tran_reg.show()

count和log_date为空是因为造的数据不足,最大的注册时间的用户没有关联到三天后的注册人数总额。

4.用充值总额除以注册总人数,即可算出ltv3

    dwd_tran_reg.createOrReplaceTempView("dwd_tran_reg")
    val ltv3_result = session.sql("select server_id,role_id,dwd_reg_log_date,amount_sum/coalesce(count,0) as LTV3 from dwd_tran_reg")
    ltv3_result.show()

 

完整代码

package com.zixuan.spark.test


import com.zixuan.spark.bean.{DWD_REG, DWD_TRAN}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SparkSession

object LTV3 {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("test").setMaster("local[1]")
    val context = new SparkContext(conf)
    //屏蔽spark的info warn信息
    context.setLogLevel("ERROR")
    val session = SparkSession.builder().config(conf).getOrCreate()
    //导入转换DF的隐式转换
    import session.implicits._

    val dwd_reg =
      context.textFile("C:\\Users\\zixuan.wu\\Desktop\\sparkTest\\DWD_REG.txt")
      .map(s=>s.split(" "))
      .map(strings=>DWD_REG(strings(0).toInt ,strings(1),strings(2),strings(3)))
      .toDF()
    //val dwd_reg = session.sql("select * from dwd_reg")
    
    val dwd_tran =
      context.textFile("C:\\Users\\zixuan.wu\\Desktop\\sparkTest\\DWD_TRAN.txt")
        .map(s=>s.split(" "))
        .map(strings=>DWD_TRAN(strings(0).toInt ,strings(1),strings(2).toFloat,strings(3),strings(4)))
        .toDF()

    //val dwd_tran = session.sql("select * from dwd_tran")
    
    //思路:1.先求出每天注册总数  2.求出用户在注册后第三天时所有充值记录的总值   3.用2关联1,在结果中将总值除以注册总数


    //1.先计算出各区服每天注册的用户数
    var dwd_reg_count = dwd_reg.groupBy("log_date","server_id").count()
    dwd_reg_count.show()

    //2.求出用户在注册后三天内所有充值记录的总值
    //先用充值表关联注册表,并过滤掉充值记录在注册时间三天后,或注册时间在当前日期前三天内的数据;然后再求每个用户的充值总值
    import org.apache.spark.sql.functions._
    val dwd_tran_sum = dwd_tran.toDF("server_id","role_id","amount","dwd_tran_log_time","dwd_tran_log_date")
      .join(dwd_reg.toDF("server_id","role_id","dwd_reg_log_time","dwd_reg_log_date"),Seq("role_id","server_id"),"left")
      .where("datediff(dwd_tran_log_date,dwd_reg_log_date)<3 and dwd_reg_log_date<=date_sub(current_date(),3)")
      .groupBy("server_id","role_id","dwd_reg_log_date")
      .agg(sum("amount").alias("amount_sum"))
    dwd_tran_sum.show()

    //3.将用户注册后三天内充值总额表 join 每日注册总额表,关联条件为 dwd_reg_log_date+2等于注册总额表的日期
    val dwd_tran_reg = dwd_tran_sum.join(dwd_reg_count,
      date_sub(dwd_reg_count.col("log_date"), 3) === dwd_tran_sum.col("dwd_reg_log_date")
        && dwd_reg_count.col("server_id") === dwd_tran_sum.col("server_id")
      , "left")
        .drop(dwd_reg_count("server_id"))

    dwd_tran_reg.show()

    //4.用充值总额除以注册总人数,即可算出ltv3
    dwd_tran_reg.createOrReplaceTempView("dwd_tran_reg")
    val ltv3_result = session.sql("select server_id,role_id,dwd_reg_log_date,amount_sum/coalesce(count,0) as LTV3 from dwd_tran_reg")
    ltv3_result.show()
  }
}

样例类

package com.zixuan.spark.bean

case class DWD_REG(server_id:Int, role_id:String, log_time:String, log_date:String)
case class DWD_TRAN(server_id:Int,role_id:String,amount:Float,log_time:String,log_date:String)

拓展

如果要计算LTV3、LTV8、LTV10、LTV15、LTV30、LTV60、LTV 180,请设计思路,如何能在凌晨快速计算完。数据量百亿级别。

思考:

1.添加一个每日注册总数表。每日更新。

无论是统计ltv几,都要考虑每日注册的总数,如果每次都重新计算,会浪费大量的时间和计算资源。像这种可复用、且只插入不更新的中间表,直接创建出来,可以节省一部分时间和资源。同理,如果集群的存储资源充足的情况下,也可以在user表中添加多个充值总额的维度,用于记录注册后x天的充值金额,使用缓慢变慢变化维或拉链表的形式更新。

2.基于1的情况下,只计算180天内的数据

ltv n从描述上来看,就是注册后n天内的充值总额除以注册后第n天的注册人数,这两部分的数据是不变的。所以,一旦计算出来,就不必每次计算。

所以,在1的基础上,只需要每次计算180天内的数据即可,180天前的历史数据可不必关心,因为结果已经计算出来并永久保存了。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值