数据:http://bigdata.edu360.cn/laozhang
http://bigdata.edu360.cn/laozhang
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laoduan
http://bigdata.edu360.cn/laoduan
http://javaee.edu360.cn/xiaoxu
http://javaee.edu360.cn/xiaoxu
http://javaee.edu360.cn/laoyang
http://javaee.edu360.cn/laoyang
http://javaee.edu360.cn/laoyang
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laoduan
http://bigdata.edu360.cn/laoduan
http://javaee.edu360.cn/xiaoxu
http://javaee.edu360.cn/xiaoxu
http://javaee.edu360.cn/laoyang
http://javaee.edu360.cn/laoyang
http://javaee.edu360.cn/laoyang
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laozhao
http://bigdata.edu360.cn/laoduan
http://bigdata.edu360.cn/laoduan
http://javaee.edu360.cn/xiaoxu
http://javaee.edu360.cn/xiaoxu
http://javaee.edu360.cn/laoyang
http://javaee.edu360.cn/laoyang
http://javaee.edu360.cn/laoyang
http://php.edu360.cn/laoli
http://php.edu360.cn/laoliu
http://php.edu360.cn/laoli
http://php.edu360.cn/laoli
任务一
求出点击次数最多的老师
解析:然后用分词计数思路解决。
-
首先将数据进行切分,将url的形式切分成,将学科、老师数据进行切分,然后进行存储,将老师和Int 1进行组合构成元组。
-
先按Key(老师)进行聚合
-
根据聚合后的value(次数)进行排序
然后就输出
package cn.edu360.day3
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object FavTeacher {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("FavTeacher").setMaster("local[4]")
val sc = new SparkContext(conf)
//指定以后从哪里读取数据
val lines: RDD[String] = sc.textFile(args(0))
//整理数据
val teacherAndOne = lines.map(line => {
val index = line.lastIndexOf("/")
val teacher = line.substring(index + 1)
//val httpHost = line.substring(0, index)
//val subject = new URL(httpHost).getHost.split("[.]")(0)
(teacher, 1)
})
//聚合
val reduced: RDD[(String, Int)] = teacherAndOne.reduceByKey(_+_)
//排序
val sorted: RDD[(String, Int)] = reduced.sortBy(_._2, false)
//触发Action执行计算
val reslut: Array[(String, Int)] = sorted.collect()
//打印
println(reslut.toBuffer)
sc.stop()
}
}
分组求TopN案例1
处理过程:
- 首先将数据进行切分,将url的形式切分成,将学科、老师数据进行切分,然后进行存储, 构造这样的数据((subject, teacher), 1)
- 将(subject, teacher)作为key,对其进行聚合累加:调用方法reduceByKey(+),在此进行了一次shullfe
- 然后根据subject进行分组,reduced.groupBy(_._1._1),调用方法返回的是RDD[(String, Iterable[((String, String), Int)])]这种形式,一个构造器中含有的是一个subject的内容,在此进行了一次shullfe
- 然后分别其Value,即对每个构造器进行操作,进行排序(排序调用,scala的sortBy方法是在内存上进行操作,当数据量太大的时候容易内存爆了),取TopN 。mapValues(.toList.sortBy(._2).reverse.take(topN))
- 触发Action执行计算 val reslut: Array[(String, Int)] = sorted.collect()
数据量少的时候比较快,适合数据量小的情况,但是数据量的时候容易发生数据溢出
package cn.edu360.day3
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}
object GroupFavTeacher1 {
def main(args: Array[String]): Unit = {
val topN = args(1).toInt
val conf = new SparkConf().setAppName("FavTeacher").setMaster("local[4]")
val sc = new SparkContext(conf)
//指定以后从哪里读取数据,输出数据为((subject, teacher), 1)
val lines: RDD[String] = sc.textFile(args(0))
//整理数据
val sbjectTeacherAndOne: RDD[((String, String), Int)] = lines.map(line => {
val index = line.lastIndexOf("/")
val teacher = line.substring(index + 1)
val httpHost = line.substring(0, index)
val subject = new URL(httpHost).getHost.split("[.]")(0)
((subject, teacher), 1)
})
//聚合,将学科和老师联合当做key,进行计数,这里进行了一次shullfe
val reduced: RDD[((String, String), Int)] = sbjectTeacherAndOne.reduceByKey(_+_)
//分组排序(按学科进行分组)
//[学科,该学科对应的老师的数据]
//返回的结果是一个迭代器,属于同一学科的在一个迭代器当中
//val grouped: RDD[(String, Iterable[((String, String), Int)])] = reduced.groupBy((t: ((String, String), Int)) =>t._1._1, 4) 也可以指定分区,使得一个分区一个学科
val grouped: RDD[(String, Iterable[((String, String), Int)])] = reduced.groupBy(_._1._1)
//经过分组后,一个分区内可能有多个学科的数据,一个学科就是一个迭代器
//将每一个组拿出来进行操作
//为什么可以调用sacla的sortby方法呢?因为一个学科的数据已经在一台机器上的一个scala集合里面了,相当于对LIst调用sortBy操作
//缺点是,scala的sortBy方法是在内存上进行操作,当数据量太大的时候容易内存爆了
val sorted = grouped.mapValues(_.toList.sortBy(_._2).reverse.take(topN))
//收集结果
val r: Array[(String, List[((String, String), Int)])] = sorted.collect()
//打印
println(r.toBuffer)
sc.stop()
}
}
分组求TopN案例2
过滤多次提交
处理过程:
-
首先将数据进行切分,将url的形式切分成,将学科、老师数据进行切分,然后进行存储, 构造这样的数据((subject, teacher), 1)
-
将(subject, teacher)作为key,对其进行聚合累加:调用方法reduceByKey(+),在此进行了一次shullfe
-
//计算有多少学科,获取全部学list,调用reduced.map(_._1._1).distinct().collect()
-
利用for循环,遍历每个学科,然后用学科作为条件,运用reduced.filter(_._1._1 == sb)将该学科过滤出来
-
每次循环会得到一个rdd,只有一个学科,然后对其调用sortBy(_._2, false).take(topN)(调用得是rdd的方法sortBy,则不会只用内存计算,不用担心内存溢出的问题),然后action触发计算
每次循环触发一次action,每次计算一部分数据,,再加上sortBy方法计算是运用内存+磁盘的方式,计算压力比较小,适合每个学科数据量比较的情况
package cn.edu360.day3
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object GroupFavTeacher2 {
def main(args: Array[String]): Unit = {
val topN = args(1).toInt
val conf = new SparkConf().setAppName("GroupFavTeacher2").setMaster("local[4]")
val sc = new SparkContext(conf)
//指定以后从哪里读取数据
val lines: RDD[String] = sc.textFile(args(0))
//整理数据
val sbjectTeacherAndOne: RDD[((String, String), Int)] = lines.map(line => {
val index = line.lastIndexOf("/")
val teacher = line.substring(index + 1)
val httpHost = line.substring(0, index)
val subject = new URL(httpHost).getHost.split("[.]")(0)
((subject, teacher), 1)
})
//聚合,将学科和老师联合当做key
val reduced: RDD[((String, String), Int)] = sbjectTeacherAndOne.reduceByKey(_+_)
//计算有多少学科
val subjects: Array[String] = reduced.map(_._1._1).distinct().collect()
//cache到内存
//val cached = reduced.cache()
//scala的集合排序是在内存中进行的,但是内存有可能不够用
//可以调用RDD的sortby方法,内存+磁盘进行排序
for (sb <- subjects) {
//该RDD中对应的数据仅有一个学科的数据(因为过滤过了)
val filtered: RDD[((String, String), Int)] = reduced.filter(_._1._1 == sb)
//现在调用的是RDD的sortBy方法,(take是一个action,会触发任务提交)
val favTeacher = filtered.sortBy(_._2, false).take(topN)
//打印
println(favTeacher.toBuffer)
}
sc.stop()
}
}
分组求TopN案例3
分区的三种形式
1、HashPartitioner
scala> val counts = sc.parallelize(List((1,'a'),(1,'aa'),(2,'b'),(2,'bb'),(3,'c')), 3)
.partitionBy(new HashPartitioner(3))
HashPartitioner确定分区的方式:partition = key.hashCode () % numPartitions
2、RangePartitioner
scala> val counts = sc.parallelize(List((1,'a'),(1,'aa'),(2,'b'),(2,'bb'),(3,'c')), 3)
.partitionBy(new RangePartitioner(3,counts))
RangePartitioner会对key值进行排序,然后将key值被划分成3份key值集合。
3、CustomPartitioner
CustomPartitioner可以根据自己具体的应用需求,自定义分区。
分区器中含有两个需要重写的方法
- override def numPartitions: Int = sbs.length
可以在这个方法指定分区的数量 - override def getPartition(key: Any): Int = {}
可以在此指定分区数量 ,默认是按照hashcode的规则来指定分区,输入参数任意,输出参数是Int即分区
CustomPartitioner(numParts: Int) extends Partitioner {
override def numPartitions: Int = numParts
override def getPartition(key: Any): Int =
{
if(key==1)){
0
} else if (key==2){
1} else{
2 }
}
}
scala> val counts = sc.parallelize(List((1,'a'),(1,'aa'),(2,'b'),(2,'bb'),(3,'c')), 3).partitionBy(new CustomPartitioner(3))
实现步骤:
处理过程:
- 首先将数据进行切分,将url的形式切分成,将学科、老师数据进行切分,然后进行存储, 构造这样的数据((subject, teacher), 1)
- 将(subject, teacher)作为key,对其进行聚合累加:调用方法reduceByKey(+),在此进行了一次shullfe
- 然后调用自定义构造器,在构造器中,构造一个hashmap,将subject与数值存入当中,规则是将构造器中的规则重写将输入subject时,输出它的分区编号
- 调用partitionBy(sbPatitioner),将构造的分区器传入,则可以重新制定分区规则,然后就会按分区进行分组,一个学科一个分区
- 调用mapPartitions方法,对每个分区进行独立的计算,在此进行了一次shullfe
- 每个分区中将迭代器转换成list,然后排序,在转换成迭代器返回
partitioned.mapPartitions(it => {
//将迭代器转换成list,然后排序,在转换成迭代器返回
it.toList.sortBy(_._2).reverse.take(topN).iterator
}) - 最后action触发计算
代码:
package cn.edu360.day3
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
import scala.collection.mutable
/**
* Created by zx on 2017/10/8.
*/
object GroupFavTeacher3 {
def main(args: Array[String]): Unit = {
val topN = args(1).toInt
val conf = new SparkConf().setAppName("GroupFavTeacher2").setMaster("local[4]")
val sc = new SparkContext(conf)
//指定以后从哪里读取数据
val lines: RDD[String] = sc.textFile(args(0))
//整理数据
val sbjectTeacherAndOne: RDD[((String, String), Int)] = lines.map(line => {
val index = line.lastIndexOf("/")
val teacher = line.substring(index + 1)
val httpHost = line.substring(0, index)
val subject = new URL(httpHost).getHost.split("[.]")(0)
((subject, teacher), 1)
})
//聚合,将学科和老师联合当做key
val reduced: RDD[((String, String), Int)] = sbjectTeacherAndOne.reduceByKey(_+_)
//计算有多少学科
val subjects: Array[String] = reduced.map(_._1._1).distinct().collect()
//自定义一个分区器,并且按照指定的分区器进行分区
val sbPatitioner = new SubjectParitioner(subjects);
//partitionBy按照指定的分区规则进行分区
//调用partitionBy时RDD的Key是(String, String)
val partitioned: RDD[((String, String), Int)] = reduced.partitionBy(sbPatitioner)
//如果一次拿出一个分区(可以操作一个分区中的数据了)
val sorted: RDD[((String, String), Int)] = partitioned.mapPartitions(it => {
//将迭代器转换成list,然后排序,在转换成迭代器返回
it.toList.sortBy(_._2).reverse.take(topN).iterator
})
//
val r: Array[((String, String), Int)] = sorted.collect()
println(r.toBuffer)
sc.stop()
}
}
//自定义分区器
class SubjectParitioner(sbs: Array[String]) extends Partitioner {
//相当于主构造器(new的时候回执行一次)
//用于存放规则的一个map
val rules = new mutable.HashMap[String, Int]()
var i = 0
for(sb <- sbs) {
//rules(sb) = i
rules.put(sb, i)
i += 1
}
//返回分区的数量(下一个RDD有多少分区)
override def numPartitions: Int = sbs.length
//根据传入的key计算分区标号
//key是一个元组(String, String)
override def getPartition(key: Any): Int = {
//获取学科名称
val subject = key.asInstanceOf[(String, String)]._1
//根据规则计算分区编号
rules(subject)
}
}
分组求TopN案例4
与上个案例相比只有一次shullfe,加快效率
- 首先将数据进行切分,将url的形式切分成,将学科、老师数据进行切分,然后进行存储, 构造这样的数据((subject, teacher), 1)
- 将(subject, teacher)作为key,对其进行聚合累加,同时调用分区器:reduceByKey(sbPatitioner, +),在此进行了一次shullfe
- 然后调用自定义构造器,在构造器中,构造一个hashmap,将subject与数值存入当中,规则是将构造器中的规则重写将输入subject时,输出它的分区编号
- 调用partitionBy(sbPatitioner),将构造的分区器传入,则可以重新制定分区规则,然后就会按分区进行分组,一个学科一个分区
- 调用mapPartitions方法,对每个分区进行独立的计算,在此进行了一次shullfe
- 每个分区中将迭代器转换成list,然后排序,在转换成迭代器返回
partitioned.mapPartitions(it => {
//将迭代器转换成list,然后排序,在转换成迭代器返回
it.toList.sortBy(_._2).reverse.take(topN).iterator
}) - 最后action触发计算
package cn.edu360.day3
import java.net.URL
import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
import scala.collection.mutable
/**
* Created by zx on 2017/10/8.
*/
object GroupFavTeacher4 {
def main(args: Array[String]): Unit = {
val topN = args(1).toInt
val conf = new SparkConf().setAppName("GroupFavTeacher2").setMaster("local[4]")
val sc = new SparkContext(conf)
//指定以后从哪里读取数据
val lines: RDD[String] = sc.textFile(args(0))
//整理数据
val sbjectTeacherAndOne: RDD[((String, String), Int)] = lines.map(line => {
val index = line.lastIndexOf("/")
val teacher = line.substring(index + 1)
val httpHost = line.substring(0, index)
val subject = new URL(httpHost).getHost.split("[.]")(0)
((subject, teacher), 1)
})
//计算有多少学科
val subjects: Array[String] = sbjectTeacherAndOne.map(_._1._1).distinct().collect()
//自定义一个分区器,并且按照指定的分区器进行分区
val sbPatitioner = new SubjectParitioner2(subjects)
//聚合,聚合是就按照指定的分区器进行分区
//该RDD一个分区内仅有一个学科的数据
val reduced: RDD[((String, String), Int)] = sbjectTeacherAndOne.reduceByKey(sbPatitioner, _+_)
//如果一次拿出一个分区(可以操作一个分区中的数据了)
val sorted: RDD[((String, String), Int)] = reduced.mapPartitions(it => {
//将迭代器转换成list,然后排序,在转换成迭代器返回
it.toList.sortBy(_._2).reverse.take(topN).iterator
//即排序,有不全部加载到内存
//长度为5的一个可以排序的集合
})
//收集结果
//val r: Array[((String, String), Int)] = sorted.collect()
//println(r.toBuffer)
sorted.saveAsTextFile("/Users/zx/Desktop/out")
sc.stop()
}
}
//自定义分区器
class SubjectParitioner2(sbs: Array[String]) extends Partitioner {
//相当于主构造器(new的时候回执行一次)
//用于存放规则的一个map
val rules = new mutable.HashMap[String, Int]()
var i = 0
for(sb <- sbs) {
//rules(sb) = i
rules.put(sb, i)
i += 1
}
//返回分区的数量(下一个RDD有多少分区)
override def numPartitions: Int = sbs.length
//根据传入的key计算分区标号
//key是一个元组(String, String)
override def getPartition(key: Any): Int = {
//获取学科名称
val subject = key.asInstanceOf[(String, String)]._1
//根据规则计算分区编号
rules(subject)
}
}