“无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。”。
目录
一、需求
二、样例数据
三、实现方式一
四、实现方式二
自定义分区取
重写排序规则
排序
五、实现方式三
在shuffle是在每一个分区中实现排序
另一种方式实现,使用ShuffleRDD
一、需求
通过分析用户浏览新闻热门话题的日志,统计每个话题下被浏览量最多的用户topN,即按照话题分组,在每一个组内进行排序
二、样例数据
数据格式:话题,时间,被浏览的用户id
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u014
#网易裁员#,2019-11-29,u014
#陈乔恩宣布恋情#,2019-11-29,u001
#陈乔恩宣布恋情#,2019-11-29,u001
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u003
#陈乔恩宣布恋情#,2019-11-29,u003
三、实现方式一
过程:调用groupBy按照话题进行分组,然后将value对应的迭代器toList,将数据全部加载到内存中,然后在调用List的sortBy方法进行排序,然后再调用take取TopN
分析:分组后,每一个组内的数量比较大,将迭代器toList会造成内存溢出,所以如果分组后组内的数据量比较大,这样的方式不适合。
object HotIssue01 {
def main(args: Array[String]): Unit = {
val isLocal = args(0).toBoolean
//创建SparkConf,然后创建SparkContext
val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
if (isLocal) {
conf.setMaster("local[*]")
}
val sc = new SparkContext(conf)
//指定以后从哪里读取数据创建RDD
val lines: RDD[String] = sc.textFile(args(1))
//排序的TopN
val topN = args(2).toInt
//对数据进行切分
val issueUidAndOne = lines.map(line => {
val fields = line.split(",")
val issue = fields(0)
val uid = fields(2)
((issue, uid), 1)
})
//聚合
val reduced = issueUidAndOne.reduceByKey(_ + _)
//分组
val grouped: RDD[(String, Iterable[((String, String), Int)])] =
reduced.groupBy(_._1._1)
//排序,将一个组的数据,全部加载到内存
// 如果在一些特殊场景下,一个组内的数据过多,可能会出现内存溢出
val result: RDD[(String, List[((String, String), Int)])] =
grouped.mapValues(_.toList.sortBy(-_._2).take(topN))
//触发Action,打印
val r = result.collect()
println(r.toBuffer)
sc.stop()
}
}
四、实现方式二
自定义分区器+TreeSet,将要计算的数据依次迭代出入放入TreeSet中排序,并且重新TreeSet的排序规则,需要注意的是并列的问题。
自定义分区取
class IssuePartitioner(val issues: Array[String]) extends Partitioner {
//初始化分器的分区规则
val rules = new mutable.HashMap[String, Int]()
var index = 0
for(is <- issues) {
rules(is) = index
index += 1
}
override def numPartitions: Int = issues.length
//该方法会在Executor中的Task中被调用
override def getPartition(key: Any): Int = {
val tuple = key.asInstanceOf[(String, String)]
val issue = tuple._1
//到实现初始化的规则中查找这个话题对应的分区编号
rules(issue)
}
}
重写排序规则
/**
* 定义封装数据的bean,并实现排序规则,同时考虑并列的问题
*/
class OrderingBean(val subject: String, val name: String, val count: Int)
extends Ordered[OrderingBean] with Serializable {
val equiv = new ArrayBuffer[(String, String, Int)]()
equiv += ((subject, name, count))
override def compare(that: OrderingBean): Int = {
if (this.count == that.count) {
equiv += ((that.subject, that.name, that.count))
0
} else {
-(this.count - that.count)
}
}
override def toString =
if (equiv.size > 1) {
equiv.toString()
} else
s"($subject, $name, $count)"
}
排序
object HotIssue02 {
def main(args: Array[String]): Unit = {
val isLocal = args(0).toBoolean
//创建SparkConf,然后创建SparkContext
val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
if (isLocal) {
conf.setMaster("local[*]")
}
val sc = new SparkContext(conf)
//指定以后从哪里读取数据创建RDD
val lines: RDD[String] = sc.textFile(args(1))
//排序的TopN
val topN = args(2).toInt
//对数据进行切分
val issueUidAndOne = lines.map(line => {
val fields = line.split(",")
val issue = fields(0)
val uid = fields(2)
((issue, uid), 1)
})
//计算所有话题,并收集到Driver端
val issues: Array[String] = issueUidAndOne.map(_._1._1)
.distinct()
.collect()
val partitioner = new IssuePartitioner(issues)
//根据指定的key和分区器进行聚合(减少一次shuffle)
val reduced: RDD[((String, String), Int)] =
issueUidAndOne.reduceByKey(partitioner, _+_)
val result = reduced.mapPartitions(it => {
//用自定义的OrderingBean封装数据
val sorter = new mutable.TreeSet[OrderingBean]()
//遍历出迭代器中的数据
it.foreach(t => {
//将数据依次迭代出来,方法TreeSet中安装定义好的规则进行排序
sorter += new OrderingBean(t._1._1, t._1._2, t._2)
if (sorter.size > topN) {
val last = sorter.last
//移除最后一个
sorter -= last
}
})
sorter.iterator
})
val r = result.collect()
println(r.toBuffer)
sc.stop()
}
}
五、实现方式三
使用reparationAndSortWithParititions方法,
在shuffle是在每一个分区中实现排序
object HotIssue03 {
def main(args: Array[String]): Unit = {
val isLocal = args(0).toBoolean
//创建SparkConf,然后创建SparkContext
val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
if (isLocal) {
conf.setMaster("local[1]")
}
val sc = new SparkContext(conf)
//指定以后从哪里读取数据创建RDD
val lines: RDD[String] = sc.textFile(args(1))
//对数据进行切分
val issueUidAndOne = lines.map(line => {
val fields = line.split(",")
val issue = fields(0)
val uid = fields(2)
((issue, uid), 1)
})
//聚合
val reduced = issueUidAndOne.reduceByKey(_ + _)
//计算所有的学科,并收集到Driver端
val issues: Array[String] = reduced.map(_._1._1).distinct().collect()
val partitioner = new IssuePartitioner(issues)
//对原来的数据进行整理
val keyByRDD: RDD[((String, String, Int), Null)] = reduced
.map(t => ((t._1._1, t._1._2, t._2), null))
//隐式转换
implicit val orderRules = new Ordering[(String, String, Int)] {
override def compare(x: (String, String, Int), y: (String, String, Int)): Int = {
-(x._3 - y._3)
}
}
val topN = args(2).toInt
//repartitionAndSortWithinPartitions按照指定的分区器进行排序并且在每个分区内进行排序
val result: RDD[((String, String, Int), Null)] = keyByRDD
.repartitionAndSortWithinPartitions(partitioner)
result.foreachPartition(it => {
var index = 1
while (it.hasNext && index <= topN) {
val tuple = it.next()
println(tuple)
index += 1
}
})
sc.stop()
}
}
另一种方式实现,使用ShuffleRDD
val sortInPartition = new ShuffledRDD(keyByRDD, partitioner).setKeyOrdering(orderRules)
sortInPartition.foreachPartition(it => {
var index = 1
while (it.hasNext && index <= topN) {
val tuple = it.next()
println(tuple)
index += 1
}
})
来源:CSDN
作者:北京小辉
链接:https://blog.csdn.net/silentwolfyh/article/details/103774496