Spark算子

Spark有许多常用算子,这里结合几篇文章做一下备份,方便自己后面快速查阅。本篇文章主要分析每个算子的应用方法,后面写到RDD、DataSet和DataFrame区别的时候会从算子的数据类型转变的进行进一步分析。

Transformation

  • map
    map的输入变换应用于RDD中的所有元素。
scala> val array = Array(1,2,3,4)
array:Array[Int] = Array(1, 2, 3, 4)
scala>array.map(x=>(x,"spark"))
res0:Array[(Int,String)]=Array((1,"spark"),(2,"spark"),(3,"spark"),(4,"spark"))
  • flatmap
    flatmap与map的区别在于flat,flat有扁平的意思。
scala> val arr = Array("spark,datas","hadoop,datah","python,datap")
arr:Array[String] = Array(spark,datas,hadoop,datah,python,datap)
scala> arr.flatmap(x=>x.split(","))
res:Array[String] = Array(spark,datas,hadoop,datah,python,datap)
  • mapValues
scala> val list = List("hadoop","spark","hive","spark")
scala> val rdd = sc.parallelize(list)
scala> val pairRdd = rdd.map(x => (x,1))
scala> pairRdd.mapValues(_+1).collect.foreach(println)
res1:(hadoop,2),(spark,2),(hive,2),(spark,2)
  • mapPartitions
    mapPartitions与map的区别也在于partitions(分区),map每处理一个元素就需要调用一次函数,而mapPartition则是每个分区调用一次。有很多博客说在数据量很低的时候可以有效提升性能,有的也说用这个算子比较危险容易出现OOM问题。特别是对于一些容易产生数据激增的业务场景里面。但是对于一些写数据库的场景中具有很好的性能优化。因为对于分布式场景中,连接数据库的操作必须在算子内部才能被executor执行,这个时候每个partition只需要建立一次连接就好了。至于数据量上的区别还有待于我自己测试后进行说明!
  • filter(func)
    过滤操作,根据func的条件过滤RDD中元素。返回一个新的RDD
scala> val a = Array(1,2,3,4,5,6,7)
a:Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)
scala> a.filter(x=>x%2 == 0)
res8:Array[Int] = Array(2,4,6,8)
  • union(rdd)
    合并两个数据集,不去重!!!
scala> val aa = Array(1,2,3)
aa: Array[Int] = Array(1, 2, 3)
scala> val bb = Array(3,4,5)
bb:Array[Int] = Array(3, 4, 5)
scala> aa.union(bb)
res:Array[Int] = Array(1,2,3,3,4,5)
  • instersect
    两个数据集求交集,并去重
scala> val aa = Array(1,2,3)
aa: Array[Int] = Array(1, 2, 3)
scala> val bb = Array(3,4,5)
bb:Array[Int] = Array(3, 4, 5)
scala> aa.instersect(bb)
res:Array[Int] = Array(3)
  • sample(withReplacement,fraction,seed)
    对数据集采样,withReplacement:表示是否放回采样;fraction:采样比例;seed:随机数种子
  • groupByKey([numTasks])
    pairRDD或(k,v)调用。返回一个(k,Iterable)。主要作用是将相同的key的值分配到一个集合序列中,当然顺序不一定。但是,需要将键值加载到内存中,如果一个键对应的值过多可能会出现OOM问题。先看例子:
# 计算单词的个数
scala> val words = Array("one","two","two","three","three","three")
scala> val wordPairRDD = sc.parallelize(words).map(word => (word, 1))
res:Array[(String,Int)] =  Array[(one,1),(two,1),(two,1),(three,1),(three,1),(three,1)]
scala> val wordGroup = wordPairRDD.groupByKey()
groupPair:RDD[(String,Iterable[Int])] = ShuffledRDD[4] at groupByKey
scala>val a =  wordGroup.collect()
res:Array[(String,Interable[Int])] = Array[("one",CompactBuffer(1)),
("two",CompactBuffer(1,1)),("three",CompactBuffer(1,1,1))]
# 这里访问方式注意一下,key是一组,value是一组。每一组里面可能也有多个元素。所以会有
#  先确定组再内部访问的方法!!!
scala>a.map(t=>(t._1,t._2.sum)).collect()

这里在补充一个例子,一般在工程中都是这样使用的。
注:1、groupBy(function)——>与groupByKey的区别在于,groupByKey是无参数的所以调用的结构本来就是key-value结构的。而groupBy中根据传入的函数来进行分组。举例来说:

