Spark算子

1:Zip算子

def zip[U](other: RDD[U])(implicit arg0: ClassTag[U]): RDD[(T, U)]

将两个RDD做zip操作,如果当两个RDD分区数目不一样的话或每一个分区数目不一样的话则会异常。

例如:

val rdd1 = sc.parallelize(Array(1,2,3,4,5,6),2)
val rdd2 = sc.parallelize(Array(1,2,3,4,5,6),3)
rdd.zip(rdd1).collect

异常信息:

java.lang.IllegalArgumentException: Can't zip RDDs with unequal numbers of partitions: List(2, 3)
  at org.apache.spark.rdd.ZippedPartitionsBaseRDD.getPartitions(ZippedPartitionsRDD.scala:57)
  at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:252)
  at org.apache.spark.rdd.RDD$$anonfun$partitions$2.apply(RDD.scala:250)

 

例如:

val rdd1 = sc.parallelize(Array(1,2,3,4,5,6),2)
val rdd2 = sc.parallelize(Array(1,2,3,4,5,6,7),2)
rdd.zip(rdd1).collect

异常信息:

Caused by: org.apache.spark.SparkException: Can only zip RDDs with same number of elements in each partition

 

2:zipPartitions

以分区为单位进行zip操作,要求分区数目相等。否则异常。

val rdd1 = sc.parallelize(Array(1,2,3,4,5,6),2)
val rdd2 = sc.parallelize(Array(1,2,3,4,5,6,7),2)
val func = (x:Iterator[Int], y:Iterator[Int])=>x.toSeq.++(y.toSeq).toIterator
rdd1.zipPartitions(rdd2)(func).collect

 

3:zipWithIndex

给RDD中的每一个元素添加上索引号,组成二元组。索引号从0开始并且索引号类型是Long,当RDD分区大于1个时候需要出发一个Spark Job。

 

4:zipWithUniqueId

复制代码
var rdd1 = sc.makeRDD(Seq("A","B","C","D","E","F"),2)
//rdd1有两个分区,
rdd1.zipWithUniqueId().collect
 Array[(String, Long)] = Array((A,0), (B,2), (C,4), (D,1), (E,3), (F,5))
//总分区数为2
//第一个分区第一个元素ID为0,第二个分区第一个元素ID为1
//第一个分区第二个元素ID为0+2=2,第一个分区第三个元素ID为2+2=4
//第二个分区第二个元素ID为1+2=3,第二个分区第三个元素ID为3+2=5
复制代码

其实就是按照每一个的分区的每一个元素的顺序进行编号。这个算子不需要出发作业到集群运行。

5:union

RDD求并集操作,不会自动去重。

复制代码
res31: Array[Int] = Array(1, 2, 3, 4, 5, 6)

scala> rdd2.collect
collect   collectAsync

scala> rdd2.collect
res32: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)

scala> rdd1.union(rdd2).collect
collect   collectAsync

scala> rdd1.union(rdd2).collect
res34: Array[Int] = Array(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7)//不去重
复制代码

6:distinct

scala> unionRDD.collect
res38: Array[Int] = Array(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7)
scala> unionRDD.distinct.collect
res39: Array[Int] = Array(4, 1, 5, 6, 2, 3, 7)

实现去重。

 

7:treeReduce

treeReduce有点类似于reduce函数,也不需要传入初始值,只不过这个算子使用一个多层树的形式来进行reduce操作。

scala> rdd1.collect
res42: Array[Int] = Array(1, 2, 3, 4, 5, 6)

scala> rdd1.treeReduce((x,y)=>x+y)
res43: Int = 21

 

8:aggregate

复制代码
scala> rdd1.collect
res53: Array[Int] = Array(1, 2, 3, 4, 5, 6)

scala> rdd1.partitions.length
res54: Int = 2

scala> rdd1.aggregate(1)((x,y)=>x+y,(x,y)=>x+y)
res56: Int = 24

scala> rdd1.repartition(3).aggregate(1)((x,y)=>x+y,(x,y)=>x+y)
res57: Int = 25
复制代码

我们设置的聚集函数的ZeroValue值是1,这个值会每一个分区聚集时候使用一次,最后在聚集所有分区时候在使用一次。

我们这里面分区内部元素计算函数是:

(x,y)=>x+y

分区之间的聚集函数:

(x,y)=>x+y

由于rdd1默认是2个分区,所以在计算两个分区时候使用两次,相当于+1,最后合并两个分区时候有使用一次,相当于再加1.所以一共加3,,即:

1+2+3+4+5+6=21,21+3 =24.另一个只因为多一个分区,所以多累加1.

 

9:treeAggregate

和8中聚集算子效果一样,只不过使用的是树的层次结构聚集。

10:top

返回前面n个最大元素,可以定义排序规则

 

11:takeSample

def takeSample(withReplacement: Boolean, num: Int, seed: Long = Utils.random.nextLong): Array[T]

随机采样,抽取num个样例。可以指定是否重复抽取,随机数种子是一个生成随机数的初始条件,可以使用系统时间戳作为种子值。

当不允许重复抽取时候,num数目大于rdd元素数目不会报错,此时只会抽取rdd的所有元素。

 

12:takeOrdered

def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T]

抽取出num个个最小的元素,唯一和top区别就是top抽取大的,takeOrdered抽取小的。

 

13:take

def take(num: Int): Array[T]

返回num个数据,一般当数据较大的时候如果collect操作会导致Driver内存溢出,所以此时可以使用take携带少量数据到Driver。

 

14:subtract

def subtract(other: RDD[T]): RDD[T]

返回一个在当前RDD中且不在other中的元素所生成的RDD

 

15:sortBy

def sortBy[K](f: (T) ⇒ K, ascending: Boolean = true, numPartitions: Int = this.partitions.length)(implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]

例如:

复制代码
scala> rdd1.collect
res19: Array[Int] = Array(1, 2, 3, 4, 5)

scala> val rdd2 = rdd1.map(x=>(x,scala.util.Random.nextInt(100),scala.util.Random.nextInt(100)))
rdd2: org.apache.spark.rdd.RDD[(Int, Int, Int)] = MapPartitionsRDD[33] at map at <console>:26

scala> rdd2.collect
collect   collectAsync

scala> rdd2.collect
res20: Array[(Int, Int, Int)] = Array((1,87,34), (2,5,62), (3,51,60), (4,72,33), (5,33,23))

scala> res13.sortBy(x=>x._3).collect
res21: Array[(Int, Int, Int)] = Array((3,26,12), (4,45,28), (1,12,37), (2,71,67), (5,80,96))
复制代码

 

16:sample

def sample(withReplacement: Boolean, fraction: Double, seed: Long = Utils.random.nextLong): RDD[T]

随机采样,是否重复采样,抽取数据的百分比例。

17:repartition

def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]

重新创建一个只有numPartitions个分区的RDD,提高分区数或降低分区数会改变并行度,内部实现实现需要shuffle。如果需要降低RDD的分区数的话尽可能使用coalesce算子,它会避免shuffle的发生。

18:coalesce

def coalesce(numPartitions: Int, shuffle: Boolean = false, partitionCoalescer: Option[PartitionCoalescer] = Option.empty)(implicit ord: Ordering[T] = null): RDD[T]

降低原来RDD的分区数目到numPartitions个分区。例如由1000个分区降到100个分区的话,这样是一个窄依赖,因此不需要shuffle过程。

但是如果RDD原本有2个分区的话,当我们调用coalesce(5)的话,生成的RDD分区还将是2,不会增加,但是如果调用coalesce(1)的话,则会生成分区个数为1的RDD。(coalesce只会减少分区数,不会增加分区数)。

拓展:如果我们的RDD分区数为1的话,我们可以传递shuffle=true,当计算时候会进行shuflle分布到多个节点进行计算。

 

19:checkpoint

def checkpoint(): Unit

Mark this RDD for checkpointing. It will be saved to a file inside the checkpoint directory set with SparkContext#setCheckpointDir and all references to its parent RDDs will be removed. This function must be called before any job has been executed on this RDD. It is strongly recommended that this RDD is persisted in memory, otherwise saving it on a file will require recomputation.

 

20:cartesian

def cartesian[U](other: RDD[U])(implicit arg0: ClassTag[U]): RDD[(T, U)]

两个RDD生成笛卡尔积。

复制代码
scala> rdd1.collect
res37: Array[Int] = Array(1, 2, 3, 4, 5)

scala> rdd2.collect
res38: Array[Int] = Array(6, 7, 8, 9, 10)

scala> rdd1.cartesian(rdd2).collect
res39: Array[(Int, Int)] = Array((1,6), (1,7), (2,6), (2,7), (1,8), (1,9), (1,10), (2,8), (2,9), (2,10), (3,6), (3,7), (4,6), (4,7), (5,6), (5,7), (3,8), (3,9), (3,10), (4,8), (4,9), (4,10), (5,8), (5,9), (5,10))
复制代码

 

21:cache

def cache(): RDD.this.type

将RDD缓存,缓存级别为:MEMORY_ONLY

22:persist

def persist(): RDD.this.type

将RDD缓存,缓存级别为:MEMORY_ONLY

23:persist

def persist(newLevel: StorageLevel): RDD.this.type

指定缓存级别,在第一次被计算之后进行缓存。

24:keyBy

def keyBy[K](f: (T) ⇒ K): RDD[(K, T)]

根据函数f进行选取key,例如:

scala> rdd1.collect
res43: Array[Int] = Array(1, 2, 3, 4, 5)

scala> rdd1.keyBy(x=>x*x).collect
res44: Array[(Int, Int)] = Array((1,1), (4,2), (9,3), (16,4), (25,5))

25:intersection

def intersection(other: RDD[T]): RDD[T]

 

求两个RDD的交集

 

1:glom

def glom(): RDD[Array[T]]

将原RDD的元素收集到一个数组,创建一个数组类型的RDD

2:getNumPartitions

final def getNumPartitions: Int

求RDD的分区书

3:groupBy

def groupBy[K](f: (T) ⇒ K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]

根据指定函数进行分组,例如:

scala> rdd1.collect
res61: Array[Int] = Array(1, 2, 3, 4, 5)

scala> rdd1.groupBy(x=>if(x%2==0) 0 else 1).collect
res62: Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(4, 2)), (1,CompactBuffer(1, 3, 5)))

4:randomSplit

def randomSplit(weights: Array[Double], seed: Long = Utils.random.nextLong): Array[RDD[T]]

将一个RDD根据weights数组进行划分多个RDD,返回一个数组。

5:countByValue

返回每一个元素出现的次数,可以更加方便实现wordcount

scala> sc.parallelize(Array(1,2,1,2,1,2,3,4,5)).countByValue
res73: scala.collection.Map[Int,Long] = Map(5 -> 1, 1 -> 3, 2 -> 3, 3 -> 1, 4 -> 1)

6:countByValueApprox

def countByValueApprox(timeout: Long, confidence: Double = 0.95)(implicit ord: Ordering[T] = null): PartialResult[Map[T, BoundedDouble]]

求一个近似的计算结果

7:++

def ++(other: RDD[T]): RDD[T]

求RDD的并集

8:fold

def fold(zeroValue: T)(op: (T, T) ⇒ T): T

例如:

scala> rdd1.collect
res90: Array[Int] = Array(1, 2, 3, 4, 5)

scala> rdd1.fold(0)(_+_)
res91: Int = 15

键值对的算子讲解 PairRDDFunctions

 

1:groupByKey

def groupByKey(): RDD[(K, Iterable[V])]

根据key进行聚集,value组成一个列表,没有进行聚集,所以在有shuffle操作时候避免使用概算子,会增大通信数据量。需要考虑进行一个本地的Combiner,所以可以直接使用reduceByKey

复制代码
cala> p.collect
res15: Array[(Int, Int)] = Array((1,1), (2,1), (1,1), (2,1), (1,1), (2,1), (3,1), (4,1), (5,1))

scala> p.groupByKey.collect
res16: Array[(Int, Iterable[Int])] = Array((4,CompactBuffer(1)), (2,CompactBuffer(1, 1, 1)), (1,CompactBuffer(1, 1, 1)), (3,CompactBuffer(1)), (5,CompactBuffer(1)))
复制代码

2:cogroup

def cogroup[W](other: RDD[(K, W)], partitioner: Partitioner): RDD[(K, (Iterable[V], Iterable[W]))]

对每一个rdd先进行groupByKey,然后在对相同key的value在进行一个groupByKey,例如

复制代码
scala> p.collect
res18: Array[(Int, Int)] = Array((1,1), (2,1), (1,1), (2,1), (1,1), (2,1), (3,1), (4,1), (5,1))

scala> p.cogroup(p).collect
res19: Array[(Int, (Iterable[Int], Iterable[Int]))] = Array((4,(CompactBuffer(1),CompactBuffer(1))), (2,(CompactBuffer(1, 1, 1),CompactBuffer(1, 1, 1))), (1,(CompactBuffer(1, 1, 1),CompactBuffer(1, 1, 1))), (3,(CompactBuffer(1),CompactBuffer(1))), (5,(CompactBuffer(1),CompactBuffer(1))))
复制代码

例如:(1,1)在pair中出现三次,所以cogroup之后(1,1)的结果是:

(1,(CompactBuffer(1, 1, 1),CompactBuffer(1, 1, 1)))

 3:aggregateByKey

def aggregateByKey[U](zeroValue: U)(seqOp: (U, V) ⇒ U, combOp: (U, U) ⇒ U)(implicit arg0: ClassTag[U]): RDD[(K, U)]

说明:这个函数相对有点难懂,其他重载版本与此类似。该函数接受三个参数,一个初始值,两个函数:seqOp和comOp:

seqOp是对分区进行具体的函数,zeroValue值只有在seqOp中有使用,在第二个函数comOp中就不在使用了。

comOp是分区之间的combine函数(combine函数有点类似combiner的作用,进行聚集的函数)。

例如:

复制代码
    val data = sc.parallelize(List((1, 3), (1, 2), (1, 4), (2, 3), (2, 5)))
    def seq(a: Int, b: Int): Int = {
      println("seq: " + a + "\t " + b)
      math.max(a, b)
    }
    def comb(a: Int, b: Int): Int = {
      println("comb: " + a + "\t " + b)
      a + b
    }
复制代码

执行过程输出:

复制代码
seq: 1     3
seq: 1     2
seq: 1     4
seq: 1     3
seq: 1     5
comb: 3     2
comb: 5     4
comb: 3     5
复制代码

说明:在调用seqOp函数时候,每一次都向(key,value)中的value和zeroValue进行求最大值,将该最大值作为key的value。执行完毕seqOp函数的状态为:

((1,(3,2,4)),(2(3,5)))

然后调用comOp函数将每一个key的value进行累加,得到最后输出。

输出:

scala> data.aggregateByKey(1)(seq, comb).collect.toBuffer
res36: scala.collection.mutable.Buffer[(Int, Int)] = ArrayBuffer((2,5), (1,7))

 

4:combineByKey

def combineByKey[C](createCombiner: (V) ⇒ C, mergeValue: (C, V) ⇒ C, mergeCombiners: (C, C) ⇒ C): RDD[(K, C)]

参数说明:

  • createCombiner, which turns a V into a C (e.g., creates a one-element list)
  • mergeValue, to merge a V into a C (e.g., adds it to the end of a list)
  • mergeCombiners, to combine two C's into a single one.

例如:

复制代码
val data1 = sc.parallelize(List("a", "b", "c", "c", "b", "a", "b", "a"))
val data2 = sc.parallelize(List(1, 2, 3, 3, 2, 1, 2, 1))
val zip = data2.zip(data1)
val combineByKey = zip.combineByKey(List(_), (x: List[String], y: String) => y :: x, (x: List[String], y: List[String]) => x ::: y)
println(combineByKey.collect().toBuffer)
复制代码

输出:

ArrayBuffer((1,List(a, a, a)), (2,List(b, b, b)), (3,List(c, c)))

 

5:flatMapValues

def flatMapValues[U](f: (V) ⇒ TraversableOnce[U]): RDD[(K, U)]

传入一个键值对的值给 (V) ⇒ TraversableOnce[U]函数,返回的是一个集合的函数。将当前的key和当前集合中每一个元素组成元组返回

val a = sc.parallelize(List((1,2),(3,4),(5,6)))
val b = a.flatMapValues(x=>1 to x)
b.collect.foreach(println(_))

输出:

复制代码
(1,1)
(1,2)
(3,1)
(3,2)
(3,3)
(3,4)
(5,1)
(5,2)
(5,3)
(5,4)
(5,5)
(5,6)
复制代码

分析:当传入是2的时候,生成一个1 to 2 的序列,然后当前key=1,所以生成(1,1),(1,2)两个元组、

 6:foldByKey

def foldByKey(zeroValue: V)(func: (V, V) ⇒ V): RDD[(K, V)]

fold:折叠的意思,根据key进行折叠,例如:

scala> rdd.collect
res2: Array[String] = Array(hello, world, xiaomi, meizu, meizu)

scala> rdd.map((_,1)).foldByKey(1)(_+_).collect
res3: Array[(String, Int)] = Array((meizu,3), (hello,2), (world,2), (xiaomi,2))

此处zeroValue为1,由于meizu这个字符出现两次并加上zeroValue的话恰好是3。hello world xiaomi这些单词都是一次,并加上zeroValue恰好是2。

当zeroValue值为0的时候,我们可以实现一个wordcount。

 

7:mapValues

def mapValues[U](f: (V) ⇒ U): RDD[(K, U)]

对键值对的每一个value进行处理,key保持不变。例如:我对现有的键值对的值进行乘以10的操作

scala> rdd.map((_,1)).collect
res14: Array[(String, Int)] = Array((hello,1), (world,1), (xiaomi,1), (meizu,1), (meizu,1))

scala> rdd.map((_,1)).mapValues(x=>x*10).collect
res15: Array[(String, Int)] = Array((hello,10), (world,10), (xiaomi,10), (meizu,10), (meizu,10))

8:rightOuterJoin

def rightOuterJoin[W](other: RDD[(K, W)], numPartitions: Int): RDD[(K, (Option[V], W))]

右外连接,other中的所有key都会出现在结果中,关联到的是一个Some值,关联不到的是一个None。例如:

复制代码
scala> rdd1.collect
res26: Array[(Int, Int)] = Array((1,1), (2,1), (3,1), (4,1), (5,1))

scala> rdd2.collect
res27: Array[(Int, Int)] = Array((3,2), (4,2), (5,2), (6,2), (7,2), (8,2))

scala> rdd1.rightOuterJoin(rdd2).collect
res28: Array[(Int, (Option[Int], Int))] = Array((4,(Some(1),2)), (6,(None,2)), (8,(None,2)), (3,(Some(1),2)), (7,(None,2)), (5,(Some(1),2)))
复制代码

9:leftOuterJoin

def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]

同上面右外连接。示例:

复制代码
scala> rdd1.collect
res31: Array[(Int, Int)] = Array((1,1), (2,1), (3,1), (4,1), (5,1))

scala> rdd2.collect
res32: Array[(Int, Int)] = Array((3,2), (4,2), (5,2), (6,2), (7,2), (8,2))

scala> rdd1.leftOuterJoin(rdd2).collect
res33: Array[(Int, (Int, Option[Int]))] = Array((4,(1,Some(2))), (2,(1,None)), (1,(1,None)), (3,(1,Some(2))), (5,(1,Some(2))))
复制代码

rdd中的所有的key都会出现,关联的不到的为None,此时关联的值在value的第二个位置。我们可以通过交换右外连接的两个rdd的位置,实现左外连接,但是区别在于value中元素的位置是逆序的:

scala> rdd2.rightOuterJoin(rdd1).collect //通过右外连接实现左外连接
res34: Array[(Int, (Option[Int], Int))] = Array((4,(Some(2),1)), (2,(None,1)), (1,(None,1)), (3,(Some(2),1)), (5,(Some(2),1)))
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YiRan_Zhao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值