Spark常用算子总结

Spark常用算子总结

算子分类

大方向分类

1)Transformation 变换/转换算子:这种变换并不触发提交作业,完成作业中间过程处理。

  Transformation 操作是延迟计算的,也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行,需要等到有 Action 操作的时候才会真正触发运算。

2)Action 行动算子 :这类算子会触发 SparkContext 提交 Job 作业。

Action 算子会触发 Spark 提交作业(Job),并将数据输出 Spark系统。

小方向分类

1)* Value数据类型的Transformation算子*,这种变换并不触发提交作业,针对处理的数据项是Value型的数据。

2)* Key-Value数据类型的Transfromation算子*,这种变换并不触发提交作业,针对处理的数据项是Key-Value型的数据对。

3)* Action算子*,这类算子会触发SparkContext提交Job作业。

准备

//构建spark环境
val conf = new SparkConf().setMaster("local").setAppName("Spark Exercise: Spark Version Word Count Program");
val sc = new SparkContext(conf);

//val distFile1 = sc.textFile("data.txt") //本地当前目录下文件
//val distFile2 = sc.textFile("hdfs://192.168.1.100:9000") //HDFS文件
//val distFile3 = sc.textFile("001.txt","002.txt") //读取多个文件
//val distFile4 = sc.textFile("/input/data.txt") //本地指定目录下文件
//注意:textFile可以读取多个文件,或一个文件夹,也支持压缩文件、包含通配符的路径
//textFile("/input") 读取目录
//textFile("/input/*.txt") 含通配符的路径
//textFile("/input/*.gz") 读取压缩文件

//创建一个数据集合格式的rdd 第二个参数是创建并行集合的一个重要参数,他指定了将数据集切为几分;一般来说spark会尝试
//根据集群的状况,来自动设定slices的数目,当然也可以手动设置他
val rdd1 = sc.parallelize( 1 to 9 , 4 )

Transformation算子

1.map

map是对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD;RDD之间的元素是一对一的关系

示例:

val rdd1 = sc.parallelize( 1 to 9 , 4 )
val rdd2 = rdd1.map(x => x\ *2 ) //结果是 (2,4,6,8,10,12,14,16,18)

2. filter

filter是对rdd元素进行过滤;返回一个新的数据集,是经过func函数返回值为true的原元素组成

示例:

val rdd3 = rdd2.filter(x => x> 10 ) //结果是12 14 16 18

3.flatMap

类似于map,但每一个输入元素,都会被映射为0到多个输出元素(因此,func函数的返回值是一个Seq,而不是单一元素),rdd之间是一对多的关系

示例:

val rdd4 = rdd3.flatMap(x => x+ 1 to 20 ) //结果是 13,14,15,16,17,18,19,20,15,16,17,18,19,20,17,18,19,20,19,20

注:网上很多基础的词频的计算,一般都是先通过flatMap截取空格啥的,将一行转成多个单词

4.mapPartitions

是map的一个变种。只不过映射函数的参数由RDD中的每一个元素变成了RDD中每一个分区的迭代器。如果在映射的过程中需要频繁创建额外的对象,使用mapPartitions要比map高效的过。

比如,将RDD中的所有数据通过JDBC连接写入数据库,如果使用map函数,可能要为每一个元素都创建一个connection,这样开销很大,如果使用mapPartitions,那么只需要针对每一个分区建立一个connection。参数preservesPartitioning表示是否保留父RDD的partitioner分区信息。

一个小技巧的 : https://blog.csdn.net/lsshlsw/article/details/48627737

示例:

//rdd_1 将rdd1的每个分区做累加
val rdd_1 = rdd1.mapPartitions{x=>{
val result = ListInt
var i = **0
** while(x.hasNext){
i += x.next()
}
result.::(i).iterator
}}
println(rdd_1.collect().mkString(",")) //action 算子 触发执行上面的操作 结果是:3,7,11,24

5.mapPartitionsWithIndex

与mapPartitions的功能类似,只是多传入 split index

示例:

//rdd_2将rdd1中每个分区的数字累加,并在每个分区的累加结果前面加了分区索引
val rdd_2 = rdd1.mapPartitionsWithIndex{
//x 是分区索引 iter是该分区的集合
(x,iter) => {
var result = ListString
var i = **0
** while(iter.hasNext){
i += iter.next()
}
result.::(x + "|" + i).iterator
}
}

