【Spark Core实战】电商网站用户行为数据分析 (1)

本文实战案例数据来自尚硅谷大数据技术之Spark教程,资源下载链接见评论区。

环境版本信息

操作系统:Windows 10家庭中文版 (21H1)
编程语言:Scala
集成开发环境:IntelliJ IDEA 2021.1.1 (Ultimate Edition)
JDK版本:13.0.1
Maven版本:3.8.1
Scala版本:2.12.11
Spark版本:3.0.0

数据集说明

在这里插入图片描述
数据集是尚硅谷的电商网站用户行为数据,主要包含用户的4种行为:搜索,点击,下单,支付。数据规则如下:

  • 数据文件中每行数据采用下划线分隔。
  • 每一行数据表示用户的一次行为。
  • 如果搜索关键字为null,表示数据不是搜索数据。
  • 如果点击的品类ID和产品ID为-1,表示数据不是点击数据。
  • 针对于下单行为,一次可以下单多个商品,所以品类ID和产品ID可以是多个,ID之间采用逗号分隔;如果本次不是下单行为,则数据采用null表示。
  • 支付行为和下单行为类似。

详细字段说明如下:

编号字段名称字段类型字段含义
1dateString用户点击行为的日期
2user_idLong用户ID
3session_idStringSession ID
4page_idLong页面ID
5action_timeString动作的时间点
6search_keywordString用户搜索关键字
7click_category_idLong商品品类ID
8click_product_idLong商品ID
9order_category_idsString一次订单中所有品类的ID集合
10order_product_idsString一次订单中所有商品的ID集合
11pay_category_idsString一次支付中所有品类的ID集合
12pay_product_idsString一次支付中所有商品的ID集合
13city_idLong城市ID

需求说明

统计热门品类的Top 10。这里对热门品类的定义是:按照每个品类的点击数量、下单数量、支付数量从高到低排列,即先按照点击数量排名,若相同则比较下单数量,若下单数量相同则比较支付数量。

实现方式1

读取文件中的数据,转换数据结构,其中点击行为转换为(商品品类ID, (1, 0, 0)),下单行为转换为(商品品类ID, (0, 1, 0)),支付行为转换为(商品品类ID, (0, 0, 1))。将商品品类ID相同的数据进行分组聚合,对其降序排序并取Top 10即可满足需求。
在IDEA中新建Maven工程,导入Scala插件支持。
在这里插入图片描述

创建data目录,用于存放数据集。项目结构如下:
在这里插入图片描述

在pom.xml文件中导入Spark相关依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cch</groupId>
    <artifactId>user_visit_action</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>
    </dependencies>

</project>

在Maven项目的target/classes/目录下新建log4j.properties文件,输入以下内容,过滤掉除ERROR以外的信息:

log4j.rootCategory=ERROR, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
# Set the default spark-shell log level to ERROR. When running the spark-shell, the
# log level for this class is used to overwrite the root logger's log level, so that
# the user can have different defaults for the shell and regular Spark apps.
log4j.logger.org.apache.spark.repl.Main=ERROR
# Settings to quiet third party logs that are too verbose
log4j.logger.org.spark_project.jetty=ERROR
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=ERROR
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=ERROR
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR
# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support
log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL
log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR

Scala代码如下:

package com.cch.bigdata.spark.core.req

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * 需求1:Top10热门品类
 */
object Req1_HotCategoryTop10Analysis {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("HotCategoryTop10Analysis")
    val sc = new SparkContext(sparkConf)

    val actionRDD: RDD[String] = sc.textFile("data/user_visit_action.txt").cache()

    // 转换数据结构
    val flatRDD: RDD[(String, (Int, Int, Int))] = actionRDD.flatMap(
      action => {
        val data: Array[String] = action.split("_")
        if (data(6) != "-1") { // 点击
          List((data(6), (1, 0, 0)))
        } else if (data(8) != "null") { // 下单
          val ids: Array[String] = data(8).split(",")
          ids.map(id => (id, (0, 1, 0)))
        } else if (data(10) != "null") { // 支付
          val ids: Array[String] = data(10).split(",")
          ids.map(id => (id, (0, 0, 1)))
        } else {
          Nil
        }
      }
    )

    // 相同品类ID数据分组聚合
    val analysisRDD: RDD[(String, (Int, Int, Int))] = flatRDD.reduceByKey(
      (t1, t2) => (t1._1 + t2._1, t1._2 + t2._2, t1._3 + t2._3)
    )

    // 降序排序并取Top10
    val result: Array[(String, (Int, Int, Int))] = analysisRDD.sortBy(_._2, ascending = false).take(10)

