1、map
map是对RDD中的每个元素都执行一个指定的函数(func 传进来的逻辑)处理之后来产生一个新的RDD(注意:不是每个元素产生一个新的RDD,而是一起产生一个新的RDD)。任何原RDD中的元素在新RDD中都有且只有一个元素与之对应,即一一对应。Map传给func 的参数是每个元素。由于简单理解不写代码了
2、flatMap算子:flatMap(func)
类似于map,但是每一个输入元素,会被映射为0到多个输出元素(因此,func函数的返回值是一个Seq,而不是单一元素),将数据扁平化,最后映射结果整合。
val list = List("hello A","hello B","hello C")
val rdd = sc.parallelize(list)
val rdd2 = rdd.flatMap(x = > x.split(" ")),collect().froeach(x=>println(x))
输出为:
hello
A
hello
B
hello
C
2)flatMapValues——对所有Value进行flatMap
scala> val rdd = sc.parallelize(List("a", "boy"), 1).keyBy(_.length) rdd: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[44] at keyBy at <console>:24 scala> rdd.collect res32: Array[(Int, String)] = Array((1,a), (3,boy)) scala> rdd.flatMapValues(x=>"*" + x + "*").collect res33: Array[(Int, Char)] = Array((1,*), (1,a), (1,*), (3,*), (3,b), (3,o), (3,y), (3,*))
3、mapPartitions算子
map是对每个元素操作, mapPartitions是对其中的每个partition操作,即map传给处理函数的是每个元素,而mapPartition传给处理函数的是一个分区的元素的迭代器(Iterator) ,mapPartitions是map的一个变种。map的输入函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区,也就是把每个分区中的内容作为整体来处理的。
该函数和map函数类似,只不过映射函数的参数由RDD中的每一个元素变成了RDD中每一个分区的迭代器。如果在映射的过程中需要频繁创建额外的对象,使用mapPartitions要比map高效的多。该算子主要是理解,其使用和map一样.
4、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)//不去重
5、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)
6、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
7、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.
8、treeAggregate
和7中聚集算子效果一样,只不过使用的是树的层次结构聚集。
9、top
返回前面n个最大元素,可以定义排序规则
10、takeSample
def takeSample(withReplacement: Boolean, num: Int, seed: Long = Utils.random.nextLong): Array[T]
随机采样,抽取num个样例。可以指定是否重复抽取,随机数种子是一个生成随机数的初始条件,可以使用系统时间戳作为种子值。
当不允许重复抽取时候,num数目大于rdd元素数目不会报错,此时只会抽取rdd的所有元素。
11、takeOrdered
def takeOrdered(num: Int)(implicit ord: Ordering[T]): Array[T]
抽取出num个个最小的元素,唯一和top区别就是top抽取大的,takeOrdered抽取小的。
12、take
def take(num: Int): Array[T]
返回num个数据,一般当数据较大的时候如果collect操作会导致Driver内存溢出,所以此时可以使用take携带少量数据到Driver。
13、subtract
def subtract(other: RDD[T]): RDD[T]
返回一个在当前RDD中且不在other中的元素所生成的RDD
- scala> var rdd1 = sc.makeRDD(Seq(1,2,2,3))
- rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[66] at makeRDD at :21
- scala> rdd1.collect
- res48: Array[Int] = Array(1, 2, 2, 3)
- scala> var rdd2 = sc.makeRDD(3 to 4)
- rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[67] at makeRDD at :21
- scala> rdd2.collect
- res49: Array[Int] = Array(3, 4)
- scala> rdd1.subtract(rdd2).collect
- res50: Array[Int] = Array(1, 2, 2) // 返回的是 1 2 2
14、sortBy
对rdd进行排序,参数有3个,第一个参数是一个函数,该函数的也有一个带T泛型的参数,返回类型和RDD中元素的类型是一致的;第二个升序还是降序,第三个排序后的分区
scala> val data = List(3,1,90,3,5,12)
data: List[Int] = List(3, 1, 90, 3, 5, 12)
scala> val rdd = sc.parallelize(data)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:14
scala> rdd.collect
res0: Array[Int] = Array(3, 1, 90, 3, 5, 12)
scala> rdd.sortBy(x => x).collect
res1: Array[Int] = Array(1, 3, 3, 5, 12, 90)
scala> rdd.sortBy(x => x, false).collect
res3: Array[Int] = Array(90, 12, 5, 3, 3, 1)
scala> val result = rdd.sortBy(x => x, false)
result: org.apache.spark.rdd.RDD[Int] = MappedRDD[23] at sortBy at <console>:16
scala> result.partitions.size
res9: Int = 2
scala> val result = rdd.sortBy(x => x, false, 1)
result: org.apache.spark.rdd.RDD[Int] = MappedRDD[26] at sortBy at <console>:16
scala> result.partitions.size
res10: Int = 1 // 分区为1
15、sample
def sample(withReplacement: Boolean, fraction: Double, seed: Long = Utils.random.nextLong): RDD[T]
对RDD进行抽样,其中参数withReplacement为true时表示抽样之后还放回,可以被多次抽样,false表示不放回;fraction表示抽样比例;seed为随机数种子,比如当前时间戳
16、repartition
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
重新创建一个只有numPartitions个分区的RDD,提高分区数或降低分区数会改变并行度,内部实现实现需要shuffle。如果需要降低RDD的分区数的话尽可能使用coalesce算子,它会避免shuffle的发生。
17、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分布到多个节点进行计算。
18、checkpoint
设置检查点,意思就是将某个重要的rdd结果放入内存或者磁盘上,防止计算的时候中间结果突然丢失了,又要从头开始计算,节省性能。在checkpoint的时候强烈建议先进行cache,并且当你checkpoint执行成功了,那么前面所有的RDD依赖都会被销毁。
19、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))
20、cache
def cache(): RDD.this.type
将RDD缓存,缓存级别为:MEMORY_ONLY
21、persist
def persist(): RDD.this.type
将RDD缓存,缓存级别为:MEMORY_ONLY
22、persist
def persist(newLevel: StorageLevel): RDD.this.type
指定缓存级别,在第一次被计算之后进行缓存。
23、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))
24、intersection
def intersection(other: RDD[T]): RDD[T]
求两个RDD的交集
25、groupByKey
groupByKey([numTasks])是数据分组操作,在一个由(K, V)键值对组成的数据集上调用,返回一个(K, Seq[V])对的数据集。
scala> val rdd0 = sc.parallelize(Array((1,1), (1, 2), (1,3), (2, 1), (2, 2), (2, 3)), 3) //rdd0: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[5] at parallelize at <console>:27
scala> val rdd1 = rdd0.groupByKey() //rdd1: org.apache.spark.rdd.RDD[(Int, Iterable[Int])] = ShuffledRDD[6] at groupByKey at <console>:29
scala> rdd1.collect //Array[(Int, Iterable[Int])] = Array((1,CompactBuffer(1, 2, 3)), (2,CompactBuffer(1, 2, 3)))
groupByKey的使用:
val words = Array("one", "two", "two", "three", "three", "three")
val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
val wordCountsWithReduce = wordPairsRDD.reduceByKey(_ + _)
val wordCountsWithGroup = wordPairsRDD.groupByKey().map(t => (t._1, t._2.sum))
25、pipe
在Linux系统中,有许多对数据进行处理的shell命令,我们可能通过pipe变换将一些shell命令用于Spark中生成新的RDD。
scala> val rdd = sc.parallelize(0 to 7, 4) rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[11] at parallelize at <console>:24
scala> rdd.glom.collect res20: Array[Array[Int]] = Array(Array(0, 1), Array(2, 3), Array(4, 5), Array(6, 7))
scala> rdd.pipe("head -n 1").collect #提取每一个分区中的第一个元素构成新的RDD
res21: Array[String] = Array(0, 2, 4, 6)
26、zip
生成由原始RDD的值为Key,另一个RDD的值为Value依次配对构成的所有Key/Value对,并返回这些Key/Value对集合构成的新RDD
27、combineByKey(不好理解)
combineByKey是Spark中一个比较核心的高级函数,其他一些高阶键值对函数底层都是用它实现的。诸如 groupByKey,reduceByKey等等
解释下3个重要的函数参数:
- createCombiner: V => C ,这个函数把当前的值作为参数,此时我们可以对其做些附加操作(类型转换)并把它返回 (这一步类似于初始化操作)
- mergeValue: (C, V) => C,该函数把元素V合并到之前的元素C(createCombiner)上 (这个操作在每个分区内进行)
- mergeCombiners: (C, C) => C,该函数把2个元素C合并 (这个操作在不同分区间进行)
combineByKey来求解平均数的例子
val initialScores = Array(("Fred", 88.0), ("Fred", 95.0), ("Fred", 91.0), ("Wilma", 93.0), ("Wilma", 95.0), ("Wilma", 98.0))
val d1 = sc.parallelize(initialScores)
type MVType = (Int, Double) //定义一个元组类型(科目计数器,分数)
d1.combineByKey(
score => (1, score),
(c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore),
(c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2)
).map { case (name, (num, socre)) => (name, socre / num) }.collect
参数含义的解释
a 、score => (1, score),我们把分数作为参数,并返回了附加的元组类型。 以"Fred"为列,当前其分数为88.0 =>(1,88.0) 1表示当前科目的计数器,此时只有一个科目
b、(c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore),注意这里的c1就是createCombiner初始化得到的(1,88.0)。在一个分区内,我们又碰到了"Fred"的一个新的分数91.0。当然我们要把之前的科目分数和当前的分数加起来即c1._2 + newScore,然后把科目计算器加1即c1._1 + 1
c、 (c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2),注意"Fred"可能是个学霸,他选修的科目可能过多而分散在不同的分区中。所有的分区都进行mergeValue后,接下来就是对分区间进行合并了,分区间科目数和科目数相加分数和分数相加就得到了总分和总科目数
执行结果如下:
res1: Array[(String, Double)] = Array((Wilma,95.33333333333333), (Fred,91.33333333333333))
28、keys
将Key/Value型RDD中的元素的Key提取出来,所有Key值构成一个序列形成新的RDD。
scala> val pairs = sc.parallelize(List("wills", "aprilchang","kris"),1).keyBy(_.length)
pairs: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[47] at keyBy at <console>:24
scala> pairs.collect
res34: Array[(Int, String)] = Array((5,wills), (10,aprilchang), (4,kris))
scala> pairs.keys.collect
res35: Array[Int] = Array(5, 10, 4)
29、values
将Key/Value型RDD中的元素的value提取出来,所有value值构成一个序列形成新的RDD。
30、mapValues——对Value值进行变换
对键值对每个value都应用一个函数,但是,key不会发生变化。
val list = List("hadoop","spark","hive","spark") val rdd = sc.parallelize(list) val pairRdd = rdd.map(x => (x,1)) pairRdd.mapValues(_+1).collect.foreach(println)//对每个value进行+1
31、partitionBy——按Key值重新分区
该函数根据key按照partitioner函数生成新的ShuffleRDD,将原RDD重新分区。
scala> val pairs = sc.parallelize(0 to 9, 2).keyBy(x=>x)
pairs: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[51] at keyBy at <console>:24 scala> pairs.collect res37: Array[(Int, Int)] = Array((0,0), (1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,9))
scala> import org.apache.spark.HashPartitioner
import org.apache.spark.HashPartitioner
scala> val partitionPairs = pairs.partitionBy(new HashPartitioner(2)) #按每个key的Hash值进行分区的
partitionPairs: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[52] at partitionBy at <console>:27
scala> partitionPairs.glom.collect res38: Array[Array[(Int, Int)]] = Array(Array((0,0), (2,2), (4,4), (6,6), (8,8)), Array((1,1),
32、reduceByKey——按Key值进行Reduce操作
概算子比较常用,故不写代码
33、sortByKey——按Key值排序
34、cogroup——按Key值聚合
scala> val rdd0 = sc.parallelize(Array((1,1), (1,2) , (1,3) , (2,1) , (2,2)), 3)
scala> val rdd2 = rdd0.cogroup(rdd0)
scala> rdd2.collect
res1: Array[(Int, (Iterable[Int], Iterable[Int]))] = Array((1,(CompactBuffer(1, 2, 3),CompactBuffer(1, 2, 3))), (2,(CompactBuffer(1, 2),CompactBuffer(1, 2))))
35、join——按Key值联结(内连接)
join方法是对两个需要连接的RDD进行内连接操作,然后对每个key下的元素进行笛卡儿积操作,返回的结果再展平。
val rdd1 = sc.parallelize(Array(("aa",1),("bb",2)))
val rdd2 = sc.parallelize(Array(("aa",3),("dd",1)))
val joincl = rdd1.join(rdd2)
val joincl2= joincl.collect()
for (i <- 0 to joincl2.length-1){
println(joincl2(i))
}
结果为:(aa,(1,3))
36、leftOuterJoin——按Key值进行左外联结
37、rightOuterJoin——按Key值进行右外联结
38、subtractByKey——按Key值求补
删掉 RDD 中键与 other RDD 中的键相同的元素
rdd=[(1,3),(3,4),(3,6)]
other=[(3,9)]
rdd.subtractByKey(other) // key相等的删除
结果:(1,3)
spark的转换算子先写到这里,后续再更新。