Spark相比于Mapreduce的一大优势就是提供了很多的方法,可以直接使用;另一个优势就是执行速度快,这要得益于DAG的调度,想要理解这个调度规则,还要理解函数之间的依赖关系。
本篇就着重描述下Spark提供的Transformations方法.
![c43e318acd7f3b886016dde85a8303d6.png](https://i-blog.csdnimg.cn/blog_migrate/777b38b72b52e99a8d1447d2df43783c.jpeg)
依赖关系
宽依赖和窄依赖
窄依赖(narrow dependencies)
窄依赖是指父RDD仅仅被一个子RDD所使用,子RDD的每个分区依赖于常数个父分区(O(1),与数据规模无关)。
输入输出一对一的算子,且结果RDD的分区结构不变。主要是map/flatmap
输入输出一对一的算子,但结果RDD的分区结构发生了变化,如union/coalesce
从输入中选择部分元素的算子,如filter、distinct、substract、sample
宽依赖(wide dependencies)
宽依赖是指父RDD被多个子分区使用,子RDD的每个分区依赖于所有的父RDD分区(O(n),与数据规模有关)
对单个RDD基于key进行重组和reduce,如groupByKey,reduceByKey
对两个RDD基于key进行join和重组,如join(父RDD不是hash-partitioned )
需要进行分区,如partitionBy
Transformations转换方法实例
![e20c98dc222e219778ed4f4d348a7a6e.png](https://i-blog.csdnimg.cn/blog_migrate/e809d8f7b51c0ba5897a5844095df759.jpeg)
map(func)
map用于遍历rdd中的每个元素,可以针对每个元素做操作处理:
scala> var data = sc.parallelize(1 to 9,3)//内容为 Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)scala> data.map(x=>x*2).collect()//输出内容 Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18)
filter(func)
filter用于过滤元素信息,仅仅返回满足过滤条件的元素
scala> var data = sc.parallelize(1 to 9,3)//内容为 Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)scala> data.filter(x=> x%2==0).collect()//输出内容 Array[Int] = Array(2, 4, 6, 8)
flatMap(func)
flatMap与map相比,不同的是可以输出多个结果,比如
scala> var data = sc.parallelize(1 to 4,1)//输出内容为 Array[Int] = Array(1, 2, 3, 4)scala> data.flatMap(x=> 1 to x).collect()//输出内容为 Array[Int] = Array(1, 1, 2, 1, 2, 3, 1, 2, 3, 4)
mapPartitions(func)
mapPartitions与map类似,只不过每个元素都是一个分区的迭代器,因此内部可以针对分区为单位进行处理。
比如,针对每个分区做和
//首先创建三个分区scala> var data = sc.parallelize(1 to 9,3)//输出为 Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)//查看分区的个数scala> data.partitions.size//输出为 Int = 3//使用mapPartitionsscala> var result = data.mapPartitions{ x=> { | var res = List[Int]() | var i = 0 | while(x.hasNext){ | i+=x.next() | } | res.::(i).iterator | }}scala> result.collect//输出为 Array[Int] = Array(6, 15, 24)
mapPartitionsWithIndex(func)
这个方法与上面的mapPartitions相同,只不过多提供了一个Index参数。
//首先创建三个分区scala> var data = sc.parallelize(1 to 9,3)//输出为 Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)//查看分区的个数scala> data.partitions.size//输出为 Int = 3scala> var result = data.mapPartitionsWithIndex{ | (x,iter) => { | var result = List[String]() | var i = 0 | while(iter.hasNext){ | i += iter.next() | } | result.::( x + "|" +i).iterator | }} result.collect//输出结果为 Array[String] = Array(0|6, 1|15, 2|24)
sample(withReplacement, fraction, seed)
这个方法可以用于对数据进行采样,比如从1000个数据里面随机5个数据。
第一个参数withReplacement代表是否进行替换,如果选true,上面的例子中,会出现重复的数据
第二个参数fraction 表示随机的比例
第三个参数seed 表示随机的种子
//创建数据var data = sc.parallelize(1 to 1000,1)//采用固定的种子seed随机data.sample(false,0.005,0).collect//输出为 Array[Int] = Array(53, 423, 433, 523, 956, 990)//采用随机种子data.sample(false,0.005,scala.util.Random.nextInt(1000)).collect//输出为 Array[Int] = Array(136, 158)
union(otherDataset)
union方法可以合并两个数据集,但是不会去重,仅仅合并而已。
//创建第一个数据集scala> var data1 = sc.parallelize(1 to 5,1)//创建第二个数据集scala> var data2 = sc.parallelize(3 to 7,1)//取并集scala> data1.union(data2).collect//输出为 Array[Int] = Array(1, 2, 3, 4, 5, 3, 4, 5, 6, 7)
intersection(otherDataset)
这个方法用于取两个数据集的交集
//创建第一个数据集scala> var data1 = sc.parallelize(1 to 5,1)//创建第二个数据集scala> var data2 = sc.parallelize(3 to 7,1)//取交集scala> data1.intersection(data2).collect//输出为 Array[Int] = Array(4, 3, 5)
distinct([numTasks]))
这个方法用于对本身的数据集进行去重处理。
//创建数据集scala> var data = sc.parallelize(List(1,1,1,2,2,3,4),1)//执行去重scala> data.distinct.collect//输出为 Array[Int] = Array(4, 1, 3, 2)//如果是键值对的数据,kv都相同,才算是相同的元素scala> var data = sc.parallelize(List(("A