Spark-ETL日志数据清洗分析项目(下)--个人学习解析(保姆级)

此篇内容为:2.用户留存率的分析、3.活跃用户分析

如需完成2、3的功能实现,须完成1.日志数据清洗篇,并且mysql中须有logDetail日志文件

1.日志数据清洗

2.用户留存分析

3.活跃用户分析

4.将各结果导入mysql

使用工具:IDEA,Maven工程下的Scala项目

关于1.的内容可直接点击此连接跳转:(30条消息) Spark-ETL日志数据清洗分析项目(上)--个人学习解析(保姆级)_weixin_53414609的博客-CSDN博客

 二、用户留存率的分析

1)我们首先要理解用户留存率是指什么,1日的用户留存率又该怎么计算

留存率指再次回到产品的用户数量初始用户数量的比率,在logDetail或test.log中体现为有重复的用户(即userID有重复)并且这个重复用户的actionName值有"Registered"或"Signin",即这个用户在一定时间内即有注册行为又有登陆行为

而一日的用户留存率是指:

当天新增的用户中,新增日之后的第1天还登录的用户数 / 第一天新增总用户数

接着再分析,当天新增用户次日还登陆的用户数是怎么得到的,大体思路是将event_time列取出来然后再分别以actionName为Registered和Signin字段分成两组数据,那么按照”当天新增的用户中,新增日之后的第1天还登录的用户数“的字面意思来理解,就可以得到:

字段为"Registered"的event_time列 = 字段为"Signin"的event_time列 - 1天(毫秒数为86400000)

那么筛选计算出符合以上条件的用户数,再与当天所有有注册行为的用户数作除法即可得到最终结果,代码如下(代码注释已经作了比较详细的解释)

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils
import org.apache.spark.{SparkConf, SparkContext}

import java.text.SimpleDateFormat
import java.util.Properties

/**
 * 用户留存率篇*/

object UserRetention {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("retention")
    val sc = SparkContext.getOrCreate(conf)
    val spark = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._

    //连接mysql,将清洗出的数据读取出来
    val url = "jdbc:mysql://hadoop01:3306/spark_dataclear"
    val properties = new Properties()
    properties.setProperty("user", "root")
    properties.setProperty("password", "123456")
    properties.setProperty("driver", "com.mysql.jdbc.Driver")

    val detailDF = spark.read.jdbc(url,"logDetail",properties)

    //调用java方法SimpleDateFormat
    val changeTimeFun = spark.udf.register("changeTime", (x: String) => {
      val time = new SimpleDateFormat("yyyy-MM-dd")
        .parse(x.substring(0, 10)).getTime  //截取第一列日期字段,年-月-日共10位
      time
    })

    //注入所有的注册用户信息(userID,register_time,注册行为)
    //过滤出test.log/logDeatil(此处的说明仅为了方便看筛选结果的内容,两个文件内的actionName内容是一致的)中用户行为(actionName)值为“Registered”的
    val registDF = detailDF.filter(detailDF("actionName") === ("Registered"))
      .select("userUID", "event_time", "actionName")    //取出userUID,event_time,actionname
      .withColumnRenamed("event_time", "register_time")   //将event_time更名
      .withColumnRenamed("userUID", "regUID")             //将userID更名

    val registDF2 = registDF.select($"regUID", changeTimeFun($"register_time")   //调用更改时间格式方法并更改字段名
      .as("register_date"), $"actionName").distinct()

    //填入所有用户登录信息(userUID,signin_time,登陆行为)
    val signinDF = detailDF.filter(detailDF("actionName") === ("Signin"))  //过滤出test.log/logDeatil中用户行为(actionName)值为“Signin”的
      .select("userUID","event_time","actionName")    //取出userUID,event_time,actionname
      .withColumnRenamed("event_time","signin_time")   //将event_time更名
      .withColumnRenamed("userUID","signUID")          //将userID更名
      .distinct()

    val signinDF2 = signinDF.select($"signUID",changeTimeFun($"signin_time")    //调用更改时间格式方法并更改字段名
      .as("signin_date"),$"actionName").distinct()

