四、基础编程
- 双Value算子
-
intersection
求两个RDD的交集
函数签名:def intersection(other: RDD[T]): RDD[T]
示例:
val dataRDD1 = sc.makeRDD(List(1,2,3,4)) val dataRDD2 = sc.makeRDD(List(3,4,5,6)) val dataRDD = dataRDD1.intersection(dataRDD2)
注意:若两个RDD的数据类型不一致,则无法做交集运算。
-
union
求两个RDD的并集
函数签名:def union(other: RDD[T]): RDD[T]
示例:
val dataRDD1 = sparkContext.makeRDD(List(1,2,3,4)) val dataRDD2 = sparkContext.makeRDD(List(3,4,5,6)) val dataRDD = dataRDD1.union(dataRDD2)
注意:若两个RDD的数据类型不一致,则无法做并集运算。
-
subtract
以一个RDD元素为主,去除两个RDD中重复元素,将其他元素保留下来。求差集。
函数签名:def subtract(other: RDD[T]): RDD[T]
示例:
val dataRDD1 = sc.makeRDD(List(1,2,3,4)) val dataRDD2 = sc.makeRDD(List(3,4,5,6)) val dataRDD = dataRDD1.subtract(dataRDD2)
注意:若两个RDD的数据类型不一致,则无法做差集运算。
-
zip
将两个RDD中的元素,以键值对的形式进行合并。其中,键值对中的Key为第1个RDD中的元素,Value为第2个RDD中的元素。
函数签名:def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]
示例:
val dataRDD1 = sc.makeRDD(List(1,2,3,4)) val dataRDD2 = sc.makeRDD(List(3,4,5,6)) val dataRDD = dataRDD1.zip(dataRDD2)
注意:
1. 若两个RDD的数据类型不一致,则无法做拉链运算。
2. 若两个RDD的数据量不一致,则无法做拉链运算。
3. 若两个RDD的分区数不一致,则无法做拉链运算。
-
- KV算子
顾名思义,KV类型的算子需要对 K-V 键值对类型的数据进行处理,因此在大多数情况下,会配合 map 算子使用。-
partitionBy
将数据按照指定Partitioner重新进行分区。Spark默认的分区器是HashPartitioner。函数签名:
def partitionBy(partitioner: Partitioner): RDD[(K, V)]
示例:
val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4), 2) val rdd1: RDD[(Int, Int)] = rdd.map((_, 1)) val rdd2: RDD[(Int, String)] = rdd.partitionBy(new HashPartitioner(2))
Spark提供了三个分区器:HashPartitioner、RangePartitioner 以及 PythonPartitioner。由于 PythonPartitioner 存在限制,且受同包约束,并不通用,这里不再介绍。
RangePartitioner,范围分区器。顾名思义,可以按照一定范围分区,例如1-5分区,6-10分区。但该分区器需要数据可以进行比较,因此使用也较少。一般在排序时使用。
Spark也提供了自定义分区器功能:
val wordRDD: RDD[String] = sc.makeRDD(List("Hello", "World", "Hello", "Spark")) wordRDD.map((_, 1)).partitionBy(new myPartition()).cache().collect().foreach(println) class myPartition extends Partitioner{ // 分区数量 override def numPartitions: Int = 3 // 分区规则 override def getPartition(key: Any): Int = { key match { case "Hello" => 1 case "World" => 2 case _ => 3 } } }
注意:
1. 与repartition算子相比,partitionBy更侧重对数据的分区。例如奇数偶数、正数负数之类的分区,repartition更侧重分区数量的变化。
2. 二者都存在shuffle过程。 -
reduceByKey
可以将数据按照相同的Key对Value进行聚合函数签名:
def reduceByKey(func: (V, V) => V): RDD[(K, V)] def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
示例:
val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("c",3))) val dataRDD2 = dataRDD1.reduceByKey(_+_) val dataRDD3 = dataRDD1.reduceByKey(_+_, 2)
-
groupByKey
将分区的数据直接转换为相同类型的内存数组.函数签名:
def reduceByKey(func: (V, V) => V): RDD[(K, V)] def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]
示例:
val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("a",3))) val dataRDD2 = dataRDD1.groupByKey() val dataRDD3 = dataRDD1.groupByKey(2) val dataRDD4 = dataRDD1.groupByKey(new HashPartitioner(2))
注意:
reduceByKey和groupByKey都是对数据进行聚合,区别在于:
1. groupByKey只是单纯将数据进行分组,结果是<k, value-list>形式;而reduceByKey则多了一步计算的操作;
2.从效率上来看,reduceByKey要比groupByKey的效率要好,因为reduceByKey底层提供了预聚合的操作。 -
aggregateByKey
将数据根据不同的规则进行分区内计算和分区间计算。
函数签名:def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)]
示例:
// 取出每个分区内相同key的最大值然后分区间相加 val rdd = sc.makeRDD(List(("a",1),("a",2),("c",3),("b",4),("c",5),("c",6)),2) // 0:("a",1),("a",2),("c",3) => (a,10)(c,10) => 和下面合并 // 1:("b",4),("c",5),("c",6) => (b,10)(c,10) => (a,10)(b,10)(c,20) val resultRDD = rdd.aggregateByKey(10)( (x, y) => math.max(x,y),// 分区内做运算 (x, y) => x + y // 分区间做运算 ) resultRDD.collect().foreach(println)
-
foldByKey
当分区内计算规则和分区间计算规则相同时,aggregateByKey就可以简化为foldByKey。 函数签名:def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
示例:
val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("c",3))) val dataRDD2 = dataRDD1.foldByKey(0)(_+_)
-
combineByKey
最通用的对key-value型rdd进行聚集操作的聚集函数(aggregation function)。类似于aggregate(),combineByKey()允许用户返回值的类型与输入不一致。示例:
// 将数据List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98))求每个key的平均值 val list: List[(String, Int)] = List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)) val input: RDD[(String, Int)] = sc.makeRDD(list, 2) val combineRdd: RDD[(String, (Int, Int))] = input.combineByKey( (_, 1), (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1), (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2) )
-
sortByKey
在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的。
函数签名:def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length): RDD[(K, V)]
示例:
val dataRDD1 = sc.makeRDD(List(("a",1),("b",2),("c",3))) val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(true) val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(false)
-
join
在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素连接在一起的(K,(V,W))的RDD。
函数签名:def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
示例:
val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c"))) val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (3, 6))) rdd.join(rdd1).collect().foreach(println)
-
cogroup
在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素连接在一起的(K,(V,W))的RDD。 函数签名:def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
示例:
val dataRDD1 = sc.makeRDD(List(("a",1),("a",2),("c",3))) val dataRDD2 = sc.makeRDD(List(("a",1),("c",2),("c",3))) val value: RDD[(String, (Iterable[Int], Iterable[Int]))] = dataRDD1.cogroup(dataRDD2)
-