Spark的算子主要分为两大类:Transformation(转换)算子和Action(动作)算子。以下是各种算子举例:
一、Transformation(转换)算子
Transformation算子用于在RDD上执行各种转换操作,但不会立即计算结果,而是延迟到遇到Action算子时才会触发真正的计算。
1.map
-
示例过程:对RDD中的每个元素应用一个函数,并返回一个新的RDD。
-
示例代码与结果:
val rdd = sc.parallelize(Array(1, 2, 3, 4)) val mappedRdd = rdd.map(x => x * 2) // 结果:mappedRdd 包含 [2, 4, 6, 8]
2.flatMap
-
示例过程:对RDD中的每个元素应用一个函数,并返回一个新的RDD,其中包含了所有由输入元素转换得到的元素。
-
示例代码与结果:
val rdd = sc.parallelize(Array(1, 2, 3)) val flatMappedRdd = rdd.flatMap(x => Array(x, x + 1)) // 结果:flatMappedRdd 包含 [1, 2, 2, 3, 3, 4]
3.filter
-
示例过程:返回一个新的RDD,其中包含通过给定函数评估为true的所有元素。
-
示例代码与结果:
val rdd = sc.parallelize(Array(1, 2, 3, 4)) val filteredRdd = rdd.filter(x => x % 2 == 0) // 结果:filteredRdd 包含 [2, 4]
4.reduceByKey
-
示例过程:对具有相同键的值执行聚合操作,并返回结果RDD。
-
示例代码与结果:
val rdd = sc.parallelize(Array(("a", 1), ("b", 2), ("a", 3))) val reducedRdd = rdd.reduceByKey(_ + _) // 结果:reducedRdd 包含 [("a", 4), ("b", 2)]
5.mapPartitions
-
说明:接收一个函数作为参数,这个函数应用于RDD的每一个分区,而不是每一个元素。因此,它允许用户以更高效的方式处理数据,因为它减少了跨分区的通信。
-
示例过程:假设我们有一个RDD,每个元素是一个整数,我们想要对每个分区中的元素进行平方操作。
-
示例代码:
val rdd = sc.parallelize(Array(1, 2, 3, 4, 5, 6), 2) // 假设有两个分区 val mappedPartitionsRdd = rdd.mapPartitions(iter => iter.map(x => x * x))
-
计算结果:
mappedPartitionsRdd
包含[1, 4, 9, 16, 25, 36]
(假设分区没有改变元素顺序)。
6.union
-
说明:对两个或多个RDD进行并集操作,并返回一个新的RDD。注意,这些RDD必须具有相同的类型。
-
示例过程:我们有两个RDD,分别包含一些整数,我们想要将它们合并成一个新的RDD。
-
示例代码:
val rdd1 = sc.parallelize(Array(1, 2, 3)) val rdd2 = sc.parallelize(Array(3, 4, 5)) val unionRdd = rdd1.union(rdd2)
-
计算结果:
unionRdd
包含[1, 2, 3, 3, 4, 5]
(注意,由于并集操作,元素3出现了两次)。
7.intersection
-
说明:返回两个RDD的交集,即两个RDD中都存在的元素。
-
示例过程:我们有两个RDD,我们想要找出它们共同的元素。
-
示例代码:
val rdd1 = sc.parallelize(Array(1, 2, 3)) val rdd2 = sc.parallelize(Array(2, 3, 4)) val intersectionRdd = rdd1.intersection(rdd2)
-
计算结果:
intersectionRdd
包含[2, 3]
。
8.distinct
-
说明:返回一个新的RDD,其中包含了原RDD中的所有不重复元素。
-
示例过程:我们有一个包含重复元素的RDD,我们想要去除这些重复元素。
-
示例代码:
val rdd = sc.parallelize(Array(1, 2, 2, 3, 3, 3)) val distinctRdd = rdd.distinct()
-
计算结果:
distinctRdd
包含[1, 2, 3]
。
9.groupByKey
-
说明:对于键值对RDD,将具有相同键的值组合成一个迭代器,并返回一个新的键值对RDD。
-
示例过程:我们有一个键值对RDD,我们想要将具有相同键的值组合在一起。
-
示例代码:
val pairRdd = sc.parallelize(Array(("a", 1), ("b", 1), ("a", 2))) val groupedRdd = pairRdd.groupByKey()
-
计算结果:
groupedRdd
包含[("a", CompactBuffer(1, 2)), ("b", CompactBuffer(1))]
(这里的CompactBuffer
是Spark内部用于表示迭代器的数据结构)。
除了之前列举的Spark Transformation(转换)算子,如map
、flatMap
、filter
、reduceByKey
等,Spark还提供了许多其他的转换操作。以下是一些额外的转换操作,包括它们的实例过程和计算结果:
10. sortBy
-
说明:对RDD中的元素进行排序,并返回一个新的RDD。
-
示例过程:假设我们有一个包含整数的RDD,我们想要按升序对它们进行排序。
-
示例代码:
val rdd = sc.parallelize(Array(5, 2, 9, 1, 5, 6)) val sortedRDD = rdd.sortBy(x => x)
-
计算结果:
sortedRDD
将包含[1, 2, 5, 5, 6, 9]
。
11. sortByKey
-
说明:对于键值对RDD,根据键进行排序,并返回一个新的键值对RDD。
-
示例过程:假设我们有一个包含键值对的RDD,我们想要根据键进行排序。
-
示例代码:
val pairRDD = sc.parallelize(Array(("b", 2), ("a", 1), ("c", 3))) val sortedByKeyRDD = pairRDD.sortByKey()
-
计算结果:
sortedByKeyRDD
将包含[("a", 1), ("b", 2), ("c", 3)]
。
12. cartesian
-
说明:对两个RDD进行笛卡尔积操作,并返回一个新的RDD,其中包含两个RDD中所有可能的元素对。
-
示例过程:假设我们有两个RDD,分别包含一些整数,我们想要得到它们之间的所有可能组合。
-
示例代码:
val rdd1 = sc.parallelize(Array(1, 2)) val rdd2 = sc.parallelize(Array(3, 4, 5)) val cartesianRDD = rdd1.cartesian(rdd2)
-
计算结果:
cartesianRDD
将包含[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]
。
13. sample(withReplacement, fraction, seed)
-
说明:对RDD进行采样,返回一个包含原始RDD中随机样本的新RDD。
-
参数:
withReplacement
: 一个布尔值,表示是否进行有放回抽样。fraction
: 抽样的比例,即抽取的元素占原始RDD的比例。seed
: 用于生成随机数的种子。
-
示例过程:假设我们有一个包含多个整数的RDD,我们想要从中抽取一部分元素作为样本。
-
示例代码:
val rdd = sc.parallelize(Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) val sampledRDD = rdd.sample(false, 0.5, 12345)
-
计算结果:
sampledRDD
将包含原始RDD中大约50%的元素,但具体哪些元素被选中是随机的(在这个例子中,使用了种子12345来确保结果的可重复性)。
14. zip
-
说明:将两个RDD的元素进行配对,并返回一个新的RDD,其中包含这些元素对。两个RDD的长度必须相同。
-
将两个RDD的元素按位置组合成一个新的RDD。这个操作在某些情况下可能被视为Transformation,因为它不直接产生结果,而是创建了一个新的RDD。但在某些上下文中,它也可以被视为一种特殊的Action,因为它要求两个RDD具有相同的分区数和元素数。
-
示例过程:假设我们有两个RDD,分别包含一些整数,我们想要将它们配对。
-
示例代码:
val rdd1 = sc.parallelize(Array(1, 2, 3)) val rdd2 = sc.parallelize(Array("a", "b", "c")) val zippedRDD = rdd1.zip(rdd2)
-
计算结果:
zippedRDD
将包含[(1, "a"), (2, "b"), (3, "c")]
。
二、Action(动作)算子
Action算子用于触发RDD的计算,并将结果返回给驱动程序或写入外部存储系统。
1.collect
-
示例过程:将RDD中的所有元素作为数组返回给驱动程序。
-
示例代码与结果:
val rdd = sc.parallelize(Array(1, 2, 3, 4)) val result = rdd.collect() // 结果:result 是一个数组 [1, 2, 3, 4]
2.count
-
示例过程:返回RDD中的元素数量。
-
示例代码与结果:
val rdd = sc.parallelize(Array(1, 2, 3, 4)) val count = rdd.count() // 结果:count 是 4
3.saveAsTextFile
-
示例过程:将RDD的内容保存为文本文件。
-
示例代码(无直接结果输出,但会在文件系统中生成文件):
val rdd = sc.parallelize(Array("Hello", "World")) rdd.saveAsTextFile("path/to/output")
4.take(n)
-
说明:返回RDD中的前n个元素。
-
示例过程:假设我们有一个包含多个整数的RDD,我们想要获取其中的前3个元素。
-
示例代码:
val rdd = sc.parallelize(Array(1, 2, 3, 4, 5, 6)) val result = rdd.take(3)
-
计算结果:
result
是一个数组,包含[1, 2, 3]
。
5.first()
-
说明:返回RDD中的第一个元素。
-
示例过程:与
take(1)
类似,但只返回第一个元素。 -
示例代码:
val rdd = sc.parallelize(Array(1, 2, 3, 4, 5, 6)) val firstElement = rdd.first()
-
计算结果:
firstElement
的值是1
。
6. countByValue()
-
说明:对于RDD中的元素,返回每个元素及其出现次数的映射。
-
示例过程:假设我们有一个包含多个整数的RDD,我们想要知道每个整数出现了多少次。
-
示例代码:
val rdd = sc.parallelize(Array(1, 1, 2, 2, 2, 3)) val countByValueResult = rdd.countByValue()
-
计算结果:
countByValueResult
是一个映射(在Scala中可能是Map[Int, Long]
),包含{(1, 2), (2, 3), (3, 1)}
。
7.saveAsSequenceFile(path)
-
说明:将RDD中的元素以Hadoop SequenceFile格式保存到指定的文件系统中。
-
示例过程:假设我们有一个键值对RDD,我们想要将其保存为SequenceFile。
-
示例代码(假设RDD为键值对RDD):
val pairRdd = sc.parallelize(Array(("key1", "value1"), ("key2", "value2"))) pairRdd.saveAsSequenceFile("path/to/output")
-
计算结果:在指定的文件系统中,会生成一个或多个SequenceFile文件,其中包含RDD中的键值对数据。
8. foreach(func)
-
说明:对RDD中的每个元素执行给定的函数。这通常用于在驱动程序中执行副作用操作(如更新累加器或打印日志)。
-
示例过程:假设我们想要打印RDD中的每个元素。
-
示例代码:
val rdd = sc.parallelize(Array(1, 2, 3, 4, 5)) rdd.foreach(println)
-
计算结果:在控制台上打印出
1
、2
、3
、4
、5
。
9.foreachPartition(func)
-
说明:类似于
foreach
,但该函数应用于RDD的每个分区,而不是每个元素。这允许用户以更高效的方式处理数据,因为它减少了跨分区的通信。 -
示例过程与示例代码(类似于mapPartitions的示例,但这里只是执行副作用操作):
val rdd = sc.parallelize(Array(1, 2, 3, 4, 5, 6), 2) rdd.foreachPartition(iter => iter.foreach(println))
-
计算结果:在控制台上打印出RDD中的每个元素,但可能按分区顺序打印。
10. takeOrdered(n, [ordering])
-
说明:返回RDD中按自然顺序或自定义顺序排序后的前n个元素。
-
示例过程:假设我们有一个包含整数的RDD,我们想要获取按升序排序后的前3个元素。
-
示例代码:
val rdd = sc.parallelize(Array(5, 2, 9, 1, 5, 6)) val top3 = rdd.takeOrdered(3) // 默认按升序
-
计算结果:
top3
将包含[1, 2, 5]
(注意这里可能包含重复的5,因为takeOrdered
不执行去重)。
如果需要自定义排序顺序,可以传入一个Ordering[T]
对象,例如:
import scala.math.Ordering[Int].reverse // 导入降序排序的Ordering
val top3Descending = rdd.takeOrdered(3)(Ordering[Int].reverse)
此时top3Descending
将包含[9, 6, 5]
。
11. lookup(key)
-
说明:对于包含键值对的RDD,通过键查找对应的值。这通常与
groupByKey
或reduceByKey
等操作结合使用。 -
示例过程:假设我们有一个通过
reduceByKey
处理过的键值对RDD,我们想要查找某个键对应的值。 -
示例代码(假设pairRDD是一个通过reduceByKey处理过的RDD):
val pairRDD = sc.parallelize(Array(("a", 1), ("b", 2), ("a", 3))).reduceByKey(_ + _) val valuesForA = pairRDD.lookup("a")
-
计算结果:
valuesForA
将是一个数组,包含[4]
(因为键"a"对应的值是1和3的和)。
12. countByKey()
-
说明:对于键值对RDD,计算每个键的出现次数。
-
示例过程:假设我们有一个包含键值对的RDD,我们想要知道每个键出现了多少次。
-
示例代码:
val pairRDD = sc.parallelize(Array(("a", 1), ("b", 2), ("a", 3), ("c", 4))) val keyCounts = pairRDD.countByKey()
-
计算结果:
keyCounts
将是一个映射(在Scala中可能是Map[K, Long]
),如Map("a" -> 2, "b" -> 1, "c" -> 1)
。
【个人笔记,如果错误,欢迎指正】