Spark日志分析项目Demo(6)--页面单跳转化率分析
来源:互联网 发布:js.10000网上营业厅 编辑:程序博客网 时间:2024/05/22 13:18
页面单跳转化率
这里用例子来解释页面单跳转化率
假设J2EE查询任务指定的页面流是3,2,5
(1)3PV ,跳转页面中前页面为3的统计个数
(2)3_5pv,跳转页面中前页面为3,后页面为5的统计个数
(3)5_2pv,跳转页面中前页面为5,后页面为的统计个数
(4)3_5 rate =3_5pv / 3 pv
(5)5_2 rate = 5_2 pv / 3_5 pv
下面结合测试数据说说流程
1.构造Spark上下文
2.生成模拟数据
3.查询任务,获取任务的参数
4.查询指定日期范围内的用户访问行为数据,groupByKey(sessionId),用户访问行为数据需要按照时间排序
5.每个session的单跳页面切片的生成,以及页面流的匹配,统计匹配的个数
使用者指定的页面流是3,2,5,8,6
需要统计匹配3->2,2->5,5->8,8->6 个数
和3开始的个数
6.计算目标页面流的各个页面切片的转化率
7.持久化页面切片转化率,保存到数据库中
相关代码:
import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.Date;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.api.java.JavaSparkContext;import org.apache.spark.api.java.function.FlatMapFunction;import org.apache.spark.api.java.function.PairFlatMapFunction;import org.apache.spark.api.java.function.PairFunction;import org.apache.spark.broadcast.Broadcast;import org.apache.spark.sql.Row;import org.apache.spark.sql.SQLContext;import scala.Tuple2;import com.alibaba.fastjson.JSONObject;import com.ibeifeng.sparkproject.constant.Constants;import com.ibeifeng.sparkproject.dao.IPageSplitConvertRateDAO;import com.ibeifeng.sparkproject.dao.ITaskDAO;import com.ibeifeng.sparkproject.dao.factory.DAOFactory;import com.ibeifeng.sparkproject.domain.PageSplitConvertRate;import com.ibeifeng.sparkproject.domain.Task;import com.ibeifeng.sparkproject.util.DateUtils;import com.ibeifeng.sparkproject.util.NumberUtils;import com.ibeifeng.sparkproject.util.ParamUtils;import com.ibeifeng.sparkproject.util.SparkUtils;/** * 页面单跳转化率模块spark作业 * @author Administrator * */public class PageOneStepConvertRateSpark { public static void main(String[] args) { // 1、构造Spark上下文 SparkConf conf = new SparkConf() .setAppName(Constants.SPARK_APP_NAME_PAGE); SparkUtils.setMaster(conf); JavaSparkContext sc = new JavaSparkContext(conf); SQLContext sqlContext = SparkUtils.getSQLContext(sc.sc()); // 2、生成模拟数据 SparkUtils.mockData(sc, sqlContext); // 3、查询任务,获取任务的参数 long taskid = ParamUtils.getTaskIdFromArgs(args, Constants.SPARK_LOCAL_TASKID_PAGE); ITaskDAO taskDAO = DAOFactory.getTaskDAO(); Task task = taskDAO.findById(taskid); if(task == null) { System.out.println(new Date() + ": cannot find this task with id [" + taskid + "]."); return; } JSONObject taskParam = JSONObject.parseObject(task.getTaskParam()); // 4、查询指定日期范围内的用户访问行为数据 JavaRDD<Row> actionRDD = SparkUtils.getActionRDDByDateRange( sqlContext, taskParam); // 对用户访问行为数据做一个映射,将其映射为<sessionid,访问行为>的格式 // 咱们的用户访问页面切片的生成,是要基于每个session的访问数据,来进行生成的 // 脱离了session,生成的页面访问切片,是么有意义的 // 举例,比如用户A,访问了页面3和页面5 // 用于B,访问了页面4和页面6 // 漏了一个前提,使用者指定的页面流筛选条件,比如页面3->页面4->页面7 // 你能不能说,是将页面3->页面4,串起来,作为一个页面切片,来进行统计呢 // 当然不行 // 所以说呢,页面切片的生成,肯定是要基于用户session粒度的 JavaPairRDD<String, Row> sessionid2actionRDD = getSessionid2actionRDD(actionRDD); sessionid2actionRDD = sessionid2actionRDD.cache(); // persist(StorageLevel.MEMORY_ONLY) // 对<sessionid,访问行为> RDD,做一次groupByKey操作 // 因为我们要拿到每个session对应的访问行为数据,才能够去生成切片 JavaPairRDD<String, Iterable<Row>> sessionid2actionsRDD = sessionid2actionRDD.groupByKey(); // 最核心的一步,每个session的单跳页面切片的生成,以及页面流的匹配,算法 JavaPairRDD<String, Integer> pageSplitRDD = generateAndMatchPageSplit( sc, sessionid2actionsRDD, taskParam); Map<String, Object> pageSplitPvMap = pageSplitRDD.countByKey(); // 使用者指定的页面流是3,2,5,8,6 // 咱们现在拿到的这个pageSplitPvMap,3->2,2->5,5->8,8->6 long startPagePv = getStartPagePv(taskParam, sessionid2actionsRDD); // 计算目标页面流的各个页面切片的转化率 Map<String, Double> convertRateMap = computePageSplitConvertRate( taskParam, pageSplitPvMap, startPagePv); // 持久化页面切片转化率 persistConvertRate(taskid, convertRateMap); } /** * 获取<sessionid,用户访问行为>格式的数据 * @param actionRDD 用户访问行为RDD * @return <sessionid,用户访问行为>格式的数据 */ private static JavaPairRDD<String, Row> getSessionid2actionRDD( JavaRDD<Row> actionRDD) { return actionRDD.mapToPair(new PairFunction<Row, String, Row>() { private static final long serialVersionUID = 1L; @Override public Tuple2<String, Row> call(Row row) throws Exception { String sessionid = row.getString(2); return new Tuple2<String, Row>(sessionid, row); } }); } /** * 页面切片生成与匹配算法 * @param sc * @param sessionid2actionsRDD * @param taskParam * @return */ private static JavaPairRDD<String, Integer> generateAndMatchPageSplit( JavaSparkContext sc, JavaPairRDD<String, Iterable<Row>> sessionid2actionsRDD, JSONObject taskParam) { String targetPageFlow = ParamUtils.getParam(taskParam, Constants.PARAM_TARGET_PAGE_FLOW); final Broadcast<String> targetPageFlowBroadcast = sc.broadcast(targetPageFlow); return sessionid2actionsRDD.flatMapToPair( new PairFlatMapFunction<Tuple2<String,Iterable<Row>>, String, Integer>() { private static final long serialVersionUID = 1L; @Override public Iterable<Tuple2<String, Integer>> call( Tuple2<String, Iterable<Row>> tuple) throws Exception { // 定义返回list List<Tuple2<String, Integer>> list = new ArrayList<Tuple2<String, Integer>>(); // 获取到当前session的访问行为的迭代器 Iterator<Row> iterator = tuple._2.iterator(); // 获取使用者指定的页面流 // 使用者指定的页面流,1,2,3,4,5,6,7 // 1->2的转化率是多少?2->3的转化率是多少? String[] targetPages = targetPageFlowBroadcast.value().split(","); // 这里,我们拿到的session的访问行为,默认情况下是乱序的 // 比如说,正常情况下,我们希望拿到的数据,是按照时间顺序排序的 // 但是问题是,默认是不排序的 // 所以,我们第一件事情,对session的访问行为数据按照时间进行排序 // 举例,反例 // 比如,3->5->4->10->7 // 3->4->5->7->10 // 排序 List<Row> rows = new ArrayList<Row>(); while(iterator.hasNext()) { rows.add(iterator.next()); } Collections.sort(rows, new Comparator<Row>() { @Override public int compare(Row o1, Row o2) { String actionTime1 = o1.getString(4); String actionTime2 = o2.getString(4); Date date1 = DateUtils.parseTime(actionTime1); Date date2 = DateUtils.parseTime(actionTime2); return (int)(date1.getTime() - date2.getTime()); } }); // 页面切片的生成,以及页面流的匹配 Long lastPageId = null; for(Row row : rows) { long pageid = row.getLong(3); if(lastPageId == null) { lastPageId = pageid; continue; } // 生成一个页面切片 // 3,5,2,1,8,9 // lastPageId=3 // 5,切片,3_5 String pageSplit = lastPageId + "_" + pageid; // 对这个切片判断一下,是否在用户指定的页面流中 for(int i = 1; i < targetPages.length; i++) { // 比如说,用户指定的页面流是3,2,5,8,1 // 遍历的时候,从索引1开始,就是从第二个页面开始 // 3_2 String targetPageSplit = targetPages[i - 1] + "_" + targetPages[i]; if(pageSplit.equals(targetPageSplit)) { list.add(new Tuple2<String, Integer>(pageSplit, 1)); break; } } lastPageId = pageid; } return list; } }); } /** * 获取页面流中初始页面的pv * @param taskParam * @param sessionid2actionsRDD * @return */ private static long getStartPagePv(JSONObject taskParam, JavaPairRDD<String, Iterable<Row>> sessionid2actionsRDD) { String targetPageFlow = ParamUtils.getParam(taskParam, Constants.PARAM_TARGET_PAGE_FLOW); final long startPageId = Long.valueOf(targetPageFlow.split(",")[0]); JavaRDD<Long> startPageRDD = sessionid2actionsRDD.flatMap( new FlatMapFunction<Tuple2<String,Iterable<Row>>, Long>() { private static final long serialVersionUID = 1L; @Override public Iterable<Long> call( Tuple2<String, Iterable<Row>> tuple) throws Exception { List<Long> list = new ArrayList<Long>(); Iterator<Row> iterator = tuple._2.iterator(); while(iterator.hasNext()) { Row row = iterator.next(); long pageid = row.getLong(3); if(pageid == startPageId) { list.add(pageid); } } return list; } }); return startPageRDD.count(); } /** * 计算页面切片转化率 * @param pageSplitPvMap 页面切片pv * @param startPagePv 起始页面pv * @return */ private static Map<String, Double> computePageSplitConvertRate( JSONObject taskParam, Map<String, Object> pageSplitPvMap, long startPagePv) { Map<String, Double> convertRateMap = new HashMap<String, Double>(); String[] targetPages = ParamUtils.getParam(taskParam, Constants.PARAM_TARGET_PAGE_FLOW).split(","); long lastPageSplitPv = 0L; // 3,5,2,4,6 // 3_5 // 3_5 pv / 3 pv // 5_2 rate = 5_2 pv / 3_5 pv // 通过for循环,获取目标页面流中的各个页面切片(pv) for(int i = 1; i < targetPages.length; i++) { String targetPageSplit = targetPages[i - 1] + "_" + targetPages[i]; long targetPageSplitPv = Long.valueOf(String.valueOf( pageSplitPvMap.get(targetPageSplit))); double convertRate = 0.0; if(i == 1) { convertRate = NumberUtils.formatDouble( (double)targetPageSplitPv / (double)startPagePv, 2); } else { convertRate = NumberUtils.formatDouble( (double)targetPageSplitPv / (double)lastPageSplitPv, 2); } convertRateMap.put(targetPageSplit, convertRate); lastPageSplitPv = targetPageSplitPv; } return convertRateMap; } /** * 持久化转化率 * @param convertRateMap */ private static void persistConvertRate(long taskid, Map<String, Double> convertRateMap) { StringBuffer buffer = new StringBuffer(""); for(Map.Entry<String, Double> convertRateEntry : convertRateMap.entrySet()) { String pageSplit = convertRateEntry.getKey(); double convertRate = convertRateEntry.getValue(); buffer.append(pageSplit + "=" + convertRate + "|"); } String convertRate = buffer.toString(); convertRate = convertRate.substring(0, convertRate.length() - 1); PageSplitConvertRate pageSplitConvertRate = new PageSplitConvertRate(); pageSplitConvertRate.setTaskid(taskid); pageSplitConvertRate.setConvertRate(convertRate); IPageSplitConvertRateDAO pageSplitConvertRateDAO = DAOFactory.getPageSplitConvertRateDAO(); pageSplitConvertRateDAO.insert(pageSplitConvertRate); }}
阅读全文
0 0
- Spark日志分析项目Demo(6)--页面单跳转化率分析
- Spark日志分析项目Demo(3)--Spark入口和DataFrame
- Spark日志分析项目Demo(1)--Flume-ng的安装
- Spark日志分析项目Demo(5)--自定义Accumulator
- Spark日志分析项目Demo(9)--常规性能调优
- Spark日志分析项目Demo(10) --JVM调优
- 日志采集分析项目Demo
- Spark日志分析项目Demo(4)--RDD使用,用户行为统计分析
- Spark日志分析项目Demo(7)--临时表查询,各区域top3热门商品统计
- Spark日志分析项目Demo(8)--SparkStream,广告点击流量实时统计
- 日志分析demo
- spark 日志分析
- Spark日志分析案例
- srs 日志分析---spark
- Spark 分析Apache日志
- Spark做日志分析
- 单进程日志分析
- Spark-项目中分析日志的核心代码
- 【查找】二分查找
- POJ 1741 Tree【Tree,点分治】
- 快速入门基础与关键:目标与方向;考证行业基础入门知识与技能————能力测试认证体系
- xGBoost GBDT
- linux内核中的active_mm的作用
- Spark日志分析项目Demo(6)--页面单跳转化率分析
- redis集群搭建及注意事项
- 二、SQL语句映射文件(2)增删改查、参数、缓存
- Java多线程--资源限制可能影响多线程效率
- leetcode 42. Trapping Rain Water 正反循环遍历求解
- TCP和UDP的区别
- [贪心]codeforces
- 关于abstract为什么不能和static连用
- Moves in squared strings (I) -- 7Kyu