/println(rdd_2.collect().mkString(",")) //action 算子 触发执行上面的操作

6.sample

sample(withReplacement,fraction,seed) 是根据给定的随机种子seed,随机抽样出数量为frac的数据。withReplacement:是否放回抽样(若是设置为true则是放回,相当于假设取10%的数据,每次取一个取完放回去然后再取,相当于会取到重复的值;设置为false则是不放回去);fraction:比例,0.1表示10%

示例:

val a = sc.parallelize( 1 to 10000 , 3 )
val a_new = a.sample( false , 0.1 , 0 )

7.union

union 数据合并 返回一个新的数据集 由原数据集合otherDataset联合而成

val a_7 = sc.parallelize( 1 to 10 , 3 )
val a_7_1 = sc.parallelize( 5 to 15 , 3 )
//println(a_7.union(a_7_1).collect().mkString(",")) //(1,2,3,4,5,6,7,8,9,10,5,6,7,8,9,10,11,12,13,14,15)

8.intersection

是数据交集,返回一个新的数据集,包含两个数据集的交集

val a_8 = a_7.intersection(a_7_1)
//println(a_8.collect().mkString(",")) //(6,9,7,10,8,5) 顺序不对 是因为在不同分区

9.distinct

distinct([nunmTasks]) 是数据去重 numTasks参数是设置任务并行数量

val a_9 = a_7.union(a_7_1).distinct()
//println(a_9.collect().mkString(",")) //(6,12,13,1,7,14,8,2,15,3,9,4,10,11,5)

10.groupByKey

是数据分组操作,在一个有(k,v)对组成的数据集上调用,返回一个(k,Seq[V])对的数据集

val rdd0 = sc.parallelize(Array(( 1 , 1 ),( 1 , 2 ),( 1 , 7 ),( 1 , 3 ),( 2 , 1 ),( 3 , 1 ),( 3 , 2 )))
val rdd11 = rdd0.groupByKey()
//println("————————-"+rdd11.collect().mkString(","))

结果:(1,CompactBuffer(1, 2, 7, 3)),(3,CompactBuffer(1, 2)),(2,CompactBuffer(1))

11.reduceByKey

reduceByKey(func,[numTasks])是数据分组聚合操作,在一个(k,v)对的数据集上使用,返回一个(k,v) 对的数据集,key相同的值都被使用指定的reduce聚合在一起(使用一个相关的函数来合并每个key的value的值的一个算子)

val rdd12 = rdd0.reduceByKey((pre,after) => pre+after) //pre和after表示同一个key的 前一个和后一个value
//println(rdd12.collect().mkString(",")) //(1,9),(3,4),(2,1)

12.aggregate

aggregate(zeroValue:U)(seqOp:(U,T)=>U,combOp:(U,U)=>U)和reduceByKey的不同在于,reduceByKey输入输出都是(k,v)而aggregateByKey输出的是(K,U),可以不同于输入(k,v),aggregateByKey的三个参数

zeroValue:U 初始值,比如空列表{}

seqOp:(U,T)=>U seq操作符,描述如何将T合并入U,比如如何将item合并到列表

combOp:(U,U) => comb操作符 描述如果合并两个U,比如合并两个列表

所以aggregate可以看成是更高抽象,更灵活的reduce或group

可以理解是 seqOp 是每个分区聚合一次 ,然后combOp再将分区聚合完了的再聚合一次,如果就一个分区,combOp就不起作用了

参考文档:https://blog.csdn.net/bitcarmanlee/article/details/78088304

示例:

//找出大于f和小于f的数量

val raw = List("a", "b", "d", "f", "g", "h", "o", "q", "x", "y")
val aaaa = sc.parallelize(raw, 3 ).aggregate(( 0 , 0 ))(
//cc 是初始值 此处为一个元组(0,0) str是raw的值
(cc, str) => {
var biggerf = cc._1
var lessf = cc._2
if (str.compareTo("f") >= 0 ) biggerf = cc._1 + **1
** else if(str.compareTo("f") <0) lessf = cc._2 +**1
** (biggerf, lessf)
},
//x 是初始值 y是操作之后的值
(x, y) => {
//println(x + "———–" + y)
(x._1, x._2)
}
)
//println(aaaa)

