一 场景分析

用户行为分析应用的场景很多,像线上网站访问统计,线下客流分析(比如图像人脸识别、wifi探针等),比较核心的指标有几个:

PV | UV | SD | SC

指标说明:

PV(Page View):网站浏览量或者商场门店的访问量
UV(Unique Visitor):独立访客数,即去重后的人数
SD(Session Duration):单次会话停留时间
SC(Session Count):会话次数

用户行为分析的原始数据通常是一系列时间离散数据,比如网站访问记录:用户在一个时间点访问了一个网页,然后又在下个时间点访问了下个网页;

这些原始数据可以抽象为:

User | Timestamp | Target

即用户在什么时间点访问了什么目标;

统计PV、UV比较简单,但是在时间离散数据的基础上,要计算SD、SC这两个指标,常用的方式是设置过期时间阈值,如果用户两次访问的时间间隔超过阈值,则认为是两次Session;然后在一次Session的所有数据中取时间最早和最晚的数据来统计本次Session Duration;

二 统计示例

输入数据

(user1, 2018-12-01 01:00:00, t1)
(user1, 2018-12-01 01:01:30, t1)
(user1, 2018-12-01 01:06:00, t1)
(user1, 2018-12-01 01:20:00, t1)
(user1, 2018-12-01 01:24:00, t1)

可以统计出

PV=5,UV=1

过期时间阈值设置为5分钟,以上数据应该统计出来2次Session,分别是:

Session1: (2018-12-01 01:00:00 到 2018-12-01 01:06:00),Duration:6分钟
Session2: (2018-12-01 01:20:00 到 2018-12-01 01:24:00),Duration:4分钟

实际处理时还要数据乱序的问题,尤其是在实时计算中,你想好怎样做了吗?

容易想到的方式是先做group然后将所有的timestamp排序后一次遍历统计出SD和SC,不过这种方式占用内存比较大,性能略差,而且只能用在离线计算中。

三 代码实现

下面给出scala实现,来见证scala的强大:

scala核心代码(一步foldLeft)

scala

  val expireInSecond = 300
def mergeTimeArray(arr1 : ArrayBuffer[(Long, Long)], arr2 : ArrayBuffer[(Long, Long)]) : ArrayBuffer[(Long, Long)] = {
if (arr1.head._1.equals(0l)) arr2
else if (arr2.head._1.equals(0l)) arr1
else (arr1 ++ arr2).sortBy(_._1).foldLeft(ArrayBuffer[(Long, Long)]())((result, item) => if (!result.isEmpty && result.last._2 + expireInSecond >= item._1) {result.update(result.length - 1, (result.last._1, math.max(result.last._2, item._2))); result} else result += item)
}

spark核心代码(2步map 1步aggregateByKey)

scala

  /**
* @param data (user, timestamp, target)
* @return (user, target, session_count, session_duration)
*/
def process(data : RDD[(String, Long, String)]) : RDD[(String, String, Integer, Double)] = {
//((user, target), timestamp)
data.map(item => ((item._1, item._3), item._2))
//((user, target), Array[(startTime, endTime)])
.aggregateByKey(ArrayBuffer((0l, 0l)))((result : ArrayBuffer[(Long, Long)], timestamp: Long) => mergeTimeArray(result, ArrayBuffer((timestamp, timestamp))), (result1 : ArrayBuffer[(Long, Long)], result2 : ArrayBuffer[(Long, Long)]) => mergeTimeArray(result1, result2))
//(user, target, session_count, session_duration)
.map(item => (item._1._1, item._1._2, item._2.length, item._2.foldLeft(0l)((result, item) => result + (item._2 - item._1)).toDouble / item._2.length))
}

测试运行

  def main(args : Array[String]) : Unit = {
val conf = new SparkConf().setAppName("UserAnalysis").setMaster("local[2]")
val sc = new SparkContext(conf)
val arr = Array(("user1", 1546054000l, "t1"), ("user1", 1546054090l, "t1"), ("user1", 1546054360l, "t1"), ("user1", 1546055200l, "t1"), ("user1", 1546055440l, "t1"))
//(user, timestamp, target)
val data : RDD[(String, Long, String)] = sc.parallelize(arr)
this.process(data).foreach(println)
}

输出

(user1,t1,2,300.0)

