1、常用算子
① aggregate算子
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
/**
* 测试aggregate算子
* action操作,
* 第一个参数是初始值,
* 第二个参数:是2个函数[每个函数都是2个参数
* (第一个参数:先对个个分区进行的操作, 第二个:对个个分区合并后的结果再进行合并), 输出一个参数]
*/
object TestAggregate {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestAggregate").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("warn")
val rdd1: RDD[Int] = sc.parallelize(List(1,2,3,4,5),2)
val i: Int = rdd1.aggregate(0)(_+_,_+_)
println(i)
}
}
② AggregateByKey算子
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 将key相同的,先局部操作[在分区内进行操作],然后再整体操作【整体到一块进行操作】,第一个值为默认值
*/
object TestAggregateByKey {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestAggregateByKey").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.setLogLevel("warn")
val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
val rdd: RDD[(String, Int)] = pairRDD.aggregateByKey(0)(math.max(_,_),_+_)
rdd.foreach(println)
}
}
③ CombineByKey算子
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 和reduceByKey是相同的效果,是reduceByKey的底层。
* 第一个参数x:原封不动取出来, 第二个参数:是函数, 局部运算, 第三个:是函数, 对局部运算后的结果再做运算
* 每个分区中每个key中value中的第一个值,
*/
object TestCombineByKey {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestAggregateByKey").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.setLogLevel("warn")
val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
val rdd1: RDD[(String, Int)] = pairRDD.combineByKey(x => x, (a:Int, b:Int) => a+b, (m:Int, n:Int) => m+n)
rdd1.foreach(println)
//----------------------
val rdd4 = sc.parallelize(List("dog","cat","gnu","salmon","rabbit","turkey","wolf","bear","bee"), 3)
val rdd5 = sc.parallelize(List(1,1,2,2,2,1,2,2,2), 3)
// 相当于是做拉链[两个集合之间形成一个对偶的集合,合并成一个rdd]
val rdd6 = rdd5.zip(rdd4)
rdd6.foreach(println)
rdd6.combineByKey(List(_),(x:List[String],y:String) => x:+y,(m:List[String],n:List[String]) => m++n).foreach(println)
}
}
④ CollectAsMap算子
/**
* 测试CollectAsMap算子
* Map(b -> 2, a -> 1)//将Array的元祖转换成Map,以后可以通过key取值
* 它会将结果换算成Map返回,但是只返回相同key中的一个(所以在使用之前一定要先进行聚合,否则这个算子还是不要使用)
*/
object TestCollectAsMap{
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestAggregateByKey").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.setLogLevel("warn")
val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
val stringToInt: collection.Map[String, Int] = pairRDD.collectAsMap()
println(stringToInt)
}
}
⑤ mapPartitionsWithIndex算子
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
/**
* 测试mapPartitionsWithIndex算子
*/
object TestMapPartitionsWithIndex {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("Test2").setMaster("local[2]")
val sc: SparkContext = new SparkContext(conf)
val rdd1: RDD[Int] = sc.parallelize(List(1,2,3,4,5),2)
sc.setLogLevel("warn")
// 这里面的分区都是随意分的,并没有说因为按照key相应的规则来分区
val rdd2: RDD[String] = rdd1.mapPartitionsWithIndex(
(index: Int, iter: Iterator[(Int)]) => {
iter.toList.map(x => "[partID:" + index + ", val: " + x + "]").iterator
}
)
rdd2.foreachPartition(
x => {
x.foreach(println)
}
)
//------------
val rdd: RDD[(String, String)] = sc.parallelize(List(("a","1"),("b","2"),("a","2"),("a","3")))
val rdd3: RDD[String] = rdd.mapPartitionsWithIndex(
(index: Int, ter: Iterator[(String, String)]) => {
ter.toList.map(x => "[partID:" + index + ", val: " + x + "]").iterator
}
)
rdd3.foreach(println)
}
}
⑥ reduce算子
import org.apache.spark.{SparkConf, SparkContext}
/**
* 测试reduce算子
*/
object TestReduce{
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local[*]")
conf.setAppName("TestCount")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("warn")
val rdd1 = sc.parallelize(List(("a", 1), ("b", 2), ("b", 2), ("c", 2), ("c", 1)))
val rdd = sc.parallelize(List(1, 2, 2, 2, 1))
val sum = rdd.reduce((a,b) => a+b)
println(sum)
}
}
⑦ reduceBykey算子
import org.apache.spark.{SparkConf, SparkContext}
/**
* 测试reduceByKey的算子
*/
object TestReduceByKey {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestAggregateByKey").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.setLogLevel("warn")
val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
pairRDD.reduceByKey(_+_)
}
}
⑧ countBykey和countByValue算子
import org.apache.spark.{SparkConf, SparkContext}
/**
* 测试countByKey和countByValue
*/
object TestCount {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local[*]")
conf.setAppName("TestCount")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("warn")
val rdd1 = sc.parallelize(List(("a", 1), ("b", 2), ("b", 2), ("c", 2), ("c", 1)))
rdd1.countByKey()
//这个是按照key进行计算总共有多少个
val tupleToLong: collection.Map[(String, Int), Long] = rdd1.countByValue()//这个是讲("a",1)作为一个元素,统计其出现的次数
println(tupleToLong)
//计算结果为:Map((c,2) -> 1, (a,1) -> 1, (b,2) -> 2, (c,1) -> 1)
}
}
⑨FlatMapValues算子
import org.apache.spark.{SparkConf, SparkContext}
/**
* 测试FlatMapValues
*/
object TestFlatMapValues {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local[*]")
conf.setAppName("TestCount")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("warn")
// s
}
}
⑩ 累加器(非自定义的)
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 测试累加器(非自定义的)
*/
object TestAccumulation {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestBrodCast").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("warn")
val rdd: RDD[Int] = sc.parallelize(List(1,2,3,4,5))
// 这种算法是错误的,因为我们的代码是会到每个task中去计算,而我们的sum是在driver端,
// 所以说,这个时候,我们就要使用累加器去完成相应的操作了
var sum = 0;
val rdd1: RDD[Unit] = rdd.map(
sum += _
)
rdd1.collect()
println(sum)
//------------------------------
val accum = sc.longAccumulator("accu")
val rdd2: RDD[Unit] = rdd.map(
accum.add(_)
)
// 如果不加collect这种行动算子的话,累加器并不会执行,那么就会造成错误发生,所以这个时候,必须要让他执行,否则就会是0
// 那么问题来了,如果我们使用两个行动算子,reduce或者是count类的,那么累加器的值应该是多少呢?
// 应该是30,原因是因为我们每触发一次行动算子,spark的rdd算子就会从头开始执行一次,既然知道是这种情况,我们可以将rdd2进行缓存,这样的
// 话,我们的rdd2只会计算一次
// 这也告诉我们:在实际开发过程中,我们需要明白行动算子与转换算子,在使用转换算子,如果涉及到数据量比较大的话,尽量先进行缓存,可以减轻spark的压力
rdd2.collect()
rdd2.collect()
println(accum.value)
}
}
⑩ 累加器(自定义)
import org.apache.spark.util.AccumulatorV2
import scala.collection.mutable
/**
* 自定义累加器
* @param myMap
*/
class MyAccumulation(var myMap:mutable.HashMap[String,Long]) extends AccumulatorV2[String,mutable.HashMap[String,Long]]{
override def isZero: Boolean = {
myMap.isEmpty
}
override def copy(): AccumulatorV2[String,mutable.HashMap[String,Long]] = {
val accumulation = new MyAccumulation(myMap)
accumulation.myMap ++= this.myMap
accumulation
}
override def reset(): Unit = {
myMap.clear()
}
override def add(key:String): Unit = {
if (!myMap.get(key).isEmpty){
myMap(key) = myMap.getOrElse(key,0L) + 1L
} else {
myMap += (key -> 1L)
}
}
override def merge(other: AccumulatorV2[String, mutable.HashMap[String, Long]]): Unit = {
val otherMap: mutable.HashMap[String, Long] = other.value
otherMap.foldLeft(otherMap){case (otherMap,(key,count)) =>
otherMap(key) = otherMap.getOrElse(key,0L) + count
otherMap
}
}
override def value: mutable.HashMap[String,Long] = {
myMap
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable
/**
* 测试自定义累加器
* 自定义累加器在2.0版本之后,才得到了较大的改进,官方已经废弃了以前的方式,使用AccumulatorV2来提供更加友好的方式
* 官方也给出了一个实现的示例:CollectionAccumulator类,这个类允许以集合的形式手机spark应用过程中的一些信息,
* 但是,由于累加器的值最终要汇聚到driver端,为了避免driver端的oom问题,需要对收集的信息规模加以控制,不宜过大
*/
object TestMyAccumulation {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("TestBrodCast").setMaster("local[*]")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("warn")
val rdd: RDD[String] = sc.parallelize(List("1","1","a","c","d"))
val map: mutable.HashMap[String, Long] = new mutable.HashMap[String,Long]()
val accumulation: MyAccumulation = new MyAccumulation(map)
sc.register(accumulation)
rdd.foreach(println)
val rdd1: RDD[Unit] = rdd.map(
accumulation.add(_)
)
rdd1.collect()
println(accumulation.value)
}
}
1.11、mapPartions和map需要注意的事项
如果说分区过多,task数量过多的话,我们直接使用mapartions要比使用map的情况糟糕的多,平常使用map算子,时间是5秒左右计算完,但在同样的情况下,使用mappartions在20秒才运算完。所以说使用mappartions也要注意情况。
1.12、分区器与序号的实现
package com.hsq.test
import org.apache.spark.rdd.RDD
import org.apache.spark.{HashPartitioner, RangePartitioner, SparkConf, SparkContext}
object TestMyPartitioner {
def main(args: Array[String]) {
val conf=new SparkConf()
.setMaster("local[*]")
.setAppName("TestMyParttioner")
val sc=new SparkContext(conf)
sc.setLogLevel("warn")
val pairRDD: RDD[(String, Int)] = sc.parallelize(List(("a",1), ("a",2), ("a",3), ("b",4), ("b",5), ("c",6),("c",7), ("c",8),("c",9), ("c",10)),2)
// 计算序号
val rdd: RDD[((String, Int), Long)] = pairRDD.zipWithIndex()
//反转
val rdd1: RDD[(Long, (String, Int))] = rdd.zipWithIndex().map(x =>
(x._2, x._1._1)
)
// 分区器主要有三种,range分区器,hash分区器以及我们自定义分区器,hash分区器是根据我们的key的hashcode的值来划分的,而我们
// range分区器主要根据key的hashcode来确定范围,会尽量均匀的分配
val value: RDD[(Long, (String, Int))] = rdd1.partitionBy(new RangePartitioner(4,rdd1))
// val value: RDD[(String, Int)] = pairRDD.partitionBy(new RangePartitioner(5,pairRDD))
// val value: RDD[(String, Int)] = pairRDD.partitionBy(new HashPartitioner(5))
val finalRDD: RDD[(String, Int)] = value.map(
x =>
x._2
)
val value1: RDD[(String, List[(String, Int)])] = finalRDD.mapPartitionsWithIndex(
(partIdx, iter) => {
var part_map = scala.collection.mutable.Map[String, List[(String, Int)]]()
while (iter.hasNext) {
var part_name = "part_" + partIdx;
var elem = iter.next()
if (part_map.contains(part_name)) {
var elems = part_map(part_name)
elems ::= elem
part_map(part_name) = elems
} else {
part_map(part_name) = List[(String, Int)] {
elem
}
}
}
part_map.iterator
}
)
value1.foreach(println(_))
sc.stop()
}
}
2、spark-sql
2.1、spark-sql基本开篇使用方式
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* spark-sql基本使用。一定要记得在sparkSession创建之后,立刻引入`import session.implicits._`
*/
object Test1 {
def main(args: Array[String]): Unit = {
val session: SparkSession = SparkSession.builder().appName("Test1").master("local[*]").getOrCreate()
import session.implicits._
session.sparkContext.setLogLevel("warn")
//读取本地文件,创建df
val df: DataFrame = session.read.json("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.json")
// df的Schema的格式
df.printSchema()
df.show()
// 仅选择名字这列
df.select("name").show()
// 选择每个属性,并且将年龄+1
df.select($"name",$"age" + 1).show()
// 选择年龄21一岁的人
df.filter($"age" > 21).show()
// 相同的年龄的个数
df.groupBy("age").count().show()
}
}
2.2、spark-sql创建临时视图
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* 测试spark-sql,创建临时视图并用sql进行查询
*/
object Test3 {
def main(args: Array[String]): Unit = {
val session: SparkSession = SparkSession.builder().master("local[*]").appName("Test3").getOrCreate()
import session.implicits._
val df: DataFrame = session.read.json("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.json")
df.createTempView("people")
val frame: DataFrame = session.sql("select * from people")
frame.show()
}
}
2.3、创建全局视图
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* 创建全局视图,全局视图是跨会话的,他会保存在系统绑定的数据库global_temp,直到spark任务
* 结束,他才会结束
*/
object Test4 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
import spark.implicits._
val df: DataFrame = spark.read.json("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.json")
df.createGlobalTempView("people")
spark.sql("select * from global_temp.people").show()
spark.newSession().sql("select * from global_temp.people").show()
}
}
2.4、两种转换数据集的方式
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
/**
* spark-sql支持两种不同的方法将现有RDD转换为数据集,
*/
object Test6 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
val peopleDF: DataFrame = spark.sparkContext.textFile("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.txt")
.map(_.split(","))
.map(x => Person(x(0), x(1).trim.toInt))
.toDF()
peopleDF.createTempView("people")
val teenagerDF = spark.sql("SELECT name, age FROM people WHERE age BETWEEN 13 AND 19")
// 两种方式都可以取出其中一个值
teenagerDF.map(x => "Name:"+x(0)).show()
teenagerDF.map(x => "Name:"+x.getAs[String]("name")).show()
println("第三种方式")
// 下面的还有一点问题,后面再调,出不来结果
// 没有数据集预定义的编码器,所以这里我们通过隐式的方式进行定义
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String,Any]]
//原始类型和case类也可以定义为
//隐式val stringIntMapEncoder:Encoder [Map [String,Any]] = ExpressionEncoder()
// row.getValuesMap [T]检索多个列立刻变成了Map [String,T]
teenagerDF.map(teenager => teenager.getValuesMap[Any](List("name","age"))).collect()
}
}
2.5、无法提前定义案例类的情况
package com.nuc.spark.spark_sql
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
import org.apache.spark.sql.types._
/**
* 如果无法提前定义案例类(例如,记录的结构以字符串形式编码,或者文本数据集将被解析,而字段将针对不同的用户进行不同的投影),DataFrame则可以通过三个步骤以编程方式创建a 。
*
* Row从原始RDD 创建s的RDD;
* 创建由StructType匹配Row步骤1中创建的RDD中的s 结构 表示的模式。
* Row通过createDataFrame提供的方法将模式应用于s 的RDD SparkSession。
*/
object Test7 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
val peopleRDD: RDD[String] = spark.sparkContext.textFile("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.txt")
// val schemaString = "name age"
// val fields: Array[StructField] = schemaString.split(" ")
// .map(filedName => StructField(filedName, StringType, nullable = true))
// 模拟 字符串
val schemaString = "name age"
// 编码 根据schema
val fields = schemaString.split(" ")
.map(fieldName => StructField(fieldName,StringType,nullable = true))
val schema = StructType(fields)
val rowRDD: RDD[Row] = peopleRDD.map(_.split(","))
.map(att => Row(att(0), att(1).trim))
// 将rowRDD与schema存入df中,这样才能保证df是按照相应的格式存储的
val peopleDF = spark.createDataFrame(rowRDD,schema)
peopleDF.createTempView("people")
val resultsDF: DataFrame = spark.sql("select * from people")
resultsDF.map(att => "name:"+att(0)).show()
}
}
2.6、spark-sql的聚合函数
package com.nuc.spark.spark_sql
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types._
/**
* spark-sql的聚合功能。
* 当然,我们用户也可以创建自己的聚合函数
* 这个是spark-sql自己的预定义的聚合函数,大致使用方式如下
*/
object Test8 extends UserDefinedAggregateFunction{
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
spark.udf.register("Test8",Test8)
val df: DataFrame = spark.read.json("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/employee.json")
df.createTempView("employees")
df.show()
val resultDF: DataFrame = spark.sql("select Test8(salary) as average_salary from employees")
resultDF.show()
}
// 数据类型的这个集合函数的输入参数
override def inputSchema: StructType = StructType(StructField("inputColumn",LongType)::Nil)
// 聚合缓冲区中的值的数据类型
override def bufferSchema: StructType = {
StructType(StructField("sum",LongType)::StructField("count",LongType)::Nil)
}
// 返回值的数据类型
override def dataType: DataType = DoubleType
// 此函数是否始终在相同的输入
override def deterministic: Boolean = true
// 初始化给定的聚合缓冲区,缓冲区本身是一个'Row',
// 除了标准方法之外,比如在索引处检索值(例如,get(),getBoolean()),提供了更新其值的机会。请注意,缓冲区内的数组和映射仍然是
// 不可变的。
override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer(0) = 0L
buffer(1) = 0L
}
// 更新(其实就是,如果是求和,就是相加,如果是平均,也是相加,主要看我们自己的功能)
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
if (!input.isNullAt(0)){
buffer(0) = buffer.getLong(0) + input.getLong(0)
buffer(1) = buffer.getLong(1) + 1
}
}
// 合并
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1(0) = buffer1.getLong(0) + buffer2.getLong(0)
buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
}
// 计算最终结果
override def evaluate(buffer: Row): Double = buffer.getLong(0).toDouble / buffer.getLong(1)
}
2.7、用户自定义函数
package com.nuc.spark.spark_sql
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
/**
* 这个是属于用户自定义的聚合函数,
* 如果是一个json格式的数据读进来之后,我们可以将他转换成一个实体类之后,然后再使用自定义聚合函数进行处理
* 这样我们可以通过非RDD的方式进行处理,对性能的提升也能起到一定的作用。
*/
object Test9 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
val df: DataFrame = spark.read.json("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/employee.json")
val ds: Dataset[Employee] = df.as[Employee]
ds.show()
// +-------+------+
// | name|salary|
// +-------+------+
// |Michael| 3000|
// | Andy| 4500|
// | Justin| 3500|
// | Berta| 4000|
// +-------+------+
// Convert the function to a `TypedColumn` and give it a name
val averageSalary = MyAverage.toColumn.name("average_salary")
val result = ds.select(averageSalary)
result.show()
// +--------------+
// |average_salary|
// +--------------+
// | 3750.0|
// +--------------+
}
}
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{Encoder, Encoders, SparkSession}
import org.apache.spark.sql.expressions.Aggregator
case class Employee(name: String, salary: Long)
case class Average(var sum: Long, var count: Long)
object MyAverage extends Aggregator[Employee, Average, Double] {
// A zero value for this aggregation. Should satisfy the property that any b + zero = b
def zero: Average = Average(0L, 0L)
// Combine two values to produce a new value. For performance, the function may modify `buffer`
// and return it instead of constructing a new object
def reduce(buffer: Average, employee: Employee): Average = {
buffer.sum += employee.salary
buffer.count += 1
buffer
}
// intermediate:中间值(合并两个中间值)
def merge(b1: Average, b2: Average): Average = {
b1.sum += b2.sum
b1.count += b2.count
b1
}
// 转换(这里面可以求平均值,也可以求和)
def finish(reduction: Average): Double = reduction.sum.toDouble / reduction.count
// 指定中间值类型的编码器
def bufferEncoder: Encoder[Average] = Encoders.product
// 指定最终输出值类型的编码器
def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}
数据源
2.8、默认数据源的操作
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* 默认数据源,如果parquet另有配置(spark.sql.sources.default),否则将用于所有操作
*/
object Test10 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
val userDF: DataFrame = spark.read.load("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/users.parquet")
userDF.select("name","favorite_color").write.save("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/namesAndFavColors.parquet")
}
}
2.9、指定数据源
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* 我们也可以手动的去指定将要使用的数据源以及要传递给数据源的任何其他选项,数据源通过其全名指定
* 即(org.apache.spark.sql.parquet),但对于内置的数据源的话,我们可以使用自己的短名称来完成,
* 比如说:(json,parquet,jdbc,orc,libsvm,csv,text),从任何数据源类型加载的df都可以使用此语法转换为其他类型
*/
object Test11 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
val peopleDF: DataFrame = spark.read.format("json").load("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.json")
peopleDF.select("name","age").write.format("parquet").save("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/namesAndAges.parquet")
}
}
2.10、加载csv文件
package com.nuc.spark.spark_sql
import org.apache.spark.sql.{DataFrame, SparkSession}
/**
* 直接加载我们的csv的文件
*/
object Test12 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
val peopleDFCsv: DataFrame = spark.read.format("csv")
.option("sep", "")
.option("inferSchema", "true")
.option("header", "true")
.load("/Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/people.csv")
}
}
2.11、直接在文件上运行sql
package com.nuc.spark.spark_sql
import org.apache.spark.sql.SparkSession
/**
* 直接在文件上运行sql
*/
object Test13 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("test4").master("local[*]").getOrCreate()
// 对于从RDD到df的隐式转换
import spark.implicits._
spark.sql("SELECT * FROM parquet./Users/yrx/workspace/idea/hsq-spark/spark-exec/src/main/resources/users.parquet`")
}
}