2023_Spark_实验十二:Spark高级算子使用

2023-09-20 16:30:26
掌握Spark高级算子在代码中的使用
相同点分析
三个函数的共同点,都是Transformation算子。惰性的算子。
不同点分析
map函数是一条数据一条数据的处理,也就是,map的输入参数中要包含一条数据以及其他你需要传的参数。
mapPartitions函数是一个partition数据一起处理,也即是说,mapPartitions函数的输入是一个partition的所有数据构成的“迭代器”,然后函数里面可以一条一条的处理,在把所有结果,按迭代器输出。也可以结合yield使用效果更优。
rdd的mapPartitions是map的一个变种,它们都可进行分区的并行处理。
两者的主要区别是调用的粒度不一样:map的输入变换函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区。
mapPartitionsWithIndex函数,其实和mapPartitions函数区别不大,因为mapPartitions背后调的就是mapPartitionsWithIndex函数,只是一个参数被close了。mapPartitionsWithIndex的函数可以或得partition索引号;
假设一个rdd有10个元素,分成3个分区。如果使用map方法,map中的输入函数会被调用10次;而使用mapPartitions方法的话,其输入函数会只会被调用3次,每个分区调用1次。
mapPartitionsWithIndex则是带上分区下标进行操作。
1、mapPartitionWithIndex
 

import org.apache.spark.{SparkConf,SparkContext}
object mapPartitionWithIndex {

def getPartInfo:(Int,Iterator[Int]) => Iterator[String] = (index:Int,iter:Iterator[Int]) =>{
iter.map(x =>"[ PartId " + index +", elems: " + x + " ]")
}

def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local").setAppName("RddMapPartitionsWithIndexDemo")
val sc = new SparkContext(conf)

val rdd1 = sc.parallelize(List(1,2,3,4,5,9,6,7,8),numSlices =3 )
val rdd2 = rdd1.mapPartitionsWithIndex(getPartInfo)
rdd2.collect().foreach(println)

}

}

2、aggregate
首先我们来创建一个 RDD

scala> val rdd1 = sc.parallelize(1 to 5)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at parallelize at <console>:24


scala> rdd1.collect
warning: there was one feature warning; re-run with -feature for details
res24: Array[Int] = Array(1, 2, 3, 4, 5)

这个 RDD 仅有 1 个分片,包含 5 个数据: 1, 2, 3, 4, 5 。
 
然后我们来应用一下 aggregate 方法。
 
在使用 aggregate 之前,我们还是先定义两个要给 aggregate 当作输入参数的函数吧。
scala> 
// Entering paste mode (ctrl-D to finish)
def pfun1(p1: Int, p2: Int): Int = {
p1 * p2
}

// Exiting paste mode, now interpreting.
pfun1: (p1: Int, p2: Int)Int

scala>

 
scala> 
// Entering paste mode (ctrl-D to finish)
def pfun2(p3: Int, p4: Int): Int = {
p3 + p4
}

// Exiting paste mode, now interpreting.
pfun2: (p3: Int, p4: Int)Int

scala>
接着是第 2 个函数。就不再解释什么了。
 
然后终于可以开始应用我们的 aggregate 方法了。
scala> rdd1.aggregate(3)(pfun1, pfun2)
res25: Int = 363

scala>
输出结果是 363 !这个结果是怎么算出来的呢?
 
首先我们的 zeroValue 即初值是 3 。然后通过上面小节的介绍,我们知道首先会应用 pfun1 函数,因为我们这个 RDD 只有 1 个分片,所以整个运算过程只会有一次 pfun1 函数调用。它的计算过程如下:
 
首先用初值 3 作为 pfun1 的参数 p1 ,然后再用 RDD 中的第 1 个值,即 1 作为 pfun1 的参数 p2 。由此我们可以得到第一个计算值为 3 * 1 = 3 。接着这个结果 3 被当成 p1 参数传入,RDD 中的第 2 个值即 2 被当成 p2 传入,由此得到第二个计算结果为 3 * 2 = 6 。以此类推,整个 pfun1 函数执行完成以后,得到的结果是  3 * 1 * 2 * 3 * 4 * 5 = 360 。这个 pfun1 的应用过程有点像是 “在 RDD 中滑动计算” 。
在 aggregate 方法的第 1 个参数函数 pfun1 执行完毕以后,我们得到了结果值 360 。于是,这个时候就要开始执行第 2 个参数函数 pfun2 了。
 
pfun2 的执行过程与 pfun1 是差不多的,同样会将 zeroValue 作为第一次运算的参数传入,在这里即是将 zeroValue 即 3 当成 p3 参数传入,然后是将 pfun1 的结果 360 当成 p4 参数传入,由此得到计算结果为 363 。因为 pfun1 仅有一个结果值,所以整个 aggregate 过程就计算完毕了,最终的结果值就是 363 

