SparkCoreの実践的な演習

1.データの準備

        このプロジェクトのデータは、eコマースWebサイトからのユーザー行動データの収集であり、主に4種類のユーザー行動(検索、クリック、注文、支払い)が含まれています。

データ形式

ここに画像の説明を挿入

  • データは_splitフィールドを使用します。
  • 各行はユーザーの行動を表すため、各行は4つの行動のうちの1つにしかなれません。
  • 検索キーワードがnullの場合は、今回は検索ではないことを意味します。
  • クリックされたカテゴリIDと製品IDが-1の場合、今回はクリックされていないことを意味します。
  • 注文動作に関しては、一度に複数の商品を注文できるため、カテゴリIDと商品IDは両方とも複数であり、IDはカンマで区切られます。これが注文でない場合、それらの関連データはnullで表されます。
  • 支払いの動作は、注文の動作と似ています。

データセットのダウンロード

リンク:https
://pan.baidu.com/s/1ZLhYdXz1Foi6MpeUBFGCnQ抽出コード:12lt

需要1:人気のある上位10のカテゴリ

ニーズの声明

        カテゴリとは、製品の分類を指します。大規模なeコマースWebサイトには、複数のカテゴリがあります。プロジェクトには1つのカテゴリしかありません。企業によって、人気の定義が異なる場合があります。人気のあるカテゴリは、各カテゴリのクリック数、注文数、支払い額に応じてカウントされます。
        このプロジェクトの要件は次のように最適化されています:クリック数に応じた最初のランク、および上位ランク。クリック数が同じ場合は注文数を比較します。注文数が再び同じ場合は、支払い回数を比較します。

需要分析

  • 操作が「クリック」、「注文」、「支払い」の3つのタイプのどれであるかを判別します。
  • カテゴリと操作カテゴリを(カテゴリ、(クリック数、注文数、支払い数))にまとめると、メタグループをオブジェクトとして組み立てることができます。
  • キーカテゴリに従って、3つの操作の総数を集計します。
  • トップ10を並べ替えます。

コード:

データ情報をカプセル化するサンプルクラスを作成します

// 样例类可以自动生成apply方法和unapply()方法
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
                           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
// 定义样例类存储商品品类及对应的操作次数
// 样例类的属性默认使用val修饰,不可重新赋值
case class CategoryCountInfo(categoryId: String, //品类id
                             var clickCount: Long, //点击次数
                             var orderCount: Long, //订单次数
                             var payCount: Long) //支付次数

1つのコードを要求する

    val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[*]")
    val sc = new SparkContext(conf)
    val dataRDD: RDD[String] = sc.textFile("D:\\学习资料\\spark\\spark\\2.资料\\spark-core数据\\user_visit_action.txt")
    // 封装对象
    val userActionRDD: RDD[UserVisitAction] = dataRDD.map {
    
    
      line => {
    
    
        val lineSplit: Array[String] = line.split("_")
        // 封装对象
        UserVisitAction(
          lineSplit(0),
          lineSplit(1).toLong,
          lineSplit(2),
          lineSplit(3).toLong,
          lineSplit(4),
          lineSplit(5),
          lineSplit(6).toLong,
          lineSplit(7).toLong,
          lineSplit(8),
          lineSplit(9),
          lineSplit(10),
          lineSplit(11),
          lineSplit(12).toLong
        )
      }
    }
    // 转换数据格式
    val cateInfoRDD: RDD[(String, CategoryCountInfo)] = userActionRDD.flatMap {
    
    
      userAction => {
    
    
        // 判断操作类型
        if (userAction.click_category_id != -1) {
    
    
          // 转换数据格式 (品类, CategoryCountInfo对象)
          List((userAction.click_category_id.toString,
            CategoryCountInfo(userAction.click_category_id.toString, 1, 0, 0)))
        } else if (userAction.order_category_ids != "null") {
    
    
          // 处理order_category_ids
          val cateIds: Array[String] = userAction.order_category_ids.split(",")
          // ListBuffer存储多个对象
          val list = new mutable.ListBuffer[(String, CategoryCountInfo)]()
          for (cateId <- cateIds) {
    
    
            list.append((cateId, CategoryCountInfo(cateId, 0, 1, 0)))
          }
          list
        } else if (userAction.pay_category_ids != "null") {
    
    
          val cateIds: Array[String] = userAction.pay_category_ids.split(",")
          // ListBuffer存储多个对象
          val list = new mutable.ListBuffer[(String, CategoryCountInfo)]()
          for (cateId <- cateIds) {
    
    
            list.append((cateId, CategoryCountInfo(cateId, 0, 0, 1)))
          }
          list
        } else {
    
    
          // 返回空集合
          Nil
        }
      }
    }
    // 根据key聚合数据
    val reduceRDD: RDD[(String, CategoryCountInfo)] = cateInfoRDD.reduceByKey(
      (cate1, cate2) => {
    
    
        cate1.clickCount = cate1.clickCount + cate2.clickCount
        cate1.orderCount = cate1.orderCount + cate2.orderCount
        cate1.payCount = cate1.payCount + cate2.payCount
        cate1
      }
    )
    // 去掉多余的key,转换格式
    val cateCountRDD: RDD[CategoryCountInfo] = reduceRDD.map {
    
    
      _._2
    }
    // 排序取前10
    val resArr: Array[CategoryCountInfo] = cateCountRDD.sortBy(
      // 元组可以按照元素的顺序来依次排序
      cate =>{
    
    (cate.clickCount,cate.orderCount,cate.payCount)},
      // 倒序
      false
    ).take(10)
    sc.stop()

需要2:人気のあるトップ10カテゴリの各カテゴリのアクティブセッション統計トップ10

ニーズの声明

        上位10のカテゴリについて、各カテゴリの上位10クリックのsessionIdを取得します。(注:ここでは、注文と支払いの数ではなく、クリック数のみに焦点を当てています。)
        top10カテゴリの場合、各カテゴリは、クリック数でトップ10にランク付けされるsessionIdを取得する必要があります。この関数を使用すると、特定のユーザーグループに最も関心のあるカテゴリと、各カテゴリの最も一般的なユーザーのセッション動作を確認できます。

需要分析

  • 需要1から人気のあるトップ10カテゴリのIDを取得します。
  • データをフィルタリングすると、値は上位10の人気のあるカテゴリIDと対応するクリックログを保持します。
  • データ形式を変換します(カテゴリid_session、1)。
  • 上記のデータを集計し、各カテゴリのセッションクリック数をカウントします。
  • データ形式(カテゴリID、(セッション、カウント))を変換します。
  • カテゴリ別にグループ化します。
  • 各グループの上位10を取得して、逆の順序で並べ替えます。

コード

  • 要件2は、要件1の結果に依存する必要があります。
	// =======================================上面是需求1=============================================
    // 取出top10的商品id
    val top10Ids: Array[String] = resArr.map(_.categoryId)
    // top10Ids要发送到每个task,可以做广播变量优化
    val broadcast: Broadcast[Array[String]] = sc.broadcast(top10Ids)
    // 过滤数据,只保留top10 id对应的点击数据
    val filterRDD: RDD[UserVisitAction] = userActionRDD.filter(
      // 注意click_category_id的类型是Long类型,需要转换为String类型
      datas => {
    
    
        if (datas.click_category_id != -1) {
    
    
          broadcast.value.contains(datas.click_category_id.toString)
        } else {
    
    
          false
        }
      }
    )
    // 转换格式 (品类id_session, 1)
    val cateIdAndSession1: RDD[(String, Int)] = filterRDD.map(
      datas => {
    
    
        (datas.click_category_id + "_" + datas.session_id, 1)
      }
    )
    // 按照相同的key聚合 (品类id_session, count)
    val cateIdAndSessionCount: RDD[(String, Int)] = cateIdAndSession1.reduceByKey(_ + _)
    // 转换数据格式 (品类id, (session, count))
    val cateIdAndSessionCount2: RDD[(String, (String, Int))] = cateIdAndSessionCount.map {
    
    
      case (idAndSession, count) => {
    
    
        val split: Array[String] = idAndSession.split("_")
        (split(0), (split(1), count))
      }
    }
    // 按照品类分组
    val cateGroupRDD: RDD[(String, Iterable[(String, Int)])] = cateIdAndSessionCount2.groupByKey()
    // 倒序排序,取前10
    val res2RDD: RDD[(String, List[(String, Int)])] = cateGroupRDD.mapValues(
      datas => {
    
    
        val list: List[(String, Int)] = datas.toList
        // 排序
        list.sortWith(_._2 > _._2)
      }.take(10)
    )
    res2RDD.foreach(println)

    sc.stop()

要件3:ページの単一ジャンプ率の統計

ニーズの声明

        ページのシングルジャンプコンバージョン率を計算します。たとえば、セッション中にユーザーがアクセスしたページパス3、5、7、9、10、21のように、ページのシングルジャンプのコンバージョン率を計算すると、ページ3がページにジャンプします。 5および一度呼び出されるシングルジャンプ、7-9はシングルジャンプとも呼ばれ、シングルジャンプレートはページクリックの確率をカウントすることです。
        例:シングルジャンプ率3〜5を計算し、最初に対象セッションの3ページの訪問数(PV)をAとして取得し、次に対象セッションの3ページへの訪問数(PV)を取得して次に5ページに移動します。回数はBで、B / Aは3〜5のページシングルジャンプ率です。

需要分析

  • 最初に各ページへの訪問数を数えることができます
        ->最初にデータ形式を変換します:(ページID、1)
        ->次にページIDに従って集計し、各ページへの訪問の総数を見つけます(ページID、カウント)
        ->次にページA->ページB->ページC->ページD ...シングルジャンプ時間をカウントします
  • セッションIDとページID(セッションID-ページID、アクセス時間)に従ってユーザーアクセス時間を並べ替えます
  • データ形式を変換し、セッションIDでグループ化します
        -> session1:ページA->ページB->ページC-
        >セッション2:->ページA->ページB->ページC
  • 各グループのアクセスシーケンスのコレクションリスト(各ユーザーのページアクセスシーケンス)を返します
        ->ページアクセスシーケンス:A-> B-> C-> D-> E-
        >コレクションテールでジッパーを作成します:B-> C-> D-> E
        ->(A、B)(B、C)(C、D)(D、E)
  • ジッパーによって形成されたセット内の各要素が表示される回数、つまり、各ページのシングルホップの数を見つけます((A、B)、カウント)
  • 最後に、ページあたりのシングルジャンプの数/ページあたりの合計回数=ページのシングルジャンプ率を使用します
	val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName).setMaster("local[*]")
    val sc = new SparkContext(conf)
    val dataRDD: RDD[String] = sc.textFile("D:\\学习资料\\spark\\spark\\2.资料\\spark-core数据\\user_visit_action.txt")
    // 封装对象
    val userVisitActionRDD: RDD[UserVisitAction] = dataRDD.map {
    
    
      line => {
    
    
        val lineSplit: Array[String] = line.split("_")
        // 封装对象
        UserVisitAction(
          lineSplit(0),
          lineSplit(1).toLong,
          lineSplit(2),
          lineSplit(3).toLong,
          lineSplit(4),
          lineSplit(5),
          lineSplit(6).toLong,
          lineSplit(7).toLong,
          lineSplit(8),
          lineSplit(9),
          lineSplit(10),
          lineSplit(11),
          lineSplit(12).toLong
        )
      }
    }

    // 先统计每个页面的访问次数
    // (页面id,1) -> (页面id,count)
    val pageAnd1: RDD[(Long, Long)] = userVisitActionRDD.map(
      action => {
    
    
        (action.page_id, 1L)
      }
    )
    // 按照pageId聚合,每个页面的访问次数, 转为map字典,方便使用
    val pageAndCount: Map[Long, Long] = pageAnd1.reduceByKey(_ + _).collect().toMap

    // 根据sessionid和pageid,按照时间排序
    val sessionAndPage: RDD[(String, String)] = userVisitActionRDD.map(
      action => {
    
    
        // 转换格式 (sessionid_pageid, 时间)
        (action.session_id + "_" + action.page_id, action.action_time)
      }
    )
    // 按照sessionid分组,按照时间正序排序
    // 转换格式 (sessionid, (pageid,时间))
    val sessionPageTime: RDD[(String, (String, String))] = sessionAndPage.map {
    
    
      case (session_page, time) => {
    
    
        val split: Array[String] = session_page.split("_")
        (split(0), (split(1), time))
      }
    }
    // 按照sessionid分组,每个用户的访问页面和时间
    val sessionGroup: RDD[(String, Iterable[(String, String)])] = sessionPageTime.groupByKey()
    // 转换格式,舍弃sessionid Iterable(pageid,时间)
    val pageTime: RDD[Iterable[(String, String)]] = sessionGroup.map(_._2)
    // 按照时间正序排序 Iterable(pageid,时间)
    val pageSortRDD: RDD[Iterable[(String, String)]] = pageTime.map(
      data => {
    
    
        data.toList.sortWith(_._2 < _._2)

      }
    )
    //转换格式,舍弃时间 Iterable(pageid)
    val pageRDD: RDD[List[String]] = pageSortRDD.map(
      datas => {
    
    
        datas.toList.map(_._1)
      }
    )

    // 拉链操作,组成((A,B),1) 的形式,方便计算从A到B的跳转数
    val zipRDD: RDD[((String, String), Long)] = pageRDD.flatMap(
      pageList => {
    
    
        pageList.zip(pageList.tail).map((_, 1L))
      }
    )
    val breakCount: RDD[((String, String), Long)] = zipRDD.reduceByKey(_ + _)

    // breakCount.take(3).foreach(println)
    // 计算A到B的跳转数
    // 跳转数 / 页面访问数
    val resRDD: RDD[String] = breakCount.map {
    
    
      case (breakPage, count) => {
    
    
        val sum: Long = pageAndCount.getOrElse(breakPage._1.toLong, 1)
        val res: Double = count * 1.0 / sum
        breakPage + ":" + res
      }
    }
    resRDD.foreach(println)

    //breakRDD.take(3).foreach(println)

    sc.stop()

おすすめ

転載: blog.csdn.net/FlatTiger/article/details/115205775