• Spark Core 练习


    1、数据准备

    本次练习的数据是采集电商网站的用户行为数据,主要包含用户的4种行为:搜索、点击、下单和支付

    数据格式

    1. 数据采用_分割字段
    2. 每一行表示用户的一个行为,所以每一行只能是四种行为中的一种。
    3. 如果搜索关键字是null,表示这次不是搜索
    4. 如果点击的品类id和产品id是-1表示这次不是点击
    5. 下单行为来说一次可以下单多个产品,所以品类id和产品id都是多个,id之间使用逗号,分割。如果本次不是下单行为,则他们相关数据用null来表示
    6. 支付行为和下单行为类似

    数据详细字段说明

    编号

    字段名称

    字段类型

    字段含义

    1

    date

    String

    用户点击行为的日期

    2

    user_id

    Long

    用户的ID

    3

    session_id

    String

    Session的ID

    4

    page_id

    Long

    某个页面的ID

    5

    action_time

    String

    动作的时间点

    6

    search_keyword

    String

    用户搜索的关键词

    7

    click_category_id

    Long

    某一个商品品类的ID

    8

    click_product_id

    Long

    某一个商品的ID

    9

    order_category_ids

    String

    一次订单中所有品类的ID集合

    10

    order_product_ids

    String

    一次订单中所有商品的ID集合

    11

    pay_category_ids

    String

    一次支付中所有品类的ID集合

    12

    pay_product_ids

    String

    一次支付中所有商品的ID集合

    13

    city_id

    Long

    城市 id

    2、需求1 求top10 热门商品品类

    需求说明:品类是指产品的分类,大型电商网站品类分多级,咱们的项目中品类只有一级,不同的公司可能对热门的定义不一样。我们按照每个品类的点击、下单、支付的量来统计热门品类。

    鞋                 点击数 下单数  支付数

    衣服              点击数 下单数  支付数

    生活用品       点击数 下单数  支付数

    例如,综合排名=点击数*20%+下单数*30%+支付数*50%

    本项目需求优化为:先按照点击数排名,靠前的就排名高;如果点击数相同,再比较下单数;下单数再相同,就比较支付数。

    2.1、需求1 分析

    2.2、需求1 实现

    object Spark_RDD_TEST_Req10 {
      def main(args: Array[String]): Unit = {
        val conf: SparkConf = new SparkConf().setAppName("SparkTest").setMaster("local[*]")
        var sc: SparkContext = new SparkContext(conf)
        //读取文件
        val lineRdd: RDD[String] = sc.textFile("data/user_visit_action.txt")
        //封装对象
        val userVistActionRdd: RDD[UserVisitAction] = lineRdd.map {
          line => {
            //对每一行数据进行切分&封装成对象
            val strings: Array[String] = line.split("_")
            UserVisitAction(
              strings(0),
              strings(1).toLong,
              strings(2),
              strings(3).toLong,
              strings(4),
              strings(5),
              strings(6).toLong,
              strings(7).toLong,
              strings(8),
              strings(9),
              strings(10),
              strings(11),
              strings(12).toLong
            )
          }
        }
    
        //对访问行为进行分析,转换为封装CategoryCountInfo的对象
        val infoRDD: RDD[CategoryCountInfo] = userVistActionRdd.flatMap( //由于一个记录会涉及多个品类ID,所有使用flatMap, 如果都是一个品类 使用map 或者mapPartition
          userAction => {
            //判断是否是点击操作 click_category_id !=-1 表示点击行为
            if (userAction.click_category_id != -1) {
              var clickLists: ListBuffer[CategoryCountInfo] = new ListBuffer[CategoryCountInfo]
              clickLists.append(CategoryCountInfo(userAction.click_category_id.toString, 1, 0, 0))
              clickLists
            } else if (userAction.order_category_ids != "null") {//下单
              var orderLists: ListBuffer[CategoryCountInfo] = new ListBuffer[CategoryCountInfo]
              //点击&支付的拼品类ID是一个集合,需要切分后保存
              val ids: Array[String] = userAction.order_category_ids.split(",")
              for (id <- ids) {
                orderLists.append(CategoryCountInfo(id, 0, 1, 0))
              }
              orderLists
            } else if (userAction.pay_category_ids != "null") {//支付
              var payLists: ListBuffer[CategoryCountInfo] = new ListBuffer[CategoryCountInfo]
              val ids: Array[String] = userAction.pay_category_ids.split(",")
              for (id <- ids) {
                payLists.append(CategoryCountInfo(id, 0, 0, 1))
              }
              payLists
            } else {
              Nil
            }
          }
        )
        //reduceRDD.take(300).foreach(println)
        //将相同的跑品类放在一起
        val groupRDD: RDD[(String, Iterable[CategoryCountInfo])] = infoRDD.groupBy(_.categoryId)
        //groupRDD.take(300).foreach(println)
        //分组之后的数据进行求和
        val reduceRDD: RDD[(String, CategoryCountInfo)] = groupRDD.mapValues(
          datas => {
            datas.reduce((info1, info2) => {
              info1.clickCount += info2.clickCount
              info1.orderCount += info2.clickCount
              info1.payCount += info2.payCount
              info1
            })
          }
        )
        //对 reduceRDD 机构进行转换
        val mapRDD: RDD[CategoryCountInfo] = reduceRDD.map(_._2)
        //mapRDD.take(300).foreach(println)
        //排序降序 取 top10
        val res: Array[CategoryCountInfo] = mapRDD.sortBy(info => (info.clickCount, info.orderCount, info.payCount), false).take(10)
        res.foreach(println)
    
        sc.stop()
      }
    }
    
    //用户访问动作表
    case class UserVisitAction(date: String, //用户点击行为的日期
                               user_id: Long, //用户的ID
                               session_id: String, //Session的ID
                               page_id: Long, //某个页面的ID
                               action_time: String, //动作的时间点
                               search_keyword: String, //用户搜索的关键词
                               click_category_id: Long, //某一个商品品类的ID,!= -1 表示 点击行为
                               click_product_id: Long, //某一个商品的ID
                               order_category_ids: String, //一次订单中所有品类的ID集合
                               order_product_ids: String, //一次订单中所有商品的ID集合
                               pay_category_ids: String, //一次支付中所有品类的ID集合
                               pay_product_ids: String, //一次支付中所有商品的ID集合
                               city_id: Long) //城市 id
    // 输出结果表
    case class CategoryCountInfo(categoryId: String, //品类id
                                 var clickCount: Long, //点击次数
                                 var orderCount: Long, //订单次数
                                 var payCount: Long) //支付次数
    

    3、需求2 Top10热门品类中每个品类的Top10活跃Session统计

    3.1、需求分析

    • 通过需求1,获取TopN热门品类的id
    • 将原始数据进行过滤(1.保留热门品类 2.只保留点击操作)
    • 对session的点击数进行转换 (category-session,1)
    • 对session的点击数进行统计 (category-session,sum)
    • 将统计聚合的结果进行转换  (category,(session,sum))
    • 将转换后的结构按照品类进行分组 (category,Iterator[(session,sum)])
    • 对分组后的数据降序 取前10

    3.2、需求实现

    object Spark_RDD_TEST_Req2_Session_Top10 {
      def main(args: Array[String]): Unit = {
        val conf: SparkConf = new SparkConf().setAppName("SparkTest").setMaster("local[*]")
        var sc: SparkContext = new SparkContext(conf)
        //读取文件
        val lineRdd: RDD[String] = sc.textFile("data/user_visit_action.txt")
        //封装对象
        val userVistActionRdd: RDD[UserVisitAction] = lineRdd.map {
          line => {
            //对一行数据进行切分&封装
            val strings: Array[String] = line.split("_")
            UserVisitAction(
              strings(0),
              strings(1).toLong,
              strings(2),
              strings(3).toLong,
              strings(4),
              strings(5),
              strings(6).toLong,
              strings(7).toLong,
              strings(8),
              strings(9),
              strings(10),
              strings(11),
              strings(12).toLong
            )
          }
        }
    
        //对访问行为进行分析,转换为封装CategoryCountInfo的对象
        val infoRDD: RDD[CategoryCountInfo] = userVistActionRdd.flatMap( //由于一个记录会涉及多个品类ID,所有使用flatMap, 如果都是一个品类 使用map 或者mapPartition
          userAction => {
            //判断是否是点击操作
            if (userAction.click_category_id != -1) {
              var clickLists: ListBuffer[CategoryCountInfo] = new ListBuffer[CategoryCountInfo]
              clickLists.append(CategoryCountInfo(userAction.click_category_id.toString, 1, 0, 0))
              clickLists
            } else if (userAction.order_category_ids != "null") {
              var orderLists: ListBuffer[CategoryCountInfo] = new ListBuffer[CategoryCountInfo]
              val ids: Array[String] = userAction.order_category_ids.split(",")
              for (id <- ids) {
                orderLists.append(CategoryCountInfo(id, 0, 1, 0))
              }
              orderLists
            } else if (userAction.pay_category_ids != "null") {
              var payLists: ListBuffer[CategoryCountInfo] = new ListBuffer[CategoryCountInfo]
              val ids: Array[String] = userAction.pay_category_ids.split(",")
              for (id <- ids) {
                payLists.append(CategoryCountInfo(id, 0, 0, 1))
              }
              payLists
            } else {
              Nil
            }
          }
        )
        //reduceRDD.take(300).foreach(println)
        //将相同的跑品类放在一起
        val groupRDD: RDD[(String, Iterable[CategoryCountInfo])] = infoRDD.groupBy(_.categoryId)
        //groupRDD.take(300).foreach(println)
        //分组之后的数据进行求和
        val reduceRDD: RDD[(String, CategoryCountInfo)] = groupRDD.mapValues(
          datas => {
            datas.reduce((info1, info2) => {
              info1.clickCount += info2.clickCount
              info1.orderCount += info2.clickCount
              info1.payCount += info2.payCount
              info1
            })
          }
        )
        //对 reduceRDD 机构进行转换
        val mapRDD: RDD[CategoryCountInfo] = reduceRDD.map(_._2)
        //mapRDD.take(300).foreach(println)
        //排序降序 取 top10
        val res: Array[CategoryCountInfo] = mapRDD.sortBy(info => (info.clickCount, info.orderCount, info.payCount), false).take(10)
        //res.foreach(println)
        // 通过需求1,获取TopN热门品类的id
        val categoryIds: Array[String] = res.map(_.categoryId)
        //  将原始数据进行过滤(1.保留热门品类 2.只保留点击操作)
        val filterRDD: RDD[UserVisitAction] = userVistActionRdd.filter(
          userActions => {
            //只保留点击行为,并且点击行为的 品类ID在 top 热门品类中
            userActions.click_category_id != -1 && categoryIds.contains(userActions.click_category_id.toString)
          })
        //   对session的点击数进行转换 (category-session,1)
        val mapRDD1: RDD[(String, Int)] = filterRDD.map(
          userAction => (userAction.click_category_id + "_" + userAction.session_id, 1)
        )
        // 对session的点击数进行统计 (category-session,sum)
        val reduceRDD1: RDD[(String, Int)] = mapRDD1.reduceByKey(_ + _)
        //   将统计聚合的结果进行转换  (category,(session,sum))
        val mapRDD2: RDD[(String, (String, Int))] = reduceRDD1.map {
          case (cateAndSession, count) => {
            (cateAndSession.split("_")(0), (cateAndSession.split("_")(1), count))
          }
        }
        //  将转换后的结构按照品类进行分组 (category,Iterator[(session,sum)])
        val groupByKeyRDD: RDD[(String, Iterable[(String, Int)])] = mapRDD2.groupByKey()
        //   对分组后的数据降序 取前10
        val res2: RDD[(String, List[(String, Int)])] = groupByKeyRDD.mapValues(
          datas => {
            datas.toList.sortBy(-_._2).take(10)
          }
        )
    
        res2.collect().foreach(println)
    
        sc.stop()
      }
    }
    

    4、需求3:页面单跳转化率统计

    4.1、需求分析

    • 读取原始数据
    • 将原始数据映射为样例类
    • 将原始数据根据session进行分组
    • 将分组后的数据根据时间进行排序(升序)
    • 将排序后的数据进行结构的转换(pageId,1)
    • 计算分母-将相同的页面id进行聚合统计(pageId,sum)
    • 计算分子-将页面id进行拉链,形成连续的拉链效果,转换结构(pageId-pageId2,1)
    • 将转换结构后的数据进行聚合统计(pageId-pageId2,sum)
    • 计算页面单跳转换率

    4.2、需求实现

    object Spark_RDD_TEST_Req3_Trans_rate10$ {
      def main(args: Array[String]): Unit = {
        val conf: SparkConf = new SparkConf().setAppName("SparkTest").setMaster("local[*]")
        var sc: SparkContext = new SparkContext(conf)
        //读取文件
        val lineRdd: RDD[String] = sc.textFile("data/user_visit_action.txt")
        //封装对象
        val userVistActionRdd: RDD[UserVisitAction] = lineRdd.map {
          line => {
            //对一行数据进行切分&封装
            val strings: Array[String] = line.split("_")
            UserVisitAction(
              strings(0),
              strings(1).toLong,
              strings(2),
              strings(3).toLong,
              strings(4),
              strings(5),
              strings(6).toLong,
              strings(7).toLong,
              strings(8),
              strings(9),
              strings(10),
              strings(11),
              strings(12).toLong
            )
          }
        }
        //计算分母
        val fmMap: collection.Map[Long, Int] = userVistActionRdd.map(userAction => (userAction.page_id, 1)).reduceByKey(_ + _).collectAsMap()
        //计算分子
        //按照sessionID 对访问数据进行分组
        val groupRDD: RDD[(String, Iterable[UserVisitAction])] = userVistActionRdd.groupBy(_.session_id)
        //对数据进行排序
        val mapValueRDD: RDD[(String, List[(String, Int)])] = groupRDD.mapValues(
          datas => {
            val sortedList: List[UserVisitAction] = datas.toList.sortBy(_.action_time)
            //排序结果保留pageID
            val userVisitList: List[Long] = sortedList.map(_.page_id)
            //拉链形成单跳效果
            val pageFlowList: List[(Long, Long)] = userVisitList.zip(userVisitList.tail)
            //对拉链集合进行转换
            pageFlowList.map {
              case (page1, pag2) => {
                (page1 + "_" + pag2, 1)
              }
            }
          }
        )
        //对mapValue进行结构转换,只保留页面跳转&计数
        val pageFlowRDD: RDD[(String, Int)] = mapValueRDD.map(_._2).flatMap(list => list)
        val reduceBykeyRDD: RDD[(String, Int)] = pageFlowRDD.reduceByKey(_ + _)
        //计算单跳转换率
        reduceBykeyRDD.foreach {
          case (pageAToPageB, fz) => {
            //获取pageAid
            val pageAID: Long = pageAToPageB.split("_")(0).toLong
            //pageAID 获取pageA 分母的总访问量
            val fm: Int = fmMap.getOrElse(pageAID, 1)
            println("pageAToPageB --->" + fz.toDouble / fm)
          }
        }
        sc.stop()
      }
    }
  • 相关阅读:
    VS2010 Extension实践(3)——实现自定义配置
    VS2010 Extension实践(2)
    WinRT开发系列之基础概念:WinRT不是……
    [VS2010 Extension]PowerExtension.GoToDefinition
    如何通过反射调用带有ref或者out的参数的方法[迁移]
    Win7硬盘安装和移动硬盘访问出错的修复办法[迁移]
    zt. Windows Mobile开发文章收藏
    WinRT开发系列之编程语言:功能和效率
    VS2010 Extension实践
    maven创建父子工程
  • 原文地址:https://www.cnblogs.com/wdh01/p/16132115.html
Copyright © 2020-2023  润新知