Spark RDD API 参考示例(一)

本文参考Zhen He

1、aggregate

原型
def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U

含义
aggregate是一个聚合函数,一个RDD分区后,产生多个Partition,在aggregate中需要指定两个处理函数,第一个函数用于对每个分区内部处理,第二个函数用于分区之间的处理。aggregate 的初始值作用于前后两个部分, 分区是严格按照顺序的,顺序不同aggregate 结果就不同。

示例

//分区顺序严重aggregate的结果,分区特点,先平均分配,多的依次放到序号大的分区

val z = sc.parallelize(List(1,2,3,4,5,6), 2)
//使用parallelize产生2个分区,可以使用mapPartitionWithIndex查看分区
//分区结果是(1,2,3)和(4,5,6)
z.aggregate(0)(math.max,_+_)
res0: Int = 9  

特别注意:初始值 0 是加在分区的左边
//第一个参数0 表示将0放入到每个分区中,然后每个分区进行计算。每个分区之间汇总时,再次将0放入其中计算
//math.max表示对每个分区内部取最大值,第一个分区有(1,2,3),再加上0,所以是(0,1,2,3)取最大值
//_+_表示每个分区之间进行相加

val x = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)
x.aggregate(5)(math.max,_+_)
res1: Int = 25

//分析三个分区为(1,2,3),(4,5,6),(7,8,9)其中aggregate的初始值为5
//每次分区计算数据为(5,1,2,3),(5,4,5,6),(5,7,8,9),分别取最大值,得到(5,6,9)
//分区之间使用 _+_ 计算时,再次将5,放入其中,所以变成(5,5,6,9)
//最后将其相加变成5+5+6+9=25

//字符串类型处理
val z = sc.parallelize(List("a","b","c","d","e","f"),2)
z.aggregate("x")(_ + _, _+_)
res2: String = xxdefxabc
//第一个分区集合中有("x","a","b","c") 分区内部的聚合结果为"xabc"
//由于是并行的聚合,所以"xabc"和"xdef"谁在前面和后面并不知道
//分区之间进行聚合时,集合变成了("x","xabc","xdef")

//两个高级的示例
val z = sc.parallelize(List("12","23","345","4567"),2)
z.aggregate("")((x,y) => math.max(x.length, y.length).toString, _+_)
res3: String = 42
//分为两个区("12","23")和("345","4567")
//由于需要使用两个函数,所以这里定义了一个闭包函数,集合中的每两个元素,取出最大的长度。
//每个分区进行比较时集合为("","12","34")、("","345","4567")
//每个分区聚合之后的结果为("2","4")
//分区之间进行聚合时,集合为("","2","4"),再次聚合结构可能是"24"也可能是"42"

val z = sc.parallelize(List("12","23","345","4567"),2)
z.aggregate("asd")((x,y) => math.min(x.length, y.length).toString, _+_)
res4: String = asd11
//分区后的结果为("12","23")  ("345","456")
//分区内部应用初始值之后的结果为("asd","12","34")   ("asd","345","456")
//每个分区中有三个元素,每个分区内部需要多次聚合,分为("asd","12")和("34")
//("asd","12")聚合结果是"2", 再与("34")聚合结果是"1"
//所以("asd","12","34") 聚合结果是"1"
//同理("asd","345","456") 聚合结果是"1"
//两个分区之间再次聚合,将初始值应用到其中,得到"asd11"

val z = sc.parallelize(List("12","23","345",""),2)
z.aggregate("")((x,y) => math.min(x.length, y.length).toString,_+_)
res5: String = 10
//分区后的结果为("12","23") ("345","")
//分区内部应用初始值之后的结果为("","12","23") ("","345","")
//有三个元素,需要多次聚合("","12")  ("23")
//("","12")聚合结果是"0"  再与("23")聚合,得到的结果是"1"
//("","345")聚合结果是"0"  再与("")聚合,得到的结果是"0"
//最后两次结果相加,得到的是"10"

特别注意:为了说明分区顺序的重要性,请看下面的例子
val z = sc.parallelize(List("12","23","","345"),2)
z.aggregate("")((x,y) => math.min(x.length, y.length).toString,_+_)
res6: String = 11
//分区后的结果为("12","23") ("","345")
//分区内部应用初始值之后的结果为("","12","23") ("","","345")
//有三个元素,需要多次聚合("","12")  ("23")
//("","12")聚合结果是"0"  再与("23")聚合,得到的结果是"1"
//("","")聚合结果是"0"  再与("345")聚合,得到的结果是"1"
//最后两次结果相加,得到的是"11"

2、aggregateByKey[Pair]

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

含义
aggregateByKey 也是一个聚合函数,一个RDD分区后,产生多个Partition,在aggregateByKey中需要指定两个处理函数,第一个函数用于对每个分区内部处理,第二个函数用于分区之间的处理。aggregateByKey 的初始值只作用于每个分区内部,不影响分区之间聚合,分区是严格按照顺序的,顺序不同aggregateByKey 结果就不同。

示例

