每日福利来一个,话不多说,我们直接开始今天的spark RDD的实战练习代码,我们学习编程,还是要多实战,在实战中我们才能领悟到平时很难理解的知识点,下面我们开始我们今天的训练吧!
没有看前面的同学可以回顾一下:
4.Spark Rdd常用算子和RDD必备知识
3.spark core 核心知识
2.spark 之 wordcount入门
1.spark 入门讲解
接下来就是我们今天的重点RDD常用算子的练习:
/**
* 主要学习SparkTransformation的操作算子
* 1、map:将集合中每个元素乘以7
* 2、filter:过滤出集合中的奇数
* 3、flatMap:将行拆分为单词
* 4、sample:根据给定的随机种子seed,随机抽样出数量为frac的数据
* 5、union:返回一个新的数据集,由原数据集和参数联合而成
* 6、groupByKey:对数组进行 group by key操作 慎用
* 7、reduceByKey:统计每个班级的人数
* 8、join:打印关联的组合信息
* 9、sortByKey:将学生身高进行排序
* 10、combineByKey
* 11、aggregateByKey
*/
object _01SparkTransformationOps1 {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.project-spark").setLevel(Level.WARN)
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(s"${_01SparkTransformationOps1.getClass.getSimpleName}")
val sc = new SparkContext(conf)
val list = List(1, 2, 3, 4, 5, 6, 7)
// transformationMap_01(list, sc)
// transformationFlatMap_02(sc)
// transformationFilter_03(list, sc)
// transformationSample_04(sc)
// transformationUnion_05(sc)
// transformationGBK_06(sc)
// transformationRBK_07(sc)
// transformationJOIN_08(sc)
transformationUnion_05(sc)
sc.stop()
}
/**
* sortByKey:将学生成绩进行排序
* 分区内部有序
* 学生表stu:id name chinese-score
*
*/
def transformationsbk_09(sc:SparkContext): Unit = {
val stuList = List(
"1 王浩玉 93.5",
"2 贾小红 56.5",
"3 薛亚曦 60.5",
"4 宋其 125"
)
val stuRDD = sc.parallelize(stuList)
println("------------------sortByKey----------------------")
val score2InfoRDD:RDD[(Double, String)] = stuRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2).toDouble, line)
}}
score2InfoRDD.sortByKey(false, numPartitions = 1).foreach(println)
println("------------------sortBy----------------------")
val sbkRDD2:RDD[String] = stuRDD.sortBy(line => line, false, 1)(
new Ordering[String](){//按照成绩排序
override def compare(x: String, y: String) = {
val xScore = x.split("\\s+")(2).toDouble
val yScore = y.split("\\s+")(2).toDouble
yScore.compareTo(xScore)
}
},
ClassTag.Object.asInstanceOf[ClassTag[String]]
)
sbkRDD2.foreach(println)
}
/**
* join: 打印关联的组合信息
* 关联操作中rdd的类型必须(K, V)
* inner join:等值连接 返回左右两张表中能对应上的数据
* left/right outer join:左(右)外连接--->
* 返回左(右)表所有数据,右(左)表能对应上的话显示,对应不上的话显示为null
* left/right full outer join
* 返回左右两张表中都有的数据:左外连接+右外连接
* 一张学生信息表stu,一张班级信息表class
* stu---> stu_id stu_name cid
* class--> cid cname
* 现在要求查询以下信息:
* stu_id stu_name cname
* 用SQL:
* select
* s.stu_id,
* s.stu_name,
* c.cname
* from stu s
* left join class c on s.cid = c.cid
* @param sc
*/
def transformationJOIN_08(sc:SparkContext): Unit = {
val stu = List(
"1 郑祥楷 1",
"2 王佳豪 1",
"3 刘鹰 2",
"4 宋志华 3",
"5 刘帆 4",
"6 OLDLi 5"
)
val cls = List(
"1 1807bd-bj",
"2 1807bd-sz",
"3 1807bd-wh",
"4 1807bd-xa",
"7 1805bd-bj"
)
val stuRDD = sc.parallelize(stu)
val clsRDD = sc.parallelize(cls)
val cid2STURDD:RDD[(String, String)] = stuRDD.map{case line => {
val fields = line.split("\\s+")
(fields(2), line)
}}
val cid2ClassRDD:RDD[(String, String)] = clsRDD.map{case line => {
val fields = line.split("\\s+")
(fields(0), fields(1))
}}
//两张表关联--join
println("---------inner join-------------")
val cid2InfoRDD:RDD[(String, (String, String))] = cid2STURDD.join(cid2ClassRDD)
cid2InfoRDD.foreach(println)
println("---------left join-------------")
val cid2LefJoinRDD:RDD[(String, (String, Option[String]))] = cid2STURDD.leftOuterJoin(cid2ClassRDD)
cid2LefJoinRDD.foreach(println)
println("---------full join-------------")
val cid2FullJoinRDD:RDD[(String, (Option[String], Option[String]))] = cid2STURDD.fullOuterJoin(cid2ClassRDD)
cid2FullJoinRDD.foreach(println)
}
/**
*
* 一张表,student表
* stu_id stu_name class_id
* 统计每个班级的人数
* 相同的统计下,reduceByKey要比groupByKey效率高,因为在map操作完毕之后发到reducer之前
* 需要先进行一次本地的预聚合,每一个mapper(对应的partition)执行一次预聚合
* @param sc
*/
def transformationRBK_07(sc:SparkContext): Unit = {
val list = List(
"1 郑祥楷 1807bd-bj",
"2 王佳豪 1807bd-bj",
"3 刘鹰 1807bd-sz",
"4 宋志华 1807bd-wh",
"5 刘帆 1807bd-xa",
"6 何昱 1807bd-xa"
)
val listRDD = sc.parallelize(list)
println("分区个数:" + listRDD.getNumPartitions)
val cid2Count = listRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2), 1)
}}
cid2Count.reduceByKey(_+_).foreach(println)
}
/**
* groupByKey:对数组进行 group by key操作 慎用
* 一张表,student表
* stu_id stu_name class_id
* 将学生按照班级进行分组,把每一个班级的学生整合到一起
* 建议groupBykey在实践开发过程中,能不用就不用,主要是因为groupByKey的效率低,
* 因为有大量的数据在网络中传输,而且还没有进行本地的预处理
* 我可以使用reduceByKey或者aggregateByKey或者combineByKey去代替这个groupByKey
*/
def transformationGBK_06(sc:SparkContext): Unit = {
val list = List(
"1 郑祥楷 1807bd-bj",
"2 王佳豪 1807bd-bj",
"3 刘鹰 1807bd-sz",
"4 宋志华 1807bd-wh",
"5 刘帆 1807bd-xa",
"6 何昱 1807bd-xa"
)
val listRDD = sc.parallelize(list)
println("分区个数:" + listRDD.getNumPartitions)
val cid2StuInfo = listRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2), line)
}}
val gbkRDD = cid2StuInfo.groupByKey()
gbkRDD.foreach{case (cid, stus) => {
println(s"$cid ---> ${stus} ----> ${stus.size}")
}}
}
/**
* union:返回一个新的数据集,由原数据集和参数联合而成
* 该union操作和sql中的union all操作一模一样
*/
def transformationUnion_05(sc:SparkContext): Unit = {
val list1 = List(1, 2, 3, 4, 5)
val list2 = List(6, 5, 4, 7, 9)
val lRDD1 = sc.parallelize(list1)
val lRDD2 = sc.parallelize(list2)
lRDD1.union(lRDD2).foreach(t => print(t + " "))
}
/**
* sample:根据给定的随机种子seed,随机抽样出数量为frac的数据
* 参数:
* withReplacement:true或者false
* true:代表有放回的抽样
* false:代表无放回的抽样
* fraction:抽取样本空间占总体的比例(分数的形式传入)
* without replacement: 0 <= fraction <= 1
* with replacement: fraction >= 0
* seed:随机数生成器
* new Random().nextInt(10)
* 注意:我们使用sample算子不能保证提供集合大小就恰巧是rdd.size * fraction,
* 结果大小会在前面数字上下浮动
* sample算子,在我们后面学习spark调优(dataskew)的时候,可以用的到
*/
def transformationSample_04(sc:SparkContext): Unit = {
val list = 0 to 99999
val listRDD = sc.parallelize(list)
val sampleRDD = listRDD.sample(false, 0.05)
println(s"sampleRDD的集合大小:${sampleRDD.count()}")
}
/**
* filter:过滤出集合中的奇数(even)
*/
def transformationFilter_03(list:List[Int], sc:SparkContext): Unit = {
val listRDD = sc.parallelize(list )
val filterRDD = listRDD.filter(num => {
// 1 / 0
num % 2 == 0
})
filterRDD.foreach(println)
}
/**
* 3、flatMap:将行拆分为单词
* 和map算子类似,只不过呢,rdd形态转化对应为one-to-many
* @param sc
*/
def transformationFlatMap_02(sc:SparkContext): Unit = {
val list = List(
"lu jia hui",
"chen zhi xiang"
)
val listRDD = sc.parallelize(list)
listRDD.flatMap(line => line.split("\\s+")).foreach(println)
}
/**
* 1、map:将集合中每个元素乘以7
* map是最常用的转换算子之一,将原rdd的形态,转化为另外一种形态,
* 需要注意的是这种转换是one-to-one
* @param sc
*/
def transformationMap_01(list:List[Int], sc:SparkContext): Unit = {
val bs = 7
val listRDD = sc.parallelize(list)
val retRDD = listRDD.map(num => num * bs)
retRDD.foreach(println)
}
}
/*
1、reduce:
执行reduce操作,返回值是一个标量
2、collect: 慎用
将数据从集群中的worker上拉取到dirver中,所以在使用的过程中药慎用,意外拉取数据过大造成driver内存溢出OOM(OutOfMemory)
NPE(NullPointerException)
所以在使用的使用,尽量使用take,或者进行filter再拉取
3、count:返回当前rdd中有多少条记录
select count(*) from tbl;
4、take:take(n)
获取rdd中前n条记录
如果rdd数据有序,可以通过take(n)求TopN
5、first:take(1)
获取rdd中的第一条记录
6、saveAsTextFile:
将rdd中的数据保存到文件系统中
7、countByKey:和reduceByKey效果相同,但reduceByKey是一个Transformation
统计相同可以出现的次数,返回值为Map[String, Long]
8、foreach:略 遍历
*/
object _04SparkActionOps extends App {
// Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
// Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
// Logger.getLogger("org.project-spark").setLevel(Level.WARN)
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(s"${_03SparkTransformationOps.getClass.getSimpleName}")
val sc = new SparkContext(conf)
val list = List(
"5 刘帆 1807bd-xa",
"2 王佳豪 1807bd-bj",
"8 邢宏 1807bd-xa",
"3 刘鹰 1807bd-sz",
"4 宋志华 1807bd-wh",
"1 郑祥楷 1807bd-bj",
"7 张雨 1807bd-bj",
"6 何昱 1807bd-xa"
)
val listRDD: RDD[String] = sc.parallelize(list)
val list1 = 1 to 9
val listRDD1 = sc.parallelize(list1)
//reduce:统计rdd中的和
val 和 = listRDD1.reduce((v1, v2) => v1 + v2)
println(和)
//2 count
println(s"listRDD1的条数:${listRDD1.count()}")
// 3 take 获取rdd中的n条记录
val take: Array[Int] = listRDD1.take(3)
println(take.mkString("[", ",", "]"))
private val first: Int = listRDD1.first()
println(first)
val top3:Array[Int] = listRDD1.takeOrdered(3)(new Ordering[Int]{
override def compare(x: Int, y: Int) = {
y.compareTo(x)
}
})
println(top3.mkString("[", ",", "]"))
println("---------------saveAsTextFile---------------------")
// listRDD.saveAsTextFile("E:/data/spark/out")
listRDD.saveAsObjectFile("E:/data/spark/out1")
println("---------------saveAsHadoop/SequenceFile---------------------")
val cid2InfoRDD:RDD[(String, String)] = listRDD.map{case line => {
val fields = line.split("\\s+")
(fields(2), line)
}}
/**
* saveAsHadoopFile() ---> org.apache.hadoop.mapred.OutputFormat 借口
* saveAsNewAPIHadoopFile() --> org.apache.hadoop.mapreduce.OutputFormat 抽象类
* job.setOutputFormat(xxx.classs)
*
*
* path: String --->将rdd数据存储的目的地址
keyClass: Class[_], mr中输出记录包含key和value,需要指定对应的class
valueClass: Class[_],
job.setOutputKeyClass()
job.setOutputValueClass()
outputFormatClass: Class[_ <: NewOutputFormat[_, _]] 指定对应的Format来完成数据格式化输出
TextOutputFormat
*/
cid2InfoRDD.saveAsNewAPIHadoopFile(
"E:/data/spark/out2",
classOf[Text],
classOf[Text],
classOf[TextOutputFormat[Text, Text]]
)
//countByKey作用就是记录每一个key出现的次数,作用同reduceByKey(_+_)
private val cid2Count: collection.Map[String, Long] = cid2InfoRDD.countByKey()
for((k, v) <- cid2Count) {
println(k + "---" + v)
}
sc.stop()
}
combineByKey 与aggregateByKey类似,都调用了combineByKeyWithClassTag,在aggregateByKey中的
第一个参数是zero value,此函数的第一个参数需要提供一个初始化函数,通过第一个函数完成分区内计算,通过第二个函数完成分区间计算:
createCombiner:V=>C turns V to C ,C is an element list;
mergeValue:(C,V)=>C merge V to C by appending V to the end of the list.
mergeCombiners:(C,C)=>C to combine two Cs into one.
def combineByKeyC:RDD[(K,C)]
def combineByKey[C](createCombiner:V=>C,mergeValue:(C,V)=>C,mergeCombiners:(C,C)=>C,partitioner:Partitioner,mapSideCombine:Boolean = true,serializer:Serializer = null):RDD[(K,C)]
练习:
/**
* 使用combineByKey和aggregateByKey来模拟groupByKey和reduceByKey
* 不管combineByKey还是aggregateByKey底层都是使用combineByKeyWithClassTag来实现的
*
* 这两个有啥区别?
*
*/
object _02SparkTransformationOps {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.project-spark").setLevel(Level.WARN)
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(s"${_02SparkTransformationOps.getClass.getSimpleName}")
val sc = new SparkContext(conf)
val list = List(
"5 刘帆 1807bd-xa",
"2 王佳豪 1807bd-bj",
"8 邢宏 1807bd-xa",
"3 刘鹰 1807bd-sz",
"4 宋志华 1807bd-wh",
"1 郑祥楷 1807bd-bj",
"7 张雨 1807bd-bj",
"6 何昱 1807bd-xa"
)
// cbk2rbk(sc, list)
cbk2gbk(sc, list)
sc.stop()
}
def cbk2gbk(sc:SparkContext, list:List[String]): Unit = {
val listRDD = sc.parallelize(list)
println("分区个数:" + listRDD.getNumPartitions)
val cid2Info:RDD[(String, String)] = listRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2), line)
}}
val cbk2gbk:RDD[(String, ArrayBuffer[String])] = cid2Info.combineByKey(
line => ArrayBuffer[String](line),
(ab:ArrayBuffer[String], line:String) => {
ab.append(line)
ab
},
(ab1:ArrayBuffer[String], ab2:ArrayBuffer[String]) => {
ab1.appendAll(ab2)
ab1
}
)
cbk2gbk.foreach(println)
}
/**
* 使用combineByKey来模拟reduceByKey
* @param sc
* @param list
*/
def cbk2rbk(sc:SparkContext, list:List[String]): Unit = {
val listRDD = sc.parallelize(list)
println("分区个数:" + listRDD.getNumPartitions)
val cid2Info:RDD[(String, String)] = listRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2), line)
}}
cid2Info.foreachPartition(patition => {
patition.foreach(println)
})
val cid2Counts = cid2Info.combineByKey(createCombiner, mergeValue, mergeCombiners)
cid2Counts.foreach(println)
}
/**
* V是被聚合的rdd中k-v键值对中的value的类型
* C是经过聚合操作之后又v转化成的类型
* 当前方法,rdd中一个key,在一个分区中,只会创建/调用一次,做数据类型初始化
* 比如:hello这个key,在partition0和partition1中都有出现
* 在partition0中聚合的时候createCombiner之被调用一次
*
* @return
*/
def createCombiner(line:String):Int = {
val fields = line.split("\\s+")
println("-----createCombiner---> " + fields(2))
1
}
/**
* scala代码,求1+。。。+10
* var sum = 0
* for(i <- 1 to 10) {
* sum = sum + i
* }
* println(sum)
* mergeValue就类似于上面的代码
* 合并同一个分区中,相同key对应的value数据,在一个分区中,相同的key会被调用多次
* @return
*/
def mergeValue(sum:Int, line:String):Int = {
val fields = line.split("\\s+")
println(">>>-----mergeValue---> " + fields(2))
sum + 1
}
/**
* 相同key对应分区间的数据进行合并
* @return
*/
def mergeCombiners(sum1:Int, sum2:Int):Int = {
println(">>>-----mergeCombiners--->>> sum1: " + sum1 + "--->sum2: " + sum2)
sum1 + sum2
}
}
aggregateByKey 和reduceByKey类似,但更具灵活性,可以自定义在分区内和分区间的聚合操作,有三种调用方式:
def aggregateByKeyU:ClassTag(seqOp:(U,V)=>U,comb:(U,U)=>U):RDD[K,U]
seqOp:(U,V)=>U 和zeroValue 完成分区内计算,分区间计算通过comb:(U,U)=>U完成。
def aggregateByKeyU:ClassTag(seqOp:(U,V)=>U,comb:(U,U)=>U):RDD[K,U]
def aggregateByKeyU:ClassTag(seqOp:(U,V)=>U,comb:(U,U)=>U):RDD[K,U]
练习:
/**
* 使用combineByKey和aggregateByKey来模拟groupByKey和reduceByKey
* 不管combineByKey还是aggregateByKey底层都是使用combineByKeyWithClassTag来实现的
*
* 这两个有啥区别?
*
* 1、本质上combineByKey和aggregateByKey都是通过combineByKeyWithClassTag来实现的,只不过实现的细节或者方式不大一样。
* 2、combineByKey更适合做聚合前后数据类型不一样的操作,aggregateByKey更适合做聚合前后数据类型一致的操作
* 因为我们可以在combineByKey提供的第一个函数中完成比较复杂的初始化操作,而aggregateByKey的第一个参数是一个值
* 3、我们使用时最简单的版本,而在实际生产过程中,一般都是相对比较复杂的版本,还有其它参数的,比如partitioner,
* mapSideCombine。
* partitioner制定并行度,
* mapSideCombine控制是否执行本地预聚合
*
*/
object _03SparkTransformationOps {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.project-spark").setLevel(Level.WARN)
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(s"${_03SparkTransformationOps.getClass.getSimpleName}")
val sc = new SparkContext(conf)
val list = List(
"5 刘帆 1807bd-xa",
"2 王佳豪 1807bd-bj",
"8 邢宏 1807bd-xa",
"3 刘鹰 1807bd-sz",
"4 宋志华 1807bd-wh",
"1 郑祥楷 1807bd-bj",
"7 张雨 1807bd-bj",
"6 何昱 1807bd-xa"
)
// abk2rbk(sc, list)
abk2gbk(sc, list)
sc.stop()
}
def abk2gbk(sc:SparkContext, list:List[String]): Unit = {
val listRDD = sc.parallelize(list)
println("分区个数:" + listRDD.getNumPartitions)
val cid2Info:RDD[(String, String)] = listRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2), line)
}}
val abk2gbk:RDD[(String, ArrayBuffer[String])] = cid2Info.aggregateByKey(ArrayBuffer[String]())(
(ab:ArrayBuffer[String], line:String) => {
ab.append(line)
ab
},
(ab1:ArrayBuffer[String], ab2:ArrayBuffer[String]) => {
ab1.appendAll(ab2)
ab1
}
)
// abk2gbk.foreach(println)
}
/**
* 使用aggregateByKey来模拟reduceByKey
* @param sc
* @param list
*/
def abk2rbk(sc:SparkContext, list:List[String]): Unit = {
val listRDD = sc.parallelize(list)
println("分区个数:" + listRDD.getNumPartitions)
val cid2Info:RDD[(String, String)] = listRDD.map { case (line) => {
val fields = line.split("\\s+")
(fields(2), line)
}}
val cid2rbkRDD:RDD[(String, Int)] = cid2Info.aggregateByKey(0)(
(sum:Int, line:String) => {
sum + 1
},
(sum1:Int, sum2:Int) => {
sum1 + sum2
}
)
cid2rbkRDD.foreach(println)
}
}
练习 1:
//通过并行化生成 rdd
val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
//对 rdd1 里的每一个元素乘 2 然后排序
val rdd2 = rdd1.map(_ * 2).sortBy(x => x, true)
//过滤出大于等于十的元素
val rdd3 = rdd2.filter(_ >= 10)
//将元素以数组的方式在客户端显示
rdd3.collect
练习 2:
val rdd1 = sc.parallelize(Array("a b c", "d e f", "h i j"))
//将 rdd1 里面的每一个元素先切分在压平
val rdd2 = rdd1.flatMap(_.split(' '))
rdd2.collect
练习 3:
val rdd1 = sc.parallelize(List(5, 6, 4, 3))
val rdd2 = sc.parallelize(List(1, 2, 3, 4))
//求并集 val rdd3 = rdd1.union(rdd2)
//求交集 val rdd4 = rdd1.intersection(rdd2)
//去重 rdd3.distinct.collect rdd4.collect
练习 4:
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
//求 jion
val rdd3 = rdd1.join(rdd2) rdd3.collect
//求并集
val rdd4 = rdd1 union rdd2
//按 key 进行分组
rdd4.groupByKey rdd4.collect
练习 5:
val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2))) //cogroup
val rdd3 = rdd1.cogroup(rdd2)
//注意 cogroup 与 groupByKey 的区别
rdd3.collect
练习 6:
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))
//reduce 聚合
val rdd2 = rdd1.reduce(_ + _) rdd2.collect
练习 7:
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2), ("shuke", 1)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))
val rdd3 = rdd1.union(rdd2)
//按 key 进行聚合
val rdd4 = rdd3.reduceByKey(_ + _) rdd4.collect
//按 value 的降序排序 val rdd5 = rdd4.map(t => (t._2, t._1)).sortByKey(false).map(t => (t._2, t._1)) rdd5.collect
想要了解更多,访问下面的地址 http://homepage.cs.latrobe.edu.au/zhe/ZhenHeSparkRDDAPIExamples.html
数据的sql文件 和txt文件,和习题答案练习代码我都已经为你们准备好了在我的github上:
https://github.com/chenjian-520/sparkStudent
下面是课后练习:答案我已经放到github上面,有兴趣的朋友可以看看:
(数据文件我也放到了github上)
数据样式:
1 Java高级 北苑 信息安全,大数据,架构 闲徕互娱 20k-40k 本科 经验5-10年 游戏 不需要融资
字段:
id job addr tag company salary edu exp type level
中文解释
id 工作岗位 地址 标签 公司 薪资 学历 经验 类型 融资级别
需求:
1、求出每个不同地点的招聘数量,输出需求量最大的10个地方,以及岗位需求量
#2、求出使用学历和经验作为联合分组条件,那种搭配的需求是最多的
3、求出大数据类工作(job字段包含"大数据",“hadoop”,"spark"就算大数据类工作)岗位对学历的要求,不同学历的需求量和占比
#4、求出不同融资级别的公司,在招聘大数据类工作岗位时,在学历和经验一致的条件下,薪资会高出多少。
本科 1-3年 A轮-B轮 2000-3000
本科 1-3年 B轮-C轮 2100-4500
.......
本科 3-5年 A轮-B轮 3000-6000
本科 3-5年 B轮-C轮 4000-5000
.....
研究生 1-3年 A轮-B轮 4000-5000
......
(4000-5000) ------ (平均最少薪资-平均最大薪资)
5、求出不同标签的公司数量和招聘数量(只输出招聘需求最大的50个标签)
公司数量和招聘数量,由高到低排序,如果公司数量相等,按照招聘数量的降序排列
结果样式:
高级,5232,1414
金融,4865,995
资深,3717,1080
Java,3531,1154
大数据,3375,831
........
下次我们还要来学习哟!!!
记得点赞加关注!!! 不迷路 !!!
人活着真累:上车得排队,爱你又受罪,吃饭没香味,喝酒容易醉,挣钱得交税!