    //过滤出有注册和登录行为的用户,内连接表格的列格式大致为:regUID register_date "Registered" signin_date "Signin"
    val joinDF = registDF2.join(signinDF2
      ,signinDF("signUID") === registDF("regUID")
      ,joinType = "inner")

    //1.一日留存率指:当天新增的用户中,新增日之后的第1天还登录的用户数/第一天新增总用户数
    //2.所以等式为 登入时间-1天毫秒数(86400000)=注册时间
    //3.因为日期取出是年-月-日格式,所以等式使用恒等是没问题的,不涉及到时-分-秒不等的情况
    //frame中存的是符合2.的用户
    val frame = joinDF.filter(
      joinDF("register_date") === joinDF("signin_date") - 86400000)
      .groupBy($"register_date").count()
      .withColumnRenamed("count","signcount")

    val frame1 = registDF2.groupBy($"register_date").count()
      .withColumnRenamed("count","regcount")
    frame1.show()
    println("这里是显示总注册人数")

    //frame2内大致列格式为:signincount regcount
    val frame2 = frame.join(frame1,"register_date")
    frame2.show()
    println("这里是显示登录和注册人数组成的dataFrame")

    //将frame2 map成列为【register_date signcount regcount 一日用户留存率】的格式
    frame2.map(x => (
      x.getAs[Long]("register_date"),
      x.getAs[Long]("signcount"),
      x.getAs[Long]("regcount"),
      x.getAs[Long]("signcount").toDouble / x.getAs[Long]("regcount")
     )
    ).show()

  }
}

如需上传结果至mysql,可参考日志清洗篇的代码末段,都是类似的写法

不过这个留存率分析的代码开头部分已经连接过mysql,所以想要将结果上传至mysql应该只需要将frame2用变量接收,然后write进mysql即可,可参考如下写法

    val resMap = frame2.map(x => (
      x.getAs[Long]("register_date"),
      x.getAs[Long]("signcount"),
      x.getAs[Long]("regcount"),
      x.getAs[Long]("signcount").toDouble / x.getAs[Long]("regcount")
     )
    )
    resMap.show()
    resMap.write.mode(saveMode = "overwrite").jdbc(url, "userRetention", properties)

 运行留存率代码的部分结果截图:

 

三、活跃用户分析

这部分相对比较简单,大体思路是将logDetail(可以直接打开test.log查找字段对应也可)里的actionName为BuyCourse(logDetail、test.log中其实不存在这个字段)或StartLearn值的字段筛选出来,再以一定的格式(作者使用Map来规范格式)存放,最后用去重聚合函数

( agg(countDistinct()) )将指定的字段聚合即可

代码如下:

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._
import java.util.Properties

/**
 *活跃用户分析篇*/

object UserActive {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[*]").setAppName("retention")
    val sc = SparkContext.getOrCreate(conf)
    val spark = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._

    //连接mysql,将清洗出的数据读取出来
    val url = "jdbc:mysql://hadoop01:3306/spark_dataclear"
    val properties = new Properties()
    properties.setProperty("user", "root")
    properties.setProperty("password", "123456")
    properties.setProperty("driver", "com.mysql.jdbc.Driver")

    val logs = spark.read.jdbc(url, "logDetail", properties)

    //将logDeatil中的用户行为为:BuyCourse或者StartLearn的筛选出来
    val ds1 = logs.filter($"actionName" === "BuyCourse" || $"actionName" === "StartLearn")
    ds1.show(false)

    //将ds1 map成列为【userUID event_time】的格式
    val ds2 = ds1.map(x => (x.getAs[String]("userUID")
      , x.getAs[String]("event_time").substring(0, 10)))
    println("这里是显示userUID event_time")
    ds2.show(false)

    //将ds2按照活跃日期(即对应test.log/logDetail里第一列产生用户行为的日期)分组
    //并且对去重聚合ds2的第一列(即userUID)求出不重复的用户人数
    ds2.withColumnRenamed("_2", "日期")
      .groupBy($"日期").agg(countDistinct($"_1").as("活跃人数")).show()
  }
}

如果也想将此结果上传至mysql,那么操作"二、用户留存率的分析"代码末尾处有解释

部分代码结果截图

 至此,此项目功能已全部实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值