SparkCore项目实战中用到的数据集
(1)数据采用 _ 分割字段;
(2)每一行表示用户的一个行为,所以每一行只能是四种行为中的一种;
(3)如果搜索关键字是null,表示这次不是搜索;
(4)如果点击的品类id和产品id是-1,表示这次不是点击;
(5)下单行为来说一次可以下单多个产品,所以品类id和产品id都是多个,id之间使用 ,(逗号)分割;如果本次不是下单行为,则他们相关数据用null来表示;
(6)支付行为和下单行为类似。
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
object Spark01_TopN_req1 {
def main(args: Array[String]): Unit = {
//创建SparkConf并设置App名称
val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
//创建SparkContext,该对象是提交Spark App的入口
val sc: SparkContext = new SparkContext(conf)
//1.读取数据,创建RDD
val dataRDD: RDD[String] = sc.textFile("D:\MyWork\IdeaProjects\spark0105_exer\src\main\input\user_visit_action.txt")
//2.将读到的数据进行切分,并且将切分的内容封装为UserVisitAction对象
val actionRDD: RDD[UserVisitAction] = dataRDD.map {
line => {
val fields: Array[String] = line.split("_")
UserVisitAction(
fields(0),
fields(1).toLong,
fields(2),
fields(3).toLong,
fields(4),
fields(5),
fields(6).toLong,
fields(7).toLong,
fields(8),
fields(9),
fields(10),
fields(11),
fields(12).toLong
)
}
}
//3.判断当前这条日志记录的是什么行为,并且封装为结果对象 (品类,点击数,下单数,支付数)==>例如:如果是鞋的点击行为 (鞋,1,0,0)
//(鞋,1,0,0)
//(保健品,1,0,0)
//(鞋,0,1,0)
//(保健品,0,1,0)
//(鞋,0,0,1)=====>(鞋,1,1,1)
val infoRDD: RDD[CategoryCountInfo] = actionRDD.flatMap {
userAction => {
//判断是否为点击行为
if (userAction.click_category_id != -1) {
//封装输出结果对象
List(CategoryCountInfo(userAction.click_category_id + "", 1, 0, 0))
} else if (userAction.order_category_ids != "null") {
//坑:读取的文件应该是null字符串,而不是null对象
//判断是否为下单行为,如果是下单行为,需要对当前订单中涉及的所有品类Id进行切分
val ids: Array[String] = userAction.order_category_ids.split(",")
//定义一个集合,用于存放多个品类id封装的输出结果对象
val categoryCountInfoList: ListBuffer[CategoryCountInfo] = ListBuffer[CategoryCountInfo]()
//对所有品类的id进行遍历
for (id <- ids) {
categoryCountInfoList.append(CategoryCountInfo(id, 0, 1, 0))
}
categoryCountInfoList
} else if (userAction.pay_category_ids != "null") {
//支付
val ids: Array[String] = userAction.pay_category_ids.split(",")
//定义一个集合,用于存放多个品类id封装的输出结果对象
val categoryCountInfoList: ListBuffer[CategoryCountInfo] = ListBuffer[CategoryCountInfo]()
//对所有品类的id进行遍历
for (id <- ids) {
categoryCountInfoList.append(CategoryCountInfo(id, 0, 0, 1))
}
categoryCountInfoList
} else {
Nil
}
}
}
//4.将相同品类的放到一组
val groupRDD: RDD[(String, Iterable[CategoryCountInfo])] = infoRDD.groupBy(_.categoryId)
//5.将分组之后的数据进行聚合处理 (鞋,100,90,80)
val reduceRDD: RDD[(String, CategoryCountInfo)] = groupRDD.mapValues {
datas => {
datas.reduce {
(info1, info2) => {
info1.clickCount = info1.clickCount + info2.clickCount
info1.orderCount = info1.orderCount + info2.orderCount
info1.payCount = info1.payCount + info2.payCount
info1
}
}
}
}
//6.对上述RDD的结构进行转换,只保留value部分 ,得到聚合之后的RDD[CategoryCountInfo]
val mapRDD: RDD[CategoryCountInfo] = reduceRDD.map(_._2)
//7.对RDD中的数据排序,取前10
val res: Array[CategoryCountInfo] = mapRDD.sortBy(info=>(info.clickCount,info.orderCount,info.payCount),false).take(10)
//8.打印输出
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
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)//支付次数
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
object Spark02_TopN_req2 {
def main(args: Array[String]): Unit = {
//创建SparkConf并设置App名称
val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
//创建SparkContext,该对象是提交Spark App的入口
val sc: SparkContext = new SparkContext(conf)
//1.读取数据,创建RDD
val dataRDD: RDD[String] = sc.textFile("D:\MyWork\IdeaProjects\spark0105_exer\src\main\input\user_visit_action.txt")
//2.将读到的数据进行切分,并且将切分的内容封装为UserVisitAction对象
val actionRDD: RDD[UserVisitAction] = dataRDD.map {
line => {
val fields: Array[String] = line.split("_")
UserVisitAction(
fields(0),
fields(1).toLong,
fields(2),
fields(3).toLong,
fields(4),
fields(5),
fields(6).toLong,
fields(7).toLong,
fields(8),
fields(9),
fields(10),
fields(11),
fields(12).toLong
)
}
}
//3.判断当前这条日志记录的是什么行为,并且封装为结果对象 (品类,点击数,下单数,支付数)==>例如:如果是鞋的点击行为 (鞋,1,0,0)
//(鞋,1,0,0)
//(保健品,1,0,0)
//(鞋,0,1,0)
//(保健品,0,1,0)
//(鞋,0,0,1)=====>(鞋,1,1,1)
val infoRDD: RDD[CategoryCountInfo] = actionRDD.flatMap {
userAction => {
//判断是否为点击行为
if (userAction.click_category_id != -1) {
//封装输出结果对象
List(CategoryCountInfo(userAction.click_category_id + "", 1, 0, 0))
} else if (userAction.order_category_ids != "null") {
//坑:读取的文件应该是null字符串,而不是null对象
//判断是否为下单行为,如果是下单行为,需要对当前订单中涉及的所有品类Id进行切分
val ids: Array[String] = userAction.order_category_ids.split(",")
//定义一个集合,用于存放多个品类id封装的输出结果对象
val categoryCountInfoList: ListBuffer[CategoryCountInfo] = ListBuffer[CategoryCountInfo]()
//对所有品类的id进行遍历
for (id <- ids) {
categoryCountInfoList.append(CategoryCountInfo(id, 0, 1, 0))
}
categoryCountInfoList
} else if (userAction.pay_category_ids != "null") {
//支付
val ids: Array[String] = userAction.pay_category_ids.split(",")
//定义一个集合,用于存放多个品类id封装的输出结果对象
val categoryCountInfoList: ListBuffer[CategoryCountInfo] = ListBuffer[CategoryCountInfo]()
//对所有品类的id进行遍历
for (id <- ids) {
categoryCountInfoList.append(CategoryCountInfo(id, 0, 0, 1))
}
categoryCountInfoList
} else {
Nil
}
}
}
//4.将相同品类的放到一组
val groupRDD: RDD[(String, Iterable[CategoryCountInfo])] = infoRDD.groupBy(_.categoryId)
//5.将分组之后的数据进行聚合处理 (鞋,100,90,80)
val reduceRDD: RDD[(String, CategoryCountInfo)] = groupRDD.mapValues {
datas => {
datas.reduce {
(info1, info2) => {
info1.clickCount = info1.clickCount + info2.clickCount
info1.orderCount = info1.orderCount + info2.orderCount
info1.payCount = info1.payCount + info2.payCount
info1
}
}
}
}
//6.对上述RDD的结构进行转换,只保留value部分 ,得到聚合之后的RDD[CategoryCountInfo]
val mapRDD: RDD[CategoryCountInfo] = reduceRDD.map(_._2)
//7.对RDD中的数据排序,取前10
val res: Array[CategoryCountInfo] = mapRDD.sortBy(info=>(info.clickCount,info.orderCount,info.payCount),false).take(10)
//8.打印输出
//res.foreach(println)
//===============================需求二===================================
//1.获取热门品类top10的品类id
val ids: Array[String] = res.map(_.categoryId)
//ids可以进行优化, 因为发送给Excutor中的Task使用,每一个Task都会创建一个副本,所以可以使用广播变量
val broadcastIds: Broadcast[Array[String]] = sc.broadcast(ids)
//2.将原始数据进行过滤(1.保留热门品类 2.只保留点击操作)
val filterRDD: RDD[UserVisitAction] = actionRDD.filter {
action => {
//只保留点击行为
if (action.click_category_id != -1) {
//同时确定是热门品类的点击
//坑 集合数据为字符串类型,id是Long类型,需要进行转换
broadcastIds.value.contains(action.click_category_id.toString)
} else {
false
}
}
}
//3.对session的点击数进行转换 (category_session,1)
val mapRDD1: RDD[(String, Int)] = filterRDD.map {
action => {
(action.click_category_id + "_" + action.session_id, 1)
}
}
//4.对session的点击数进行统计 (category_session,sum)
val reduceRDD1: RDD[(String, Int)] = mapRDD1.reduceByKey(_+_)
//5.将统计聚合的结果进行转换 (category,(session,sum))
val mapRDD2: RDD[(String, (String, Int))] = reduceRDD1.map {
case (categoryAndSession, sum) => {
(categoryAndSession.split("_")(0), (categoryAndSession.split("_")(1), sum))
}
}
//6.将转换后的结构按照品类进行分组 (category,Iterator[(session,sum)])
val groupRDD2: RDD[(String, Iterable[(String, Int)])] = mapRDD2.groupByKey()
//7.对分组后的数据降序 取前10
val resRDD: RDD[(String, List[(String, Int)])] = groupRDD2.mapValues {
datas => {
datas.toList.sortWith {
case (left, right) => {
left._2 > right._2
}
}.take(10)
}
}
resRDD.foreach(println)
// 关闭连接
sc.stop()
}
}
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
object Spark03_TopN_req3 {
def main(args: Array[String]): Unit = {
//创建SparkConf并设置App名称
val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
//创建SparkContext,该对象是提交Spark App的入口
val sc: SparkContext = new SparkContext(conf)
//1.读取数据,创建RDD
val dataRDD: RDD[String] = sc.textFile("E:\\Felix课程\\大数据\\大数据_200105\\Felix_02_尚硅谷大数据技术之Spark\\2.资料\\spark-core数据\\user_visit_action.txt")
//2.将读到的数据进行切分,并且将切分的内容封装为UserVisitAction对象
val actionRDD: RDD[UserVisitAction] = dataRDD.map {
line => {
val fields: Array[String] = line.split("_")
UserVisitAction(
fields(0),
fields(1).toLong,
fields(2),
fields(3).toLong,
fields(4),
fields(5),
fields(6).toLong,
fields(7).toLong,
fields(8),
fields(9),
fields(10),
fields(11),
fields(12).toLong
)
}
}
//========================================需求三 ========================================
//1.对当前日志中记录的访问页面进行计数
val pageIdRDD: RDD[(Long, Long)] = actionRDD.map {
action => {
(action.page_id, 1L)
}
}
//2.通过页面的计数,计算每一个页面出现的总次数 作为求单跳转换率的分母
val fmIdsMap: Map[Long, Long] = pageIdRDD.reduceByKey(_+_).collect().toMap
/*
zs 11:35:00 首页
ls 11:35:00 首页
zs 11:36:00 详情
zs 11:37:00 下单
*/
//3.计算分子
//3.1 将原始数据根据sessionId进行分组
val sessionRDD: RDD[(String, Iterable[UserVisitAction])] = actionRDD.groupBy(_.session_id)
//3.2 将分组后的数据按照时间进行升序排序
val pageFlowRDD: RDD[(String, List[(String, Int)])] = sessionRDD.mapValues {
datas => {
//得到排序后的同一个session的用户访问行为
val userActions: List[UserVisitAction] = datas.toList.sortWith {
(left, right) => {
left.action_time < right.action_time
}
}
//3.3 对排序后的用户访问行为进行结构转换,只保留页面就可以
val pageIdsList: List[Long] = userActions.map(_.page_id)
//A->B->C->D->E->F
//B->C->D->E->F
//3.4 对当前会话用户访问页面 进行拉链 ,得到页面的流转情况 (页面A,页面B)
val pageFlows: List[(Long, Long)] = pageIdsList.zip(pageIdsList.tail)
//3.5 对拉链后的数据,进行结构的转换 (页面A-页面B,1)
pageFlows.map {
case (pageId1, pageId2) => {
(pageId1 + "-" + pageId2, 1)
}
}
}
}
// 3.6 将每一个会话的页面跳转统计完毕之后,没有必要保留会话信息了,所以对上述RDD的结构进行转换
//只保留页面跳转以及计数
val pageFlowMapRDD: RDD[(String, Int)] = pageFlowRDD.map(_._2).flatMap(list=>list)
//3.7 对页面跳转情况进行聚合操作
val pageAToPageBSumRDD: RDD[(String, Int)] = pageFlowMapRDD.reduceByKey(_+_)
//4.页面单跳转换率计算
pageAToPageBSumRDD.foreach{
//(pageA-pageB,sum)
case (pageFlow,fz)=>{
val pageIds: Array[String] = pageFlow.split("-")
//获取分母页面id
val fmPageId: Long = pageIds(0).toLong
//根据分母页面id,获取分母页面总访问数
val fmSum: Long = fmIdsMap.getOrElse(fmPageId,1L)
//转换率
println(pageFlow +"--->" + fz.toDouble / fmSum)
}
}
// 关闭连接
sc.stop()
}
}