【原创】大叔案例分享(3)用户行为分析--见证scala的强大的更多相关文章

  1. 【原创】大叔案例分享(4)定位分析--见证scala的强大

    一 场景分析 定位分析广泛应用,比如室外基站定位,室内蓝牙beacon定位,室内wifi探针定位等,实现方式是三点定位 Trilateration 理想情况 这种理想情况要求3个基站‘同时’采集‘准确 ...

  2. 【原创】大叔案例分享(5)id打通

    经常有一些需要做id打通的场景,比如用户id打通等, 问题抽象是每条数据都可以解析出一个或多个kv pair:(id_type,id),然后需要将某一个kv pair匹配的多条数据进行merge: 比 ...

  3. 老李案例分享:MAT分析应用程序服务出现内存溢出过程

    老李案例分享:MAT分析应用程序服务出现内存溢出过程   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loa ...

  4. Office 2010 KMS激活原理和案例分享

    Office 2010 KMS激活原理和案例分享     为了减低部署盗版(可能包含恶意软件.病毒和其他安全风险)的可能性,Office 2010面向企业客户推出了新的批量激活方式:KMS和MAK.这 ...

  5. Office 2010 KMS激活原理和案例分享 - Your Office Solution Here - Site Home - TechNet Blogs

    [作者:葛伟华.张玉工程师 ,  Office/Project支持团队, 微软亚太区全球技术支持中心 ] 为了减低部署盗版(可能包含恶意软件.病毒和其他安全风险)的可能性,Office 2010面向企 ...

  6. 性能调优案例分享:jvm crash的原因 1

    性能调优案例分享:jvm crash的原因   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq: ...

  7. mysql的"双1设置"-数据安全的关键参数(案例分享)

    mysql的"双1验证"指的是innodb_flush_log_at_trx_commit和sync_binlog两个参数设置,这两个是是控制MySQL 磁盘写入策略以及数据安全性 ...

  8. MySQL数据库详解之"双1设置"的数据安全的关键参数案例分享

    mysql的"双1验证"指的是innodb_flush_log_at_trx_commit和sync_binlog两个参数设置,这两个是是控制MySQL 磁盘写入策略以及数据安全性 ...

  9. 【案例分享】使用ActiveReports报表工具,在.NET MVC模式下动态创建报表

    提起报表,大家会觉得即熟悉又陌生,好像常常在工作中使用,又似乎无法准确描述报表.今天我们来一起了解一下什么是报表,报表的结构.构成元素,以及为什么需要报表. 什么是报表 简单的说:报表就是通过表格.图 ...

随机推荐

  1. Django之 Form和ModelForm组件

    01-Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用 ...

  2. BugPhobia开发篇章:Beta阶段第X次Scrum Meeting

    0x01 :Scrum Meeting基本摘要 Beta阶段第十次Scrum Meeting 敏捷开发起始时间 2015/12/29 00:00 A.M. 敏捷开发终止时间 2016/01/01 23 ...

  3. git修改已push的commit信息

    本条适用于修改已push的最新的commit信息,确保本地的文件是最新的. 使用 git commit --amend 命令,(修改最近一次提交的注释信息),会进入到vim 编辑器 编辑提交信息,保存 ...

  4. Java Mail 实现第三方邮件发送功能

    1 创建一个用于发送邮件的类 package com.latiny.service; import java.io.IOException; import java.io.InputStream; i ...

  5. Java基础:Java简介及安装配置(1)

    Java简介 Java是Sun公司于1995年推出的高级编程语言,具有跨平台特性,编译后的程序能够运行在多种类型的操作系统平台上. 1.1 Java应用程序版本 Java的3个独立用于开发不同类型应用 ...

  6. 使用按钮触发element 时间事件 --时间戳

    本日 本周 本月 本年  时间按钮   date 组件内添加 pickerOptions2: { shortcuts: [ { text: '今日', onClick(picker) { picker ...

  7. Python学习之路——函数的参数分类

    今日内容 '''实参:调用函数,在括号内传入的实际值,值可以为常量.变量.表达式或三者的组合​*****形参:定义函数,在括号内声明的变量名,用来接受外界传来的值​'''​'''注:形参随着函数的调用 ...

  8. 前置通知也能对参数进行加工 通过joiPoint这个方法

  9. HTML5特性之AJAX跨域

    js跨域问题一般会考虑iframe.jsonp. 后端语言设置响应头:header('Access-Control-Allow-Origin:*),*号表示允许所有域名,可以将*号改为指定允许的访问域 ...

  10. Codeforces1100F Ivan and Burgers 【整体二分】【线性基】

    题目分析: 一道近似的题目曾经出现在SCOI中,那题可以利用RMQ或者线段树做,这题如果用那种做法时间复杂度会是$log$三次方的. 采用一种类似于整体二分的方法可以解决这道题. 将序列的线段树模型建 ...