spark 常用算子大全(分类详细,图片解析)

spark 系列

spark 常用算子大全



前言

本篇博客将为大家带来 spark 常用算子的介绍。本节建议大家在有 scala 集合函数大全 的基础上去学习,会更加的轻松!


RDD 算子

RDD 算子分为 lazynon-lazy 两种

  • Transformation(lazy):也称转换操作、转换算子
  • Actions(non-lazy):立即执行,也称动作操作、动作算子

RDD 转换算子

对于转换操作,RDD 的所有转换都不会直接计算结果

  • 仅记录作用于 RDD 上的操作
  • 当遇到动作算子(Action)时才会进行真正计算

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

Value类型

map(func)

作用:返回一个新的 RDD ,该 RDD 由每一个输入元素经过 func 函数转换后组成

详解

map 将原来 RDD 的每个数据项通过 map 中的用户自定义函数 f 映射转变为一个新的元素。源码中的map 算子相当于初始化一个 RDD,新RDD叫做 MappedRDD(this,sc.clean(f))。

如下图所示,每个方框表示一个 RDD 分区,左侧的分区经过用户自定义函数 f:T->U 映射为右侧的新 RDD 分区。但是,实际只有等到 Action算子触发后,这个 f 函数才会和其他函数在一个 stage 中对数据进行运算。第一个分区的数据记录 V1 输入 f,通过 f 转换输出为转换后的分区中的数据记录 V’1。

在这里插入图片描述
举例

scala> sc.makeRDD(1 to 10).map(_*2).collect
res1: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

mapPartitions(func)

作用:类似于 map ,但独立地在 RDD 的每一个分片上运行,因此在类型为 T 的 RDD 上运行时,func 的函数类型必须是Iterator[T] => Iterator[U]假设有 N 个元素,有 M 个分区,那么 map 的函数的将被调用 N 次,而 mapPartitions 被调用 M 次,一个函数一次处理所有分区。

详解

mapPartitions 函数获取到每个分区的迭代器,在函数中通过这个分区整体的迭代器对整个分区的元素 进行操作。内部实现是生成 MapPartitionsRDD。

如下图所示,方框代表一个 RDD 分区。用户通过函数 f(iter)=>iter.filter(_>=3) 对分区中所有数据进行过滤,大于和等于 3 的数据保留。一个方块代表一个 RDD 分区,含有 1、 2、 3 的分区过滤只剩下元素 3。
在这里插入图片描述
举例

#表示划分为2个分区
scala> val rdd = sc.parallelize(1 to 6, 2)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[5] at parallelize at <console>:24

scala> rdd.partitions.size
res4: Int = 2

# items表示一个分区中的数据
scala> rdd.mapPartitions(items => items.filter(_>=3)).collect
res7: Array[Int] = Array(3, 4, 5, 6)

map()和mapPartition()的区别

  1. map():每次处理一条数据

  2. mapRartition(): 每次处理一个分区的数据,这个分区的数据处理完之后,原 RDD 中分区的数据才能释放,可能导致OOM。

  3. 开发指导:当内存空间较大的时候建议使用 mapPartition() ,以提高处理效率。

mapPartitionsWithIndex(func)

作用:类似于 mapPartitions ,但 func 带有一个整数参数表示分片的索引值,因此在类型为 T 的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U]

举例

scala> val rdd = sc.parallelize(1 to 6, 2)rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24

scala> rdd.mapPartitionsWithIndex((index,items) => Iterator(index + ":" + items.toList)).collect
res0: Array[String] = Array(0:List(1, 2, 3), 1:List(4, 5, 6))

flatMap(func)

作用:类似于 map ,但是每一个输入元素可以被映射为0或多个输出元素(== 所以 func 应该返回一个序列,而不是单一元素 ==)

详解

将原来 RDD 中的每个元素通过函数 f 转换为新的元素,并将生成的 RDD 的每个集合中的元素合并为一个集合,内部创建 FlatMappedRDD(this,sc.clean(f))。

如下图所示,表示 RDD 的一个分区,进行 flatMap 函数操作,flatMap 中传入的函数为f:T->U,T 和 U 可以是任意的数据类型。将分区中的数据通过用户自定义函数 f 转换为新的数据。外部大方框可以认为是一个 RDD 分区,小方框代表一个集合。 V1、 V2、 V3 在一个集合作为 RDD 的一个数据项,可能存储为数组或其他容器,转换为V’1、 V’2、 V’3 后,将原来的数组或容器结合拆散,拆散的数据形成为 RDD 中的数据项。

在这里插入图片描述

举例

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

scala> rdd.flatMap(1 to _).collect
res1: Array[Int] = Array(1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6)

glom

作用:将每一个分区形成一个数组,形成新的 RDD 类型是RDD[Array[T]]

详解

glom 函数将每个分区形成一个数组,内部实现是返回的 GlommedRDD。 下图中的每个方框代表一个RDD分区。 该图表示含有V1、 V2、 V3的分区通过函数 glom 形成一数组Array[(V1),(V2),(V3)]

在这里插入图片描述
举例

scala> val rdd = sc.parallelize(1 to 16,4)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[65] at parallelize at <console>:24
scala> rdd.glom().collect()
res25: Array[Array[Int]] = Array(Array(1, 2, 3, 4), Array(5, 6, 7, 8), Array(9, 10, 11, 12), Array(13, 14, 15, 16))