scala> val a = List(("a",2),("a",3),("d",333),("c",3),("c",4))
scala> val b = a.groupBy(_._1)
b: scala.collection.immutable.Map[String,List[(String, Int)]] = 
Map(d -> List((d,333)), a -> List((a,2), (a,3)), c -> List((c,3), (c,4)))
scala> val c = b.mapValues(r => {r.map(r => {r._2})})
scala> println(c)
c: scala.collection.immutable.Map[String,List[Int]] = Map(d -> List(333), a -> List(2, 3), c -> List(3, 4))
  • reduceByKey
    reduceByKey的作用是聚合,groupByKey的作用是分组。上文例子中也可以使用reduceByKey来解决。解决方案如下所示:
val wordCount = wordPairsRDD.reduceByKey(_ + _).collect()

这个是根据key,对迭代器中的数值进行迭代累加。


  • 迭代器(Iterator)
### Spark算子概述 Spark 是一种分布式计算框架,其核心概念之一是 **RDD(弹性分布式数据集)**。通过一系列的转换操作(Transformation),可以构建复杂的并行计算流程。最终可以通过行动操作(Action)触发实际的任务执行。 以下是常见的 Spark 算子分类及其使用方法: --- #### 转换算子(Transformation) ##### `map` 将输入中的每一个元素应用指定函数,并返回一个新的 RDD。 ```python rdd = sc.parallelize([1, 2, 3]) mapped_rdd = rdd.map(lambda x: x * 2) print(mapped_rdd.collect()) # 输出: [2, 4, 6] ``` ##### `filter` 过滤掉不满足条件的数据项。 ```python filtered_rdd = rdd.filter(lambda x: x % 2 == 0) print(filtered_rdd.collect()) # 输出: [2] ``` ##### `flatMap` 类似于 map,但是会将结果扁平化处理。 ```python words = ["hello world", "this is a test"] flat_mapped_rdd = sc.parallelize(words).flatMap(lambda line: line.split()) print(flat_mapped_rdd.collect()) # 输出: ['hello', 'world', 'this', 'is', 'a', 'test'] ``` ##### `repartition` 重新分配分区数量,可能引发 Shuffle 操作。 ```python rep_rdd = rdd.repartition(4) # 将分区数调整为4 print(rep_rdd.getNumPartitions()) # 输出: 4 ``` 此过程涉及 Shuffle 的实现细节可参见[^3]。 --- #### 行动算子(Action) ##### `reduce` 对 RDD 中的所有元素进行两两聚合运算。 ```python sum_result = rdd.reduce(lambda x, y: x + y) print(sum_result) # 输出: 7 ``` ##### `collect` 将整个 RDD 收集到驱动程序中作为列表返回。 ```python collected_data = rdd.collect() print(collected_data) # 输出: [1, 2, 3] ``` ##### `count` 统计 RDD 中的元素总数。 ```python cnt = rdd.count() print(cnt) # 输出: 3 ``` ##### `take` 获取前 N 个元素。 ```python taken_elements = rdd.take(2) print(taken_elements) # 输出: [1, 2] ``` --- #### Join 类型算子 ##### `join` 和 `leftOuterJoin` 用于两个键值对类型的 RDD 进行连接操作。 ```python rdd1 = sc.parallelize([(1, "Apple"), (2, "Banana")]) rdd2 = sc.parallelize([(2, "Yellow"), (3, "Red")]) joined_rdd = rdd1.join(rdd2) print(joined_rdd.collect()) # 输出 [(2, ('Banana', 'Yellow'))] outer_joined_rdd = rdd1.leftOuterJoin(rdd2) print(outer_joined_rdd.collect()) # 结果示例可见于[^4] ``` --- #### 自定义算子 在 Java 或 Scala 中自定义算子时,通常需要继承特定接口并重写方法。例如,在 Java 中实现 Filter 功能: ```java JavaRDD<String> filteredRdd = rdd.filter(new Function<String, Boolean>() { public Boolean call(String row) throws Exception { return !row.isEmpty(); // 只保留非空字符串 } }); ``` 上述逻辑也可扩展至其他复杂场景下,具体实现方式详见[^2]。 --- ### 总结 以上展示了部分常用的 Spark 算子以及它们的应用实例。每种算子都有独特的用途和性能特点,合理选择能够显著提升作业效率。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值