    result.foreach(println)

    sc.stop()
  }
}

实现方式2

在使用reduceByKey()方法时会引入Shuffle操作,可以使用累加器对数据进行聚合,提高程序性能。
Scala代码如下:

package com.cch.bigdata.spark.core.req

import org.apache.spark.rdd.RDD
import org.apache.spark.util.AccumulatorV2
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable

/**
 * 需求1:Top10热门品类
 */
object Req1_HotCategoryTop10Analysis_Acc {
  def main(args: Array[String]): Unit = {
    val sc = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("HotCategoryTop10AnalysisAcc"))

    val actionRDD: RDD[String] = sc.textFile("data/user_visit_action.txt").cache()
    val acc = new HotCategoryAccumulator
    sc.register(acc, "hotCategory")

    actionRDD.foreach(
      action => {
        val data: Array[String] = action.split("_")
        if (data(6) != "-1") { // 点击
          acc.add((data(6), "click"))
        } else if (data(8) != "null") { // 下单
          val ids: Array[String] = data(8).split(",")
          ids.foreach(id => acc.add((id, "order")))
        } else if (data(10) != "null") { // 支付
          val ids: Array[String] = data(10).split(",")
          ids.foreach(id => acc.add((id, "pay")))
        }
      }
    )
    val accVal: mutable.Map[String, HotCategory] = acc.value
    val categories: Iterable[HotCategory] = accVal.values
    val result: List[(String, (Int, Int, Int))] = categories.toList.sortWith(
      (left, right) => {
        if (left.clickCnt > right.clickCnt) {
          true
        } else if (left.clickCnt == right.clickCnt) {
          if (left.orderCnt > right.orderCnt) {
            true
          } else if (left.orderCnt == right.orderCnt) {
            left.payCnt > right.payCnt
          } else {
            false
          }
        } else {
          false
        }
      }
    ).take(10).map(hc => (hc.cid, (hc.clickCnt, hc.orderCnt, hc.payCnt)))

    result.foreach(println)

    sc.stop()
  }

  case class HotCategory(cid: String, var clickCnt: Int, var orderCnt: Int, var payCnt: Int)

  /**
   * 自定义累加器
   */
  class HotCategoryAccumulator extends AccumulatorV2[(String, String), mutable.Map[String, HotCategory]] {
    private val hcMap = mutable.Map[String, HotCategory]()

    override def isZero: Boolean = hcMap.isEmpty

    override def copy(): AccumulatorV2[(String, String), mutable.Map[String, HotCategory]] = new HotCategoryAccumulator

    override def reset(): Unit = hcMap.clear()

    override def add(v: (String, String)): Unit = {
      val cid = v._1
      val actionType = v._2
      val category: HotCategory = hcMap.getOrElse(cid, HotCategory(cid, 0, 0, 0))
      if (actionType == "click") {
        category.clickCnt += 1
      } else if (actionType == "order") {
        category.orderCnt += 1
      } else if (actionType == "pay") {
        category.payCnt += 1
      }
      hcMap.update(cid, category)
    }

    override def merge(other: AccumulatorV2[(String, String), mutable.Map[String, HotCategory]]): Unit = {
      val map1: mutable.Map[String, HotCategory] = this.hcMap
      val map2: mutable.Map[String, HotCategory] = other.value
      map2.foreach {
        case (cid, hc) =>
          val category: HotCategory = map1.getOrElse(cid, HotCategory(cid, 0, 0, 0))
          category.clickCnt += hc.clickCnt
          category.orderCnt += hc.orderCnt
          category.payCnt += hc.payCnt
          map1.update(cid, category)
      }
    }

    override def value: mutable.Map[String, HotCategory] = hcMap
  }
}

运行结果

(15,(6120,1672,1259))
(2,(6119,1767,1196))
(20,(6098,1776,1244))
(12,(6095,1740,1218))
(11,(6093,1781,1202))
(17,(6079,1752,1231))
(7,(6074,1796,1252))
(9,(6045,1736,1230))
(19,(6044,1722,1158))
(13,(6036,1781,1161))
  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
