spark分析美团日志
- 一、数据描述
- 二、数据下载
- 三、需求
- 二、某答题系统日志解析
-
- 2.1 数据描述
- 2.2 数据下载
- 2.3 需求
-
- 题1:数据准备,请在HDFS 中创建目录/app/data/exam,并将answer_question.log 传到该目录
- 题2:在Spark-Shell 中,加载HDFS 文件系统answer_question.log 文件,并使用RDD 完成以下分析,也可使用Spark 的其他方法完成数据分析
- 题3:创建HBase 数据表
- 题4:创建Hive数据表
- 题5:使用ex_exam_record表中的数据统计每个学员总分、答题的试题数和正确率,并保存到ex_exam_anlysis 表中,其中正确率的计算方法如下:正确率=总分/答题的试题数
- 题6:使用ex_exam_record 表中的数据统计每个做对,做错,半对的题目列表
一、数据描述
meituan_waimai_meishi.csv 是美团外卖平台的部分外卖 SPU(Standard Product Unit)
标准产品单元数据, 包含了外卖平台某地区一时间的外卖信息 。具体字段说明如下:
字段名称 | 中文名称 | 数据类型 |
---|---|---|
spu_id | 商品spuID | String |
shop_id | 店铺ID | String |
shop_name | 店铺名称 | String |
category_name | 类别名称 | String |
spu_name | SPU名称 | String |
spu_price | SPU商品售价 | Double |
spu_originprice | SPU商品原价 | Double |
month_sales | 月销售量 | Int |
praise_num | 点赞数 | Int |
spu_unit | SPU单位 | String |
spu_desc | SPU描述 | String |
spu_image | 商品图片 | String |
二、数据下载
链接:meituan_waimai_meishi.csv
提取码:47k1
三、需求
1. 数据准备
请在HDFS中创建目录 /app/data/exam,并将meituan_waimai_meishi.csv 文件传到该目录,并通过HDFS命令查询出文档有多少行数据
- 创建目录:
hdfs dfs -mkdir -p /app/data/exam
- 上传文件:
hdfs dfs -put meituan_waimai_meishi.csv /app/data/exam/
- 读取文件行数:
hdfs dfs -cat /app/data/exam/meituan_waimai_meishi.csv | wc -l
2. 使用 Spark 加载HDFS文件系统 meituan_waimai_meishi.csv 文件,并分别使用 RDD 和 SparkSQL 完成以下分析(不用考虑数据去重)
①统计每个店铺分别有多少商品(SPU)
②统计每个店铺的总销售额
③统计每个店铺销售额最高的前三个商品输出内容包括店铺名,商品名和销售额其中销售额为0的商品不进行统计计算,例如:如果某个店铺销售为0则不进行统计
2.1 使用RDD查询
- 装载数据:
val fileRDD = sc.textFile("hdfs://hadoop-single:9000/app/data/exam/meituan_waimai_meishi.csv")
- 过滤首行,以及错误数据(行数字段<12)
val spuRDD = fileRDD.filter(x => x.startsWith("spu_id")==false).map(x => x.split(",",-1)).filter(_.length==12)
- ①统计每个店铺分别有多少商品(SPU)
spuRDD.map(x => (x(2),1)).reduceByKey(_+_).foreach(println)
- ②统计每个店铺的总销售额
简单的写法:
spuRDD.map(x => (x(2),x(5).toDouble * x(7).toInt)).reduceByKey(_+_).foreach(println)
安全的写法,防止有格式不对的值无法转型 double 或者 int 会报错:
需先引入scala工具包:
import scala.util._
使用 Try() 包裹转换类型,转成Option对象 toOption,在使用 getOrElse()
spuRDD.map(x => (x(2),Try(x(5).toDouble).toOption.getOrElse(0.0) * Try(x(7).toInt).toOption.getOrElse(0))).reduceByKey(_+_).foreach(println)
- ③统计每个店铺销售额最高的前三个商品输出内容包括店铺名,商品名和销售额其中销售额为0的商品不进行统计计算,例如:如果某个店铺销售为0则不进行统计
思路: 用元祖形式统计三个字段:“店铺名shop_name x(2)”, “商品名spu_name x(4)”, “销售额spu_price * month_sales即x(5) * x(7)” -> 过滤销量为0的 -> 分组groupBy(shop_name) -> 自定义map中的内容map((shop_name,销售前三的销售额))
步骤1: 查询每个店铺每种商品的销售额
spuRDD.map(x => (x(2),x(4),Try(x(5).toDouble).toOption.getOrElse(0.0) * Try(x(7).toInt).toOption.getOrElse(0))).foreach(println)
部分截图如下:
步骤2: 过滤销量为0的商品,并进行分组
spuRDD.map(x => (x(2),x(4),Try(x(5).toDouble).toOption.getOrElse(0.0) * Try(x(7).toInt).toOption.getOrElse(0))).filter(_._3>0).groupBy(_._1).foreach(println)
部分截图如下:
注: 此时经过groupBy之后的,元组的格式变成了 (String, Iterable[(String, String, Double)]) ,_.1表示商家名称,_.2是一个可迭代的对象,其中的元组 (String, String, Double) 代表原先map查找的字段 (店铺名称,商品名称,销售额)
如下如查询所示,分组后的元组格式为二元组,二元组中的第二个元素又是嵌套了很多个三元组
格式为: (shop_name, ( (shop_name,spu_name,spu_pricemonth_sales),(shop_name,spu_name,spu_pricemonth_sales) … )
步骤3: 完整答案,过滤销量为0的,取销量前三名,倒序输出sortBy(-1* )
//answer 1:
spuRDD.map(x => (x(2),x(4),Try(x(5).toDouble).toOption.getOrElse(0.0) * Try(x(7).toInt).toOption.getOrElse(0))).filter(_._3>0).groupBy(_._1).map(x => {
val shop_name = x._1;
val spu_name_totalPrice = x._2.toList.sortBy(-1*_._3).take(3).map(item => {
item._2+"--"+item._3});
(shop_name,spu_name_totalPrice)
}).foreach(println)
返回结果元组格式:(string,List( (string,string), (string,string), (string,string) )
如下例查询结果所示:
(
三顾冒菜(曼城国际店),
List(单人荤素+1个米饭