import org.apache.spark.{SparkConf, SparkContext}

object RddAggregateDemo {

def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local").setAppName("RddDemos")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(List("12","23","345",""),numSlices = 2)
//下面代码等价与rdd1.aggregate("")(x+y) => math.min(x.length,y.length)),(x,y)=>x+y))
val result = rdd1.aggregate("")(func1,func2)
println(result)
}
//(x,y)=>math.min(x.Length,y.length)
def func1:(String,String) => String =(x:String,y:String) =>{
println("<x: " + x + ",x.len: " + x.length + ">,<y: " + y +",y.len: " + y.length + ">")
val ret =math.min(x.length,y.length).toString
println("func1 ret:" +ret)
ret
}
//(x+y) => x+y
def func2:(String,String)=>String=(x:String,y:String) =>{
println("========" +(x+y))
x+y
}
}

3、aggregateByKey
通过scala集合以并行化方式创建一个RDD
scala> val pairRdd = sc.parallelize(List((“cat”,2),(“cat”,5),(“mouse”,4),(“cat”,12),(“dog”,12),(“mouse”,2)),2)
pairRdd 这个RDD有两个区,一个区中存放的是:
(“cat”,2),(“cat”,5),(“mouse”,4)
另一个分区中存放的是:
(“cat”,12),(“dog”,12),(“mouse”,2)
然后,执行下面的语句
scala > pairRdd.aggregateByKey(100)(math.max(_ , _), _ + _ ).collect
结果:
res0: Array[(String,Int)] = Array((dog,100),(cat,200),(mouse,200)
下面是以上语句执行的原理详解:
aggregateByKey的意思是:按照key进行聚合
第一步:将每个分区内key相同数据放到一起
分区一
(“cat”,(2,5)),(“mouse”,4)
分区二
(“cat”,12),(“dog”,12),(“mouse”,2)
第二步:局部求最大值
对每个分区应用传入的第一个函数,math.max(_ , _),这个函数的功能是求每个分区中每个key的最大值
这个时候要特别注意,aggregateByKe(100)(math.max(_ , _),_+_)里面的那个100,其实是个初始值
在分区一中求最大值的时候,100会被加到每个key的值中,这个时候每个分区就会变成下面的样子
分区一
(“cat”,(2,5,100)),(“mouse”,(4,100))
然后求最大值后变成:
(“cat”,100), (“mouse”,100)
分区二
(“cat”,(12,100)),(“dog”,(12.100)),(“mouse”,(2,100))
求最大值后变成:
(“cat”,100),(“dog”,100),(“mouse”,100)
第三步:整体聚合
将上一步的结果进一步的合成,这个时候100不会再参与进来
最后结果就是:
(dog,100),(cat,200),(mouse,200)
对PairRDD中相同的Key值进行聚合操作,在聚合过程中同样使用了一个中立的初始值。和aggregate函数类似,aggregateByKey返回值的类型不需要和RDD中value的类型一致。因为aggregateByKey是对相同Key中的值进行聚合操作,所以aggregateByKey'函数最终返回的类型还是PairRDD,对应的结果是Key和聚合后的值,而aggregate函数直接返回的是非RDD的结果。


import org.apache.spark.{SparkConf, SparkContext}

object RddAggregateByKeyDemo {
def main(args: Array[String]): Unit = {
val conf =new SparkConf().setMaster("local").setAppName("RddAggregateByKeyDemo")
val sc = new SparkContext(conf)
val pairRDD = sc.parallelize(List(("cat",2),("cat",5),("mouse",4),("cat",12),("dog",12),("mouse",2)),numSlices = 2)
val rdd = pairRDD.aggregateByKey(100)(func2,func2)
val resultArray = rdd.collect
resultArray.foreach(println)
sc.stop()

}
def func2(index:Int,iter:Iterator[(String,Int)]):Iterator[String] = {
iter.map(x=>"[partID:" + index + ",val: " +x +"]")
}

//(x,y)=>math.max(x+y)
def func1:(Int,Int) => Int =(x:Int,y:Int) =>{
println("<x: " + x + "," +",y:"+ y + ">")
val ret =math.max(x,y)
println("func1 max:" +ret)
ret
}
//(x+y) => x+y
def func2:(Int,Int)=>Int=(x:Int,y:Int) =>{
println("========func2 x :" + x + ",y:"+y)
println("========func2 ret =====" +(x+y))
x+y
}

}

更多推荐

保护你的iOS应用,防止逆向破解

​转载:怎么保护苹果手机移动应用程序iosipa文件中的代码?目录转载:怎么保护苹果手机移动应用程序iosipa文件中的代码?代码混淆步骤1.选择要混淆保护的ipa文件2.选择要混淆的类名称3.选择要混淆保护的函数,方法4.配置签名证书5.混淆和测试运行​编辑在当今移动应用市场竞争激烈的环境中,代码保护功能对于iOS应

ETLCloud工具让美团数据管理更简单

美团为第三方开发者和商家提供了一系列开放的API接口和工具,使其可以与美团的业务进行对接和集成,从而获得更多的业务机会和增长空间。通过美团开放平台,第三方开发者和商家可以实现以下功能:开放接口:美团开放平台提供了各种API接口,包括商品信息、订单管理、支付结算、配送物流等,方便第三方应用和系统与美团进行数据交互和业务对

Android codec2 编码 -- 基于录屏

文章目录前言android原生的应用srcreenrecordMediaCodec获取编码数据流程前言本篇文章主要是理解Android12编码的流程,首先从上层的应用出发理解mediacodec提供给外部API的用法。然后针对每个api分析编码各个流程中框架中的流程。熟悉一个框架的流程可以从简单到复杂、从整体到局部去展

极光笔记 | 大语言模型插件

在人工智能领域,大语言模型(LLMs)是根据预训练数据集进行”学习“,获取可以拟合结果的参数,虽然随着参数的增加,模型的功能也会随之增强。但无论专业领域的小模型,还是当下最火、效果最好的大模型,都有一个共同的劣势:无法准确/正确地回答出训练数据集以外(区别于验证集和测试集的新增数据,如实时新闻、未公开的企业信息等)的结

快速学习Netty

Netty框架探索:助力高效网络编程一、Netty是个啥?二、“HelloWorld”服务器端实现(Server)客户端实现(Client)思考🤔三、Netty的核心组件EventLoopChannelChannelPipelineChannelHandlerByteBufFuture&Promise四、源码探究Ne

MySQL: 锁

一、tableCREATETABLE`dog`(`id`int(11)NOTNULLAUTO_INCREMENT,`age`int(11)DEFAULTNULL,`weight`int(11)DEFAULTNULL,PRIMARYKEY(`id`),KEY`idx_age`(`age`))ENGINE=InnoDBAU

集卡拖车运输最新政策调整来了_箱讯科技

国庆佳节马上就要到了,我们即将迎来一个重要的假期。然而,对于许多进出口企业来说,国庆节并不仅仅意味着放松和庆祝,还需要提前做好出运准备。准时出运对于维护客户信任和业务运营至关重要。伴随着国庆节的临近,运输需求日益增长,运力十分紧张!外贸朋友们,是不是会有以下这样的糟心经历?“一到节假日出货高峰,找拖车找到崩溃.....

Oracle数据库体系结构(二)_物理结构

目录1概述2物理结构2.1数据文件2.2控制文件2.4归档日志文件2.5参数文件2.6警报文件2.7跟踪文件2.8备份文件3总结1概述存储结构是Oracle数据库的体系结构之一,是Oracle管理的基础。Oracle存储结构可分为物理结构和逻辑结构。一个数据库的物理结构可包括数据文件、联机日志文件和控制文件及其他的一些

【Docker】深入解析企业中 Docker 仓库的用法和作用

深入解析企业中Docker仓库的用法和作用Docker仓库是企业中管理和分发镜像的关键工具。本文将详细介绍Docker仓库的概念、常见用法以及在企业中的作用,并结合Java代码演示如何使用Docker仓库。1.Docker仓库概述Docker仓库是存储和分享Docker镜像的中心化存储库。它允许开发者将自己的镜像上传到

鸿蒙应用开发(基础篇)之列表组件

一、简介在我们常用的手机应用中,经常会见到一些数据列表,如设置页面、通讯录、商品列表等。下图中两个页面都包含列表,“首页”页面中包含两个网格布局,“商城”页面中包含一个商品列表。上图中的列表中都包含一系列相同宽度的列表项,连续、多行呈现同类数据,例如图片和文本。常见的列表有线性列表(List列表)和网格布局(Grid列

Linux READ_ONCE/WRITE_ONCE宏

文章目录前言一、简介1.1READ_ONCE1.2WRITE_ONCE1.3volatile关键字二、Compilerbarrier2.1barrier2.2READ_ONCE/WRITE_ONCE三、总结参考资料前言最近在看arm64架构内存页表源码部分,发现在遍历页表项的时候经常出现READ_ONCE宏和WRITE

热文推荐