//求平均值、最大值、最小值,count
val bbbbArray = 3 to 10 ;
val bbbb = sc.parallelize(bbbbArray, 2 ).aggregate(( 0 ,bbbbArray( 0 )))((a,str:Int)=>{
(math.max(a._1,str),math.min(a._2,str))
},(x,y)=>{
//println(x + "—————–" + y)
y
})
// println(bbbb) //(10,3) 最大值为 10 最小值 是3

13.combineByKey

是对rdd中的数据集按照key进行聚合操作,聚合的逻辑是通过自定义的函数提供给combineByKey combineByKeyC:RDD[(K,C)]把(K,C)类型的RDD转换为(K,V)类型的rdd

详细参考: https://blog.csdn.net/t1dmzks/article/details/70249743

createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素, combineByKey() 会使用一个叫作 createCombiner() 的函数来创建那个键对应的累加器的初始值

mergeValue: 如果这是一个在处理当前分区之前已经遇到的键, 它会使用 mergeValue() 方法将该键的累加器对应的当前值与这个新的值进行合并

mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。

示例:

/demo 计算平均成绩
val scores = List(
("xiaoming", "Math", 98 ),
("xiaoming", "English", 88 ),
("wangwu", "Math", 75 ),
("wangwu", "English", 78 ),
("lihua", "Math", 90 ),
("lihua", "English", 80 ),
("zhangsan", "Math", 91 ),
("zhangsan", "English", 80 ))
val newScores = for (i<-scores) yield {
(i._1,(i._2,i._3))
}
val rdd13 = sc.parallelize(newScores, 2 )
type scoreTyp = (Int,Int)
var rdd13_1 = rdd13.combineByKey(
(x:(String,Int)) => {
(x._2, 1 )
}, //createCombiner 统计分数和计数器
(acc1: scoreTyp,x:(String,Int)) => (acc1._1 + x._2,acc1._2+ 1 ), //统计重复的key 重复的key 分数相加 计数器+1
(acc1: scoreTyp,acc2:scoreTyp)=>(acc1._1+acc2._1,acc1._2+acc2._2) //统计各个分区的结果
)//.map({case(k,v) => (k,v._1/v._2)})
//rdd13_1.collect().foreach(println)

14.sortByKey

排序操作 对(K,V)类型的数据进行按照K进行排序(key相同时,没有对value进行排序),其中K需要实现Ordered方法

val rdd14 = rdd0.sortByKey()

15.join

是连接操作,将输入数据集(K,V)和另一个数据集(K,W)进行 join得到(K,(V,W))该操作是对相同k的vw进行笛卡尔积操作,也就是v和w的所有集合

val rdd15 = sc.parallelize(Array(( 1 , 1 ),( 1 , 2 ),( 1 , 3 ),( 2 , 1 ),( 2 , 2 )))
val rdd15_1 = rdd15.join(rdd15)

16 cogroup

是将输入的数据集(K,V)和另一个数据集(K,W)进行操作,得到格式(K,Seq[v],Seq[W])的数据集

val rdd16 = rdd15.cogroup(rdd15)
//println("—————-"+rdd16.collect().mkString(","))

17 cartesian

对于数据集T和U进行笛卡尔积操作得到(T,U)的数据集

val rdd17 = rdd1.cartesian(rdd3)
//println(rdd17.collect().mkString(","))

Action算子

1.reduce

是对数据集的所有元素执行狙击函数,该函数必须是可交换的

val rdd01 = sc.parallelize( 1 to 9 , 3 )
val rdd02 = rdd01.reduce(_+_)
//println("—————-"+rdd02) 结果是45

2.collect

是将数据集中所有的元素以一个array的形式返回

3.count

返回数据集中元素的个数

4.first

返回数据集中的第一个元素,类似于take

5.take

take(n) 返回一个包含数据集中钱n个元素的数组,当前该操作不能并行

6.takeSample

takeSample(withReplacement,num,[seed])返回包含随机的num个元素的数组,和Sample不同,takeSample是行动操作所以返回的是数组而不是RDD 第一个参数withReplacement 是抽样时是否放回,第二个参数num会精确指定抽样数,而不是比例

7.takeOrdered

takeOrdered(n,[Ordering])是返回包含随机的n个元素的数组,按照顺序输出

8.saveAsTextFile

数据集中的元素写到一个文本文件,Spark会对每个元素调用toString方法来把每个元素存成文本文件的一行

9.countByKey

对于(K,V)类型的RDD 返回一个(K,Int)的map ,Int为K的个数

10.foreach(func)

是对数据集中的每个元素都执行func函数

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值