1. 程式人生 > >Spark學習筆記(19)—— 遊戲日誌分析

Spark學習筆記(19)—— 遊戲日誌分析

1 資料

0  管理員登入
1 首次登入
2 上線
3 下線
1|2016年2月1日,星期一,10:01:08|10.51.4.168|李明剋星|法師|男|1|0|0/800000000
1|2016年2月1日,星期一,10:01:12|10.117.45.20|風道|道士|男|1|0|0/800000000
4|2016年2月1日,星期一,10:01:27|10.51.4.168|李明剋星|法師|男|2|0|0/800000000
4|2016年2月1日,星期一,10:01:30|10.117.45.20|風道|道士|男|2|0|0/800000000
4|2016年2月1日,星期一,10:01:35|10.51.4.168|李明剋星|法師|男|3|0|273/800000000
1|2016年2月1日,星期一,10:01:37|10.171.198.176|主宰|武士|男|1|0|0/800000000
4|2016年2月1日,星期一,10:01:37|10.117.45.20|風道|道士|男|3|0|0/800000000
4|2016年2月1日,星期一,10:01:42|10.51.4.168|李明剋星|法師|男|4|0|538/800000000
4|2016年2月1日,星期一,10:01:45|10.117.45.20|風道|道士|男|4|0|157/800000000
4|2016年2月1日,星期一,10:01:47|10.51.4.168|李明剋星|法師|男|5|0|750/800000000
1|2016年2月1日,星期一,10:01:49|10.168.8.103|潮流哥|法師|男|1|0|0/800000000
4|2016年2月1日,星期一,10:01:54|10.51.4.168|李明剋星|法師|男|6|0|872/800000000
4|2016年2月1日,星期一,10:01:54|10.117.45.20|風道|道士|男|5|0|340/800000000
4|2016年2月1日,星期一,10:02:00|10.51.4.168|李明剋星|法師|男|7|0|987/800000000
4|2016年2月1日,星期一,10:02:01|10.117.45.20|風道|道士|男|6|0|453/800000000
4|2016年2月1日,星期一,10:02:05|10.51.4.168|李明剋星|法師|男|8|0|1243/800000000
4|2016年2月1日,星期一,10:02:06|10.117.45.20|風道|道士|男|7|0|684/800000000
4|2016年2月1日,星期一,10:02:14|10.51.4.168|李明剋星|法師|男|9|0|1375/800000000
4|2016年2月1日,星期一,10:02:16|10.117.45.20|風道|道士|男|8|0|905/800000000
1|2016年2月1日,星期一,10:02:22|10.168.8.103|潮流鍋|武士|男|1|0|0/800000000
4|2016年2月1日,星期一,10:02:23|10.117.45.20|風道|道士|男|9|0|1120/800000000
4|2016年2月1日,星期一,10:02:24|10.51.4.168|李明剋星|法師|男|10|0|1500/800000000
........

2 程式碼

TimeUtils .scala

package gamelog

import java.text.SimpleDateFormat
import java.util.Calendar

object TimeUtils {

  val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
  val calendar = Calendar.getInstance()

  def apply(time: String) = {
    calendar.setTime(simpleDateFormat.parse(time))
    calendar.getTimeInMillis
  }

  def getCertainDayTime(amount: Int): Long ={
    calendar.add(Calendar.DATE, amount)
    val time = calendar.getTimeInMillis
    calendar.add(Calendar.DATE, -amount)
    time
  }
  

}

package gamelog

/**
  * 事件型別列舉
  * 0 管理員登陸
  * 1 首次登陸
  * 2 上線
  * 3 下線
  * 4 升級
  */
object EventType {

  val REGISTER = "1"

  val LOGIN = "2"

  val LOGOUT = "3"

  val UPGRADE = "4"

}

FilterUtils.scala

package gamelog

import org.apache.commons.lang3.time.FastDateFormat

object FilterUtils {

  val dateFormat = FastDateFormat.getInstance("yyyy年MM月dd日,E,HH:mm:ss")

  def filterByTime(fields: Array[String], startTime: Long, endTime: Long): Boolean = {
    val time = fields(1)
    val logTime = dateFormat.parse(time).getTime

    logTime >= startTime && logTime < endTime
  }

  def filterByType(fields: Array[String], evenType: String): Boolean = {
    val _type = fields(0)
    evenType == _type
  }

  def filterByTypes(fields: Array[String], eventTypes: String*): Boolean = {
    val _type = fields(0)
    for(et <- eventTypes){
      if(_type==et)
        return true
    }
    false
  }

  def filterByTypeAndTime(fields: Array[String], eventType: String, beginTime: Long, endTime: Long): Boolean = {
    val _type = fields(0)
    val _time = fields(1)
    val logTime = dateFormat.parse(_time).getTime
    eventType == _type && logTime >= beginTime && logTime < endTime
  }


}

2.1 新增使用者

package gamelog

import org.apache.spark.{SparkConf, SparkContext}

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

    val queryTime = "2016-02-01 00:00:00"
    val beginTime = TimeUtils(queryTime)
    val endTime = TimeUtils.getCertainDayTime(+1)

    val conf = new SparkConf().setAppName("GameKPI").setMaster("local[*]")
    val sc = new SparkContext(conf)

    //切分後的資料
    val splitedLogs =  sc.textFile("d://Gamelog.txt").map(_.split("\\|"))

    //過濾後並快取
    val filteredLogs =  splitedLogs.filter(fields => FilterUtils.filterByTime(fields,beginTime,endTime))
      .cache()

    //日新增使用者
    val dnu = filteredLogs.filter(fields => FilterUtils.filterByType(fields,EventType.REGISTER))
      .count()
    println(dnu)

    //

    sc.stop()

  }
}

在這裡插入圖片描述

2.2 活躍使用者

  //Daily Active Users
    val dau =  filteredLogs.filter(fields => FilterUtils.filterByTypes(fields,EventType.REGISTER,EventType.LOGIN))
        .map(_(3))
        .distinct()
        .count()
    println(dau)

    sc.stop()

在這裡插入圖片描述

2.3 留存率

  //  留存率:某段時間的新增使用者數記為A,經過一段時間後,仍然使用的使用者佔新增使用者A的比例即為留存率
    //  次日留存率(Day 1 Retention Ratio) Retention Ratio
    //  日新增使用者在+1日登陸的使用者佔新增使用者的比例
    val t1 = TimeUtils.getCertainDayTime(-1)
    val lastDayRegUser = splitedLogs.filter(fields => FilterUtils.filterByTypeAndTime(fields, EventType.REGISTER, t1, beginTime))
      .map(x => (x(3), 1))
    val todayLoginUser = filteredLogs.filter(fields => FilterUtils.filterByType(fields, EventType.LOGIN))
      .map(x => (x(3), 1))
      .distinct()

    val d1r: Double = lastDayRegUser.join(todayLoginUser).count()
    println(d1r)
    val d1rr = d1r / lastDayRegUser.count()
    println(d1rr)