Spark算子简介及实例【Transformation算子和Action算子】

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(转换)算子,如mapflatMapfilterreduceByKey等,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)
    
  • 计算结果:在控制台上打印出 12345

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,通过键查找对应的值。这通常与groupByKeyreduceByKey等操作结合使用。

  • 示例过程:假设我们有一个通过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)

【个人笔记,如果错误,欢迎指正】

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值