背景
原文轉自作者Allen-Gao的一位博主,使用的api是RDD計算,文章最後附上我的和原博主的代碼。
項目說明:附件爲要計算數據的demo。附件(https://download.csdn.net/download/u013560925/10425558)
其中bs_log文件夾數據格式爲(手機號,時間戳,基站ID,連接狀態(“1”爲連接,“0”爲斷開))
lac_info.txt 文件數據格式爲(基站ID,經度,緯度,信號輻射類型)
程序思路:
1、先根據"手機號,基站ID"構成一個元祖,做爲唯一標識, 和時間戳構成新的數據結構->(手機號, 站點, 時間戳)
2、(手機號,基站ID)作爲key,通過reduceByKey算子進行聚合,計算出在基站的停留時間,構成新的數據結構,以便和座標數據進行joi
n,->(基站ID,(手機號,停留時間))
3、將基站座標數據信息通過map,構建成數據類型 ->(基站ID,(經度,緯度))
4、將2、3進行join操作,構成新的數據類型 ->(手機號,基站ID,停留時間,經度,緯度)
5、按手機號進行分組。->(手機號,(手機號,基站ID,停留時間,經度,緯度))
6、取出停留時間最長的兩個基站ID。
正文
原作者提供了兩個數據集,手機在某個基站的狀態和基站的位置信息:
原作者思路:先join,後篩選出手機停留基站時間top數據
我的代碼實現:先篩選出手機停留基站時間top數據,然後跟基站位置信息join
原作者代碼:
- package cn.allengao.Location
- import org.apache.spark.{SparkConf, SparkContext}
- /**
- * class_name:
- * package:
- * describe: 基站信息查詢
- * creat_user: Allen Gao
- * creat_date: 2018/1/29
- * creat_time: 10:03
- **/
- /*
- * 說明:
- * 1, 先根據"手機號,基站ID"構成一個元祖,做爲唯一標識, 和時間戳構成新的數據結構->(手機號, 站點, 時間戳)
- * 2、(手機號,基站ID)作爲key,通過reduceByKey算子進行聚合,計算出在基站的停留時間,構成新的數據結構,
- * 以便和座標數據進行join,->(基站ID,(手機號,停留時間))
- * 3、將基站座標數據信息通過map,構建成數據類型 ->(基站ID,(經度,緯度))
- * 4、將2、3進行join操作,構成新的數據類型 ->(手機號,基站ID,停留時間,經度,緯度)
- * 5、按手機號進行分組。->(手機號,(手機號,基站ID,停留時間,經度,緯度))
- * 6、取出停留時間最長的兩個基站ID。
- *
- */
- object UserLocation {
- def main(args: Array[String]): Unit = {
- //創建Spark配置信息
- val conf = new SparkConf().setAppName("UserLocation").setMaster("local[*]")
- //建立Spark上下文,並將配置信息導入
- val sc = new SparkContext(conf)
- /*
- 基站連接手機號,連接時間戳,基站站點ID信息,“1”表示連接,“0”表示斷開連接。
- 18688888888,20160327082400,16030401EAFB68F1E3CDF819735E1C66,1
- */
- //從log文件拿到數據,並按行採集。
- //sc.textFile("c://information//bs_log").map(_.split(",")).map(x => (x(0), x(1), x(2), x(3)))
- val rdd_Info = sc.textFile("j://information//bs_log").map(line => {
- //通過“,”將數據進行切分field(0)手機號,field(1)時間戳,field(2)基站ID信息,field(3)事件類型
- val fields = line.split(",")
- //事件類型,“1”表示連接,“0”表示斷開。
- val eventType = fields(3)
- val time = fields(1)
- //連接基站將時間戳至爲“-”,斷開基站將時間戳至爲“+”,以便後面進行計算。
- val timeLong = if(eventType == "1") -time.toLong else time.toLong
- //構成一個數據類型(手機號,基站ID信息,帶符號的時間戳)
- ((fields(0),fields(2)),timeLong)
- })
- val rdd_lacInfo = rdd_Info.reduceByKey(_+_).map(t=>{
- val mobile = t._1._1
- val lac = t._1._2
- val time = t._2
- (lac, (mobile, time))
- })
- val rdd_coordinate = sc.textFile("j://information//lac_info.txt").map(line =>{
- val f = line.split(",")
- //(基站ID, (經度, 緯度))
- (f(0),(f(1), f(2)))
- })
- //rdd1.join(rdd2)-->(CC0710CC94ECC657A8561DE549D940E0,((18688888888,1300),(116.303955,40.041935)))
- val rdd_all = rdd_lacInfo.join(rdd_coordinate).map(t =>{
- val lac = t._1
- val mobile = t._2._1._1
- val time = t._2._1._2
- val x = t._2._2._1
- val y = t._2._2._2
- (mobile, lac, time, x, y)
- })
- //按照手機號進行分組
- val rdd_mobile = rdd_all.groupBy(_._1)
- //取出停留時間最長的前兩個基站
- val rdd_topTwo= rdd_mobile.mapValues(it =>{
- it.toList.sortBy(_._3).reverse.take(2)
- })
- // println(rdd_Info.collect().toBuffer)
- // println(rdd_lacInfo.collect().toBuffer)
- // println(rdd_coordinate.collect().toBuffer)
- // println(rdd_all.collect().toBuffer)
- // println(rdd_mobile.collect().toBuffer)
- // println(rdd_topTwo.collect().toBuffer)
- rdd_topTwo.saveAsTextFile("j://information//out")
- sc.stop()
- }
- }
我的代碼:
val data = spark.read.textFile("hdfs://master:9000/user/wangqi/logs").rdd
.map(x=>x.split(",")).map(line=>{
val stayTime = if(line(3).toInt==1) line(1).toLong else -line(1).toLong
((line(0),line(2)),stayTime)
}).reduceByKey(_+_).groupBy(_._1._2).mapValues(it=>{
it.toList.sortBy(_._2).reverse.take(2)
}).flatMap{
line=>line._2
}
val top2Rdd = data.map(line=>(line._1._2,(line._1._1,line._2)))
val lacRdd = spark.read.textFile("hdfs://master:9000/user/wangqi/lac_info.txt").rdd
.map(line=>{
val f = line.split(",")
(f(0),(f(1),f(2)))
})
val result = lacRdd.join(top2Rdd).map(line=>(line._2._2._1,line._1,line._2._2._2,line._2._1._1,line._2._1._2))
result.saveAsTextFile("hdfs://master:9000/user/wangqi/lacResult")
總結
sortBy(key,false)按照key進行降序排列
groupBy:後是(key,List())後可使用mapValues對List進行操作
核心思想是:尋找所有類別格子的top數據