内容简介:版权声明:本套技术专栏是作者(秦凯新)平时工作的总结和升华,通过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,如有任何技术交流,可随时联系。}}
版权声明:本套技术专栏是作者(秦凯新)平时工作的总结和升华,通过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,如有任何技术交流,可随时联系。
1 页面转化率概念
-
页面转化率的求解思路是通过UserAction表获取一个session的所有UserAction,根据时间顺序 排序 后获取全部PageId 然后将PageId组合成PageFlow,即1,2,3,4,5的形式(按照时间顺序排列),之后,组合为1_2, 2_3, 3_4, ...的形式 然后筛选出出现在targetFlow中的所有A_B
-
每个A_B进行数量统计,然后统计startPage的PV,之后根据targetFlow的A_B顺序,计算每一层的转化率
2 页面转化率业务分析
2.1 创建Spark客户端
// 任务的执行ID,用户唯一标示运行后的结果,用在 MySQL 数据库中 val taskUUID = UUID.randomUUID().toString // 构建Spark上下文 val sparkConf = new SparkConf().setAppName("SessionAnalyzer").setMaster("local[*]") // 创建Spark客户端 val spark = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate() val sc = spark.sparkContext 复制代码
2.2 查询指定日期范围内的用户访问行为数据
查询指定日期范围内的用户访问行为数据 val actionRDD = this.getActionRDDByDateRange(spark, taskParam) def getActionRDDByDateRange(spark:SparkSession, taskParam:JSONObject): RDD[UserVisitAction] = { val startDate = ParamUtils.getParam(taskParam, Constants.PARAM_START_DATE) val endDate = ParamUtils.getParam(taskParam, Constants.PARAM_END_DATE) import spark.implicits._ spark.sql("select * from user_visit_action where date>='" + startDate + "' and date<='" + endDate + "'") .as[UserVisitAction].rdd } 复制代码
2.3 具体业务分析
将用户行为信息转换为 K-V 结构 val sessionid2actionRDD = actionRDD.map(item => (item.session_id, item)) 将数据进行内存缓存 sessionid2actionRDD.persist(StorageLevel.MEMORY_ONLY) 对<sessionid,访问行为> RDD,做一次groupByKey操作,生成页面切片 val sessionid2actionsRDD = sessionid2actionRDD.groupByKey() 复制代码
2.4 最核心的一步,每个session的单跳页面切片的生成,以及页面流的匹配,算法
val pageSplitRDD = generateAndMatchPageSplit(sc, sessionid2actionsRDD, taskParam) def generateAndMatchPageSplit(sc:SparkContext, sessionid2actionsRDD:RDD[(String, Iterable[UserVisitAction])], taskParam:JSONObject ):RDD[(String, Int)] = { /* 对目标PageFlow进行解析 */ //1,2,3,4,5,6,7 val targetPageFlow = ParamUtils.getParam(taskParam, Constants.PARAM_TARGET_PAGE_FLOW) //将字符串转换成为了List[String] val targetPages = targetPageFlow.split(",").toList //targetPages.slice(0, targetPages.length-1) :[1,2,3,4,5,6] //targetPages.tail :[2,3,4,5,6,7] //targetPages.slice(0, targetPages.length-1).zip(targetPages.tail):(1,2)(2,3)(3,4)(4,5)(5,6)(6,7) //map(item => item._1 + "_" + item._2):(1_2,2_3,3_4,4_5,5_6,6_7) val targetPagePairs = targetPages.slice(0, targetPages.length-1).zip(targetPages.tail).map(item => item._1 + "_" + item._2) //将结果转换为广播变量 //targetPagePairs类型为List[String] val targetPageFlowBroadcast = sc.broadcast(targetPagePairs) /* 对所有PageFlow进行解析 */ // 对全部数据进行处理 sessionid2actionsRDD.flatMap{ case (sessionid, userVisitActions) => // 获取使用者指定的页面流 // 使用者指定的页面流,1,2,3,4,5,6,7 // 1->2的转化率是多少?2->3的转化率是多少? // 这里,我们拿到的session的访问行为,默认情况下是乱序的 // 比如说,正常情况下,我们希望拿到的数据,是按照时间顺序排序的 // 但是问题是,默认是不排序的 // 所以,我们第一件事情,对session的访问行为数据按照时间进行排序 // 举例,反例 // 比如,3->5->4->10->7 // 3->4->5->7->10 // userVisitActions是Iterable[UserAction],toList.sortWith将Iterable中的所有UserAction按照时间进行排序 // 按照时间排序 val sortedUVAs = userVisitActions.toList.sortWith((uva1, uva2) => DateUtils.parseTime(uva1.action_time).getTime() < DateUtils.parseTime(uva2.action_time).getTime()) // 提取所有UserAction中的PageId信息 val soredPages = sortedUVAs.map(item => if(item.page_id != null) item.page_id) //【注意】页面的PageFlow是将session的所有UserAction按照时间顺序排序后提取PageId,再将PageId进行连接得到的 // 按照已经排好的顺序对PageId信息进行整合,生成所有页面切片:(1_2,2_3,3_4,4_5,5_6,6_7) val sessionPagePairs = soredPages.slice(0, soredPages.length-1).zip(soredPages.tail).map(item => item._1 + "_" + item._2) /* 由此,得到了当前session的PageFlow */ // 只要是当前session的PageFlow有一个切片与targetPageFlow中任一切片重合,那么就保留下来 // 目标:(1_2,2_3,3_4,4_5,5_6,6_7) 当前:(1_2,2_5,5_6,6_7,7_8) // 最后保留:(1_2,5_6,6_7) // 输出:(1_2, 1) (5_6, 1) (6_7, 1) sessionPagePairs.filter(targetPageFlowBroadcast.value.contains(_)).map((_,1)) } 复制代码
}
2.4 计算跳转率
// 返回:(1_2, 1),(3_4, 1), ..., (100_101, 1) // 统计每个跳转切片的总个数 // pageSplitPvMap:(1_2, 102320), (3_4, 90021), ..., (100_101, 45789) val pageSplitPvMap = pageSplitRDD.countByKey 复制代码
2.5 首先计算首页PV的数量
// 使用者指定的页面流是3,2,5,8,6 // 咱们现在拿到的这个pageSplitPvMap,3->2,2->5,5->8,8->6 // 首先计算首页PV的数量 val startPagePv = getStartPagePv(taskParam, sessionid2actionsRDD) def getStartPagePv(taskParam:JSONObject, sessionid2actionsRDD:RDD[(String, Iterable[UserVisitAction])]) :Long = { // 获取配置文件中的targetPageFlow val targetPageFlow = ParamUtils.getParam(taskParam, Constants.PARAM_TARGET_PAGE_FLOW) // 获取起始页面ID val startPageId = targetPageFlow.split(",")(0).toLong // sessionid2actionsRDD是聚合后的用户行为数据 // userVisitAction中记录的是在一个页面中的用户行为数据 val startPageRDD = sessionid2actionsRDD.flatMap{ case (sessionid, userVisitActions) => // 过滤出所有PageId为startPageId的用户行为数据 userVisitActions.filter(_.page_id == startPageId).map(_.page_id) } // 对PageId等于startPageId的用户行为数据进行技术 startPageRDD.count() 复制代码
}
2.6 计算目标页面流的各个页面切片的转化率(比如:2_3/1_2)
-
版权声明:本套技术专栏是作者(秦凯新)平时工作的总结和升华,通过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,如有任何技术交流,可随时联系。
计算目标页面流的各个页面切片的转化率 val convertRateMap = computePageSplitConvertRate(taskParam, pageSplitPvMap, startPagePv) def computePageSplitConvertRate(taskParam:JSONObject, pageSplitPvMap:collection.Map[String, Long], startPagePv:Long):collection.Map[String, Double] = { val convertRateMap = new mutable.HashMap[String, Double]() //1,2,3,4,5,6,7 val targetPageFlow = ParamUtils.getParam(taskParam, Constants.PARAM_TARGET_PAGE_FLOW) val targetPages = targetPageFlow.split(",").toList //(1_2,2_3,3_4,4_5,5_6,6_7) val targetPagePairs = targetPages.slice(0, targetPages.length-1).zip(targetPages.tail).map(item => item._1 + "_" + item._2) // lastPageSplitPv:存储最新一次的页面PV数量 var lastPageSplitPv = startPagePv.toDouble // 3,5,2,4,6 // 3_5 // 3_5 pv / 3 pv // 5_2 rate = 5_2 pv / 3_5 pv // 通过for循环,获取目标页面流中的各个页面切片(pv) for(targetPage <- targetPagePairs){ // 先获取pageSplitPvMap中记录的当前targetPage的数量 val targetPageSplitPv = pageSplitPvMap.get(targetPage).get.toDouble println((targetPageSplitPv, lastPageSplitPv)) // 用当前targetPage的数量除以上一次lastPageSplit的数量,得到转化率 val convertRate = NumberUtils.formatDouble(targetPageSplitPv / lastPageSplitPv, 2) // 对targetPage和转化率进行存储 convertRateMap.put(targetPage, convertRate) // 将本次的targetPage作为下一次的lastPageSplitPv lastPageSplitPv = targetPageSplitPv } convertRateMap } 复制代码
2.7 持久化页面转化率
persistConvertRate(spark, taskUUID, convertRateMap) def persistConvertRate(spark:SparkSession, taskid:String, convertRateMap:collection.Map[String, Double]) { val convertRate = convertRateMap.map(item => item._1 + "=" + item._2).mkString("|") val pageSplitConvertRateRDD = spark.sparkContext.makeRDD(Array(PageSplitConvertRate(taskid,convertRate))) import spark.implicits._ pageSplitConvertRateRDD.toDF().write .format("jdbc") .option("url", ConfigurationManager.config.getString(Constants.JDBC_URL)) .option("dbtable", "page_split_convert_rate") .option("user", ConfigurationManager.config.getString(Constants.JDBC_USER)) .option("password", ConfigurationManager.config.getString(Constants.JDBC_PASSWORD)) .mode(SaveMode.Append) .save() 复制代码
}
以上所述就是小编给大家介绍的《Spark综合使用及电商案例页面转化率统计分析实战-Spark商业应用实战》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 预算有限,还要营销转化率高,我们建议企业这么做
- 转化率提高利器之(一)再定向与客户价值历程匹配
- 通过热力图、A/B测试优化落地页,提升200%转化率!
- 流量涨价、转化率低…谷歌能否以“大数据+机器学习”拯救买量市场?
- 获天使轮融资 前豆瓣副总裁研发售前服务机器人 个性化推荐提升50%转化率
- 「Flask实战」鱼书项目实战一
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Rust编程之道
张汉东 / 电子工业出版社 / 2019-1 / 128
Rust 是一门利用现代化的类型系统,有机地融合了内存管理、所有权语义和混合编程范式的编程语言。它不仅能科学地保证程序的正确性,还能保证内存安全和线程安全。同时,还有能与C/C++语言媲美的性能,以及能和动态语言媲美的开发效率。 《Rust编程之道》并非对语法内容进行简单罗列讲解,而是从四个维度深入全面且通透地介绍了Rust 语言。从设计哲学出发,探索Rust 语言的内在一致性;从源码分析入......一起来看看 《Rust编程之道》 这本书的介绍吧!