groupBy(func)

作用:分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器

详解

将元素通过函数生成相应的 Key,数据就转化为 Key-Value 格式,之后将 Key 相同的元素分为一组。

函数实现如下:
1)将用户函数预处理:val cleanF = sc.clean(f)
2)对数据 map 进行函数操作,最后再进行 groupByKey 分组操作:this.map(t => (cleanF(t), t)).groupByKey(p),其中, p 确定了分区个数和分区函数,也就决定了并行化的程度。

下图中方框代表一个 RDD 分区,相同key 的元素合并到一个组。例如 V1 和 V2 合并为 V, Value 为 V1,V2。形成 V,Seq(V1,V2)。
在这里插入图片描述

举例

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

scala> val group = rdd.groupBy(_%2)
group: org.apache.spark.rdd.RDD[(Int, Iterable[Int])] = ShuffledRDD[6] at groupBy at <console>:25

scala> group.collect
res2: Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(2, 4)), (1,CompactBuffer(1, 3)))

filter(func)

作用: 过滤。返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入元素组成。返回值为 false 的元素将被过滤掉。 内部实现相当于生成 FilteredRDD(this,sc.clean(f))。

详解

下面代码为函数的本质实现:deffilter(f:T=>Boolean):RDD[T]=newFilteredRDD(this,sc.clean(f))

下图中每个方框代表一个 RDD 分区, T 可以是任意的类型。通过用户自定义的过滤函数 f,对每个数据项操作,将满足条件、返回结果为 true 的数据项保留。例如,过滤掉 V2 和 V3 保留了 V1,为区分命名为 V’1。

在这里插入图片描述
举例

scala> val rdd = sc.makeRDD(1 to 10)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[7] at makeRDD at <console>:24

scala> rdd.filter(_%2==0).collect
res3: Array[Int] = Array(2, 4, 6, 8, 10)

sample(withReplacement, fraction, seed)

作用 : 以指定的随机种子随机抽样出数量为 fraction 的数据,withReplacement 表示是抽出的数据是否放回,true为有放回的抽样,false为无放回的抽样,seed用于指定随机数生成器种子。

详情
sample 将 RDD 这个集合内的元素进行采样,获取所有元素的子集。用户可以设定是否有放回的抽样、百分比、随机种子,进而决定采样方式。内部实现是生成 SampledRDD(withReplacement, fraction, seed)
注意:

  • 有放回时,fraction可以大于1,代表元素被抽到的次数
  • 无放回时,fraction代表元素被抽到的概率(0-1)

下图中的每个方框是一个 RDD 分区。 通过 sample 函数, 采样 50% 的数据。V1、 V2、 U1、 U2、U3、U4 采样出数据 V1 和 U1、 U2 形成新的 RDD。

在这里插入图片描述
举例

// 1)创建RDD
scala> val rdd = sc.parallelize(1 to 10)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[20] at parallelize at <console>:24

// 2) 打印
scala> rdd.collect()
res15: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 3)放回抽样
scala> var sample1 = rdd.sample(true,0.4,2)
sample1: org.apache.spark.rdd.RDD[Int] = PartitionwiseSampledRDD[21] at sample at <console>:26

// 4)打印放回抽样结果
scala> sample1.collect()
res16: Array[Int] = Array(1, 2, 2, 7, 7, 8, 9)

// 5)打印不放回抽样结果
scala> var sample2 = rdd.sample(false,0.2,3)
sample2: org.apache.spark.rdd.RDD[Int] = PartitionwiseSampledRDD[22] at sample at <console>:26

// 6)打印不放回抽样结果
scala> sample2.collect()
res17: Array[Int] = Array(1, 9)

takeSample

作用
takeSample()函数和上面的sample函数是一个原理,但是不使用相对比例采样,而是按设定的采样个数进行采样,同时返回结果不再是RDD,而是相当于对采样后的数据进行Collect(),返回结果的集合为单机的数组。

下图中左侧的方框代表分布式的各个节点上的分区,右侧方框代表单机上返回的结果数组。 通过takeSample对数据采样,设置为采样一份数据,返回结果为V1。

在这里插入图片描述
举例

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

scala> rdd.takeSample(true,3,3)
res4: Array[Int] = Array(6, 2, 2)

scala> rdd.takeSample(false,3,3)
res5: Array[Int] = Array(6, 4, 2)

distinct([numTasks]))

作用:对原 RDD 进行去重后返回一个新的 RDD。默认情况下,只有8个并行任务来操作,但是可以传入一个可选的 numTasks 参数改变它。

详解

下图中的每个方框代表一个RDD分区,通过 distinct 函数,将数据去重。 例如,重复数据V1、 V1去重后只保留一份V1。
在这里插入图片描述
举例

scala> val rdd = sc.parallelize(List(1,2,3,4,4,2,1))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[82] at parallelize at <console>:24

scala> rdd.distinct.collect
res51: Array[Int] = Array(4, 2, 1, 3)

coalesce(numPartitions)

作用:缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。

举例

// 1)创建一个RDD
scala> val rdd = sc.parallelize(1 to 16,4)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[54] at parallelize at <console>:24

// 2)查看RDD的分区数
scala> rdd.partitions.size
res20: Int = 4

// 3)对RDD重新分区
scala> 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值