上百节课详细讲解,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 课程介绍: 讲解一个真实的、复杂的大型企业级大数据项目,是Spark的大型项目实战课程。 通过本套课程的学习,可以积累大量Spark项目经验,迈入Spark高级开发行列。 课程特色: 1、项目中全面覆盖了Spark CoreSpark SQL和Spark Streaming这三个技术框架几乎全部的初级和高级的技术点和知识点, 让学员学以致用,通过一套课程,即掌握如何将Spark所有的技术点和知识点应用在真实的项目中,来实现业务需求! 2、项目中的4个功能横块,全郃是实际企业项目中提取出来的,并进行技术整合和改良过的功能模块.全都是企业级的复杂和真实的需求,业务模块非常之复杂,绝对不是市面上的Dem级别的大数据项目能够想比拟的,学习过后,真正帮助学员增加实际 企业级项目的实战经验。 3、项目中通过实际的功能模块和业务场景,以及讲师曾经开发过的处理十亿、甚至百亿以上数据级别的SparK作业的经验积累,贯穿讲解了大量的高级复杂的性能调优技术和知识、troubleshooting解决线上报错和故障的经验、高端的全方位数据倾斜处理和解决方案.真正帮助学员掌握高精尖的Spark技术! 4、项目中采用完全还原企业大数据项目开发场景的方式来讲解,每一个业务模块的讲解都包括了需求分析、方案设计、数据设计、编码实现、功能测试、性能调优等环节,真实还原企业级大数据项目开发场景。 模块简介: 1、用户访问session分析,该模块主要是对用户访问session进行统计分析.包括session的聚合指标计算、 按时间比例随机抽取session、获取每天点击、下单和购买排名前10的品类、并获取top10品类的点击量排名前10的session.该模块可以让产品经理、数据分析师以及企业管理层形象地看到各种条件下的具体用户行为以及统计指标.从而对公司的产品设计以及业务发展战略做出调整.主要使用Spark Core实现. 2、页面单跳转化率统计,该模块主要是计算关键页面之间的单步跳转转化率,涉及到页面切片算法以及页面流匹配算法.该模块可以让产品经理、数据分析师以及企业管理层看到各个关键页面之间的转化率.从而对网页布局,进行更好的优化设计。主要使用Spark Core实现. 3、热门商品离线统计,该模块主要实现每天统计出各个区域的top3热门商品.然后使用Oozie进行离线统计任务的定时调度,使用Zeppeline进行数据可视化的报表展示.该模块可以让企业管理层看到公司售卖的 商品的整体情况,从而对公司的商品相关的战略进行调螫.主要使用Spark SQL实现。 4、广告流量实时统计.该模块负责实时统计公司的广告流量.包括广告展现流量和广告点击流量,实现动态黑名单机制以及黑名单过滤,实现滑动窗口内的各城市的广告展现流立和广告点击流直的统计,实现 每个区域诲个广告的点击流置实时统计,实现每个区域top3点击量的广告的统计,主要使用Spark Streaming实现.
如今的大数据技术应用场景,对实时性的要求已经越来越高。作为新一代大数据流处理框架,由于非常好的实时性,Flink独树一帜,在近些年引起了业内极大的兴趣和关注。Flink能够提供毫秒级别的延迟,同时保证了数据处理的低延迟、高吞吐和结果的正确性,还提供了丰富的时间类型和窗口计算、Exactly-once 语义支持,另外还可以进行状态管理,并提供了CEP(复杂事件处理)的支持。Flink在实时分析领域的优势,使得越来越多的公司开始将实时项目向Flink迁移,其社区也在快速发展壮大。目前,Flink已经成为各大公司实时领域的发力重点,特别是国内以阿里为代表的一众大厂,都在全力投入,不少公司为Flink社区贡献了大量源码。如今Flink已被很多人认为是大数据实时处理的方向和未来,很多公司也都在招聘和储备了解掌握Flink的人才。本教程将Flink理论与电商数据分析项目实战并重,对Flink基础理论知识做了系统的梳理和阐述,并通过电商用户行为分析的具体项目用多个指标进行了实战演练。为有志于增加大数据项目经验、扩展流式处理框架知识的工程师提供了学习方式。二、教程内容和目标本教程主要分为两部分:第一部分,主要是Flink基础理论的讲解,涉及到各种重要概念、原理和API的用法,并且会有大量的示例代码实现;第二部分,以电商作为业务应用场景,以Flink作为分析框架,介绍一个电商用户行为分析项目的开发实战。通过理论和实际的紧密结合,可以使学员对Flink有充分的认识和理解,在项目实战中对Flink和流式处理应用的场景、以及电商分析业务领域有更深刻的认识;并且通过对流处理原理的学习和与批处理架构的对比,可以对大数据处理架构有更全面的了解,为日后成长为架构师打下基础。三、谁适合学1、有一定的 Java、Scala 基础,希望了解新的大数据方向的编程人员2、有 Java、Scala 开发经验,了解大数据相关知识,希望增加项目经验的开发人员3、有较好的大数据基础,希望掌握Flink及流式处理框架的求职人员

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值