val z = sc.parallelize(List( ("cat",2), ("cat", 5), ("pig", 4),("cat", 12), ("dog", 12)), 2)
z.aggregateByKey(0)(math.max(_, _), _ + _).collect
res1: Array((dog,12), (pig,4), (cat,17))  
//分区之后的结果是(("cat",2),("cat",5))   (("pig",4),("cat",12),("dog",12))
//应用aggregate的初始值后,第二个分区为: 
//([("pig",0),("pig","4")],[("cat",0),("cat",12)],[("dog",0),("dog",12)])
//使用math.max函数后,结果为(("pig","4"),("cat",12),("dog",12))
//如果使用math.min函数后,结果就为(("pig",0),("cat",0),("dog",0))
//使用函数_+_聚合时,不会应用初始值

z.aggregateByKey(100)(math.max(_, _), _ + _).collect
res2: Array((dog,100), (pig,100), (cat,200))

3、cartesian

原型
def cartesian[U: ClassTag](other: RDD[U]): RDD[(T, U)]

含义
cartesian 笛卡尔积,两个RDD中的元素两两组合

示例

val x = sc.parallelize(List(1,2,3,4,5))
val y = sc.parallelize(List(6,7,8,9,10))
x.cartesian(y).collect
//结果是两者由小到大的顺序排列
res0: Array[(Int, Int)] = Array((1,6), (1,7), (1,8), (1,9), (1,10), (2,6), (2,7), (2,8), (2,9), (2,10), (3,6), (3,7), (3,8), (3,9), (3,10), (4,6), (5,6), (4,7), (5,7), (4,8), (5,8), (4,9), (4,10), (5,9), (5,10))

4、checkpoint

原型
def checkpoint()

含义
checkpoint 检查点机制,假设你在迭代1000次的计算中在第999次失败了,然后你没有checkpoint,你只能重新开始恢复了,如果恰好你在第998次迭代的时候你做了一个checkpoint,那么你只需要恢复第998次产生的rdd,然后再执行2次迭代完成总共1000的迭代,这样效率就很高,比较适用于迭代计算非常复杂的情况,也就是恢复计算代价非常高的情况,适当进行checkpoint会有很大的好处。

示例

sc.setCheckpointDir("hdfs://192.168.10.71:9000/wc")
//检查点目录必须存在
val a = sc.parallelize(1 to 5)
a.checkpoint
//将其结果检查点更新
a.collect
res1: Long = 5

5、coalesce, repartition

原型
def coalesce ( numPartitions : Int , shuffle : Boolean = false ): RDD [T]
def repartition ( numPartitions : Int ): RDD [T]

含义
repartition 表示对已经分区的数据重新分区,是coalesce 的一个简单应用

示例

val y = sc.parallelize(1 to 10, 5)
y.coalesce(2, false).collect
res1: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
//coalesce分区中false表示直接分区,不对其重新打乱顺序

val z = y.coalesce(2, true)
val x = y.repartition(2)
z.collect
res2: Array[Int] = Array(3, 5, 9, 1, 7, 2, 8, 4, 6, 10)
//coalesce分区中false表示重新打乱顺序,再分区,和repartition相等,默认是不打乱顺序的

6、cogroup [Pair], groupWith [Pair]

原型
def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
def groupWith[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
含义
cogroup 超级分组,可以将3个key-value类型的RDD进行分组

示例

//3个key-value类型的RDD分组聚合
val x = sc.parallelize(List((1, "apple"), (2, "banana"), (3, "orange"), (4, "kiwi")), 2)
val y = sc.parallelize(List((5, "computer"), (1, "laptop"), (1, "desktop"), (4, "iPad")), 2)
val z = sc.parallelize(List((3,"iphone"),(1,"xiaomi"),(4,"huawei")),2)
x.cogroup(y,z).collect

res1: Array((4,(CompactBuffer(kiwi),CompactBuffer(iPad),CompactBuffer(huawei))), (2,(CompactBuffer(banana),CompactBuffer(),CompactBuffer())), (1,(CompactBuffer(apple),CompactBuffer(laptop, desktop),CompactBuffer(xiaomi))), (3,(CompactBuffer(orange),CompactBuffer(),CompactBuffer(iphone))), (5,(CompactBuffer(),CompactBuffer(computer),CompactBuffer())))

//分析:如果x中不存在这个元素,那么就会将其值置为空

7、collect, toArray

原型
def collect[U: ClassTag](f: PartialFunction[T, U]): RDD[U]
def toArray(): Array[T]
含义
collect 将RDD数据转换成Scala中的数组并返回

示例


val c = sc.parallelize(List("Gnu", "Cat", "Rat", "Dog", "Gnu", "Rat"), 2)
c.collect
res1: Array[String] = Array(Gnu, Cat, Rat, Dog, Gnu, Rat)

8、collectAsMap

原型
def collectAsMap(): Map[K, V]

含义
collectAsMap 和collect非常类似,但是,是将数据转换成key-value类型的Map

示例


val a = sc.parallelize(List(1, 2, 1, 3), 1)
val b = a.zip(a)
//以List中的数字作为key,出现次数作为value
b.collectAsMap
res1: scala.collection.Map[Int,Int] = Map(2 -> 2, 1 -> 1, 3 -> 3)

9、combineByKey[Pair]

原型
def combineByKey[C](createCombiner: V => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C): RDD[(K, C)]

含义
combineByKey 高效的实现按照key来聚合,通过在每一个分区内部先聚合,再在分区之间聚合。每个分区内部是通过迭代方式来聚合,效率非常高。

示例

val a = sc.parallelize(List("dog","cat","gnu","salmon","rabbit","turkey","wolf","bear","bee"), 3)
val b = sc.parallelize(List(1,1,2,2,2,1,2,2,2), 3)
val c = b.zip(a)
//将数据分成三个区,使用数字当作key,字符串作为value

val d = c.combineByKey(List(_), (x:List[String], y:String) => y :: x, (x:List[String], y:List[String]) => x ::: y)
//combineByKey的三个参数的意思是:
//第一个分区为((1,"dog"), (1,"cat"), (2,"gnu"))
//List(_) 每个分区中的key,每次取该分区中的一个key
//第一次取1,初始时x中的List为空,找到第一个key为1的dog,将dog放入到x中的List,然后再找到cat
//第二次取2,初始时x中的List为空,找到第一个key为2的gnu
//第一个分区得到了((1,("cat","dog")),(2,("gnu")))
//同理,第二个分区得到了((2,("salmon","rabbit")),(1,("turkey")))
//同理,第三个分区得到了(2,("wolf","bear","bee"))
//最后,每个分区之间进行合并,将相同的key的数据合并

d.collect
res1: Array[(Int, List[String])] = Array((1,List(cat, dog, turkey)), (2,List(gnu, rabbit, salmon, bee, bear, wolf)))

10、context, sparkContext

原型
def compute(split: Partition, context: TaskContext): Iterator[T]

含义
context 返回创建RDD时的SparkContext

示例

val c = sc.parallelize(List("Gnu", "Cat", "Rat", "Dog"), 2)
c.context
res1: org.apache.spark.SparkContext = org.apache.spark.SparkContext@74c08828

11、count

原型
def count(): Long

含义
count 返回RDD中存储的元素的个数

示例

val c = sc.parallelize(List("Gnu", "Cat", "Rat", "Dog"), 2)
c.count
res2: Long = 4

12、countApproxDistinct

原型
def countApproxDistinct(relativeSD: Double = 0.05): Long

含义
countApproxDistinct 近似统计RDD中不重复的值的个数,可以通过relativeSD 来确定统计精度,这种方式适合大规模,分布式的数据快速统计

示例

val z = sc.parallelize(List(1,3,4,1,2,3))
z.countApproxDistinct(0.01)
res1: Long = 4
//countApproxDistinct统计的是去掉重复的数值

val a = sc.parallelize(1 to 10000, 20)
val b = a++a++a++a++a
b.countApproxDistinct(0.1)
res2: Long = 8224

b.countApproxDistinct(0.05)
res3: Long = 9750

b.countApproxDistinct(0.01)
res4: Long = 9947

b.countApproxDistinct(0.001)
res5: Long = 10000

13、countApproxDistinctByKey [Pair]

原型
def countApproxDistinctByKey(relativeSD: Double = 0.05): RDD[(K, Long)]

含义
countApproxDistinctByKeycountApproxDistinct 类似,但是他是统计元组中的相同的key的个数,相比于其他统计方式,这种统计速度更快

示例

val a = sc.parallelize(List("Gnu", "Cat", "Rat", "Dog"), 2)
val b = sc.parallelize(a.takeSample(true, 100, 0), 5)
//产生100个随机的,从a中选取的字符,并分为5个区

val c = sc.parallelize(1 to b.count().toInt, 5)
//产生1-100的数组,并分为5个区

val d = b.zip(c)
//将其转换为map类型
//按照key来进行统计
d.countApproxDistinctByKey(0.1).collect
res1: Array((Rat,27), (Cat,25), (Dog,29), (Gnu,24))

d.countApproxDistinctByKey(0.01).collect
res2:  Array((Rat,27), (Cat,24), (Dog,27), (Gnu,22))

14、countByKey [Pair]

原型
def countByKey(): Map[K, Long]

含义
countByKeycount 类似,但是他是用于统计元组中每个相同的key出现的次数,最后返回的是一个元组

示例

val c = sc.parallelize(List((3, "Gnu"), (3, "Yak"), (5, "Mouse"), (3, "Dog")), 2)
c.countByKey
res1: scala.collection.Map[Int,Long] = Map(3 -> 3, 5 -> 1)

15、countByValue [Pair]

原型
def countByValue(): Map[T, Long]

含义
countByValue 计算RDD中不同值出现的次数,以value作为统计的标准。分区统计时,这个操作会将信息合并到单个reduce中去。

示例


val b = sc.parallelize(List(1,2,3,4,5,6,7,8,2,4,2,1,1,1,1,1))
b.countByValue
res1: Map(5 -> 1, 8 -> 1, 3 -> 1, 6 -> 1, 1 -> 6, 2 -> 3, 4 -> 2, 7 -> 1)
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值