Spark---RDD常用方法

前言:RDD算子分为Transformation(转换算子)、Action(行动算子)
转换算子—lazy—只记录操作,只有遇到行动算子才会真正的计算
行动算子—non-lazy
本质上动作算子通过SparkContext执行提交作业操作,触发RDD DAG(有向无环图)的执行

val rdd1 = sc.makeRDD(1 to 10)
val rdd2 = sc.parallelize(5 to 15)
val rdd3 = sc.makeRDD(Array(2,4,4,9,10,-2))
val rdd4 = sc.makeRDD(Array("hello","scala","python","helloWorld","javascript"))

++

两个RDD的组合,和Union作用一样

aggregate

和Scala中的一样

rdd1.aggregate(0)(_+_,_+_).collect

cartesian — 笛卡尔

rdd1.cartesian(rdd2).foreach(println)

coalesce — 分区

小—>大 :true; 大—>小 :false

rdd1.coalesce(3,true)

collect

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

checkpoint — 检查点

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

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

context

和SparkContext一样返回创建RDD时候的SparkContext

scala> rdd3.context
res2: org.apache.spark.SparkContext = org.apache.spark.SparkContext@41aa99fc
scala> rdd3.sparkContext
res3: org.apache.spark.SparkContext = org.apache.spark.SparkContext@41aa99fc

sparkContext

同上

count — 统计个数

print(rdd1.count)

countByValue — 统计一个key对应的多个value个数

rdd3.countByValue
res101: scala.collection.Map[Int,Long] = Map(5 -> 1, 1 -> 1, 6 -> 1, 2 -> 1, 3 -> 1, 4 -> 1)

dependencies—依赖

查看当前RDD是否有依赖,可以帮助恢复重建RDD
1、没有依赖

rdd3.dependencies
res103: Seq[org.apache.spark.Dependency[_]] = List()

2、有一个依赖

rdd3.map(_+2)
res104: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[75] at map at <console>:26
res104.dependencies
res105: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.OneToOneDependency@27917a6b)

distinct — 去重

rdd3.distinct().foreach(println)

filter — 过滤

rdd3.filter(_%2==0).foreach(println)

first — 第一个元素

print(rdd3.first)

flatMap — 扁平化 同Scala

操作类似map,但是其粒度更小

fold — 同Scala

有初始值,只有一个函数

foreach — 遍历

foreachAsync — 异步遍历

foreachPartition — 一个分区执行一次遍历

foreachPartitionAsync —同理异步

getNumPartitions — 获取分区数

print(rdd3.getNumPartitions)

partitions.size — 获取分区数

同上

getStorageLevel — 存储等级

rdd3.getStorageLevel
res102: org.apache.spark.storage.StorageLevel = StorageLevel(1 replicas)

glom — 合并

将RDD的每一个分区作为一个单独的包装

val rg = rdd3.groupBy(_%2)
rg.glom.foreach(x=>println(x.mkString(",")))

groupBy — 分组

返回的是分区号和迭代器

rdd3.groupBy(_%2).collect
res15: Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(2, 4, 4, 10, -2)), (1,CompactBuffer(9)))

groupByKey — 按key分组

写wordcount的时候有用到,不需要参数,

rdd4.map(x=>(x.length,x))
res17: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[17] at map at <console>:26

res17.groupByKey.collect
res19: Array[(Int, Iterable[String])] = Array((6,CompactBuffer(python)), (10,CompactBuffer(helloWorld, javascript)), (5,CompactBuffer(hello, scala)))

histogram - - 直方图

输入的参数buckets可以是一个数字,也可以是一个列表
输出结果为一个元组,元组包含两个列表分别是桶(直方图的边界)和直方图的频数
【注意】:
1、桶必须是排好序的,并且不包含重复元素,至少有两个元素
2、所有直方图的结果集合区右边是开区间,最后一个区间除外。
计算方式 : 一、参数buckets是一个数字的情况: 根据桶的总数来计算
先用排好序的数组的边界值来得出两个桶之间的间距 (9.0-1.1)/5 = 1.58
所以得到第一个元组(直方图的边界) 1.1-2.68 2.68-4.26 以此类推
然后计算每个桶中的频数 : 数组中的数字在桶中的分布情况, 位于第一个区间(1.1~2.68) 中的数字有 1.1、1.2、1.3、2.0、2.1 一共5个 对应第二个数组中的第一个数字

val a = sc.parallelize(List(1.1,1.2,1.3,2.0,2.1,7.4,7.5,7.6,8.8,9.0),3)
a.histogram(5)
res0:(Array[Double], Array[Long]) = (Array(1.1, 2.68, 4.26, 5.84, 7.42, 9.0),Array(5, 0, 0, 1, 4))

id — 编号

返回RDD的编号

scala> rdd4.id
res20: Int = 11

scala> rdd3.id
res21: Int = 10

intersection — 交集

返回两个集合中相同元素

scala> rdd1.collect
res22: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> rdd2.collect
res23: Array[Int] = Array(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)

rdd2.intersection(rdd1).collect
res25: Array[Int] = Array(6, 7, 8, 9, 10, 5)

isCheckpointed

检查一个RDD是否有检查点,返回true 或 false

isEmpty

判断RDD是否为空

keyBy

指定一个函数特定产生的数据作为RDD的key

scala> rdd4.collect
res28: Array[String] = Array(hello, scala, python, helloWorld, javascript)

scala> rdd4.keyBy(_.length).collect
res29: Array[(Int, String)] = Array((5,hello), (5,scala), (6,python), (10,helloWorld), (10,javascript))

keys

获取RDD中的元组的key,key可以重复出现,List类型可以使用

l1.keyBy(_.length).keys.collect
res31: Array[Int] = Array(5, 4, 4, 4)

join

join 用于两个key-value类型的RDD的内连接操作,类似于数据库中的内连接。只有两者的key相同时,才会连接

val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
val b = a.keyBy(_.length)
val c = sc.parallelize(List("dog","cat","gnu","salmon","rabbit","turkey","wolf","bear","bee"), 3)
//相同的key,就能连接在一起
val d = c.keyBy(_.length)
b.join(d).collect 

res0: Array[(Int, (String, String))] = Array((6,(salmon,salmon)), (6,(salmon,rabbit)), (6,(salmon,turkey)), (6,(salmon,salmon)), (6,(salmon,rabbit)), (6,(salmon,turkey)), (3,(dog,dog)), (3,(dog,cat)), (3,(dog,gnu)), (3,(dog,bee)), (3,(rat,dog)), (3,(rat,cat)), (3,(rat,gnu)), (3,(rat,bee)))

fullOuterJoin

类似于数据库中的外连接,一共有 n×m 行,每个元素都要出现。按照key进行分组。

val pairRDD1 = sc.parallelize(List( ("cat",2), ("cat", 5), ("book", 4),("gnu", 12)))
val pairRDD2 = sc.parallelize(List( ("cat",2), ("gnu", 5), ("mouse", 4),("cat", 12)))
pairRDD1.fullOuterJoin(pairRDD2).collect
//pairRDD1中的每个元素都会于 m 个 pairRDD2 个元素连接,形成一个 n×m 行的数据
res0: Array[(String, (Option[Int], Option[Int]))] = Array((gnu,(Some(12),Some(5))), (cat,(Some(2),Some(12))), (cat,(Some(2),Some(2))), (cat,(Some(5),Some(12))), (cat,(Some(5),Some(2))), (book,(Some(4),None)), (mouse,(None,Some(4))))

leftJoin

类似于数据库中的左外连接,以左边作为标准,右边没有的填缺失值,左边没有的右边有,舍弃掉。

val a = sc.parallelize(List(("dog",2),("salmon",2),("rat",1),("elephant",10)),3)
val b = sc.parallelize(List(("dog",2),("salmon",2),("rabbit",1),("cat",7)), 3)
a.leftOuterJoin(b).collect

//左边有的,在结果集中都有,左边没有的,右边都舍弃掉。以左边作为参考标准
res1:Array((rat,(1,None)), (salmon,(2,Some(2))), (elephant,(10,None)), (dog,(2,Some(2))))

rightOuterJoin

rightOuterJoin 类似于数据中的右外连接,以右边的作为参考,要是左边没有,那么就将其补空。右边没有的,左边有,那么就舍弃。

//设置两个key-value集合
val a = sc.parallelize(List("dog", "salmon", "salmon", "rat", "elephant"), 3)
val b = a.keyBy(_.length)
val c =sc.parallelize(List("dog","cat","gnu","salmon","turkey","wolf","bear"), 3)
val d = c.keyBy(_.length)
b.rightOuterJoin(d).collect
//如果右边有相同的key,他们会按照多个key来计算
res2: Array[(Int, (Option[String], String))] = Array((6,(Some(salmon),salmon)), (6,(Some(salmon),turkey)), (6,(Some(salmon),salmon)), (6,(Some(salmon),turkey)), (3,(Some(dog),gnu)), (3,(Some(dog),dog)), (3,(Some(dog),cat)), (3,(Some(rat),gnu)), (3,(Some(rat),dog)), (3,(Some(rat),cat)), (4,(None,wolf)), (4,(None,bear)))

lookup

查看指定key的value,List类型可以使用

val a = sc.parallelize(List("dog", "tiger", "lion", "cat", "panther", "eagle"), 2)
val b = a.map(x => (x.length, x))
b.lookup(5)
//通过全表扫描来查找 key=5 的值
res1: Seq[String] = WrappedArray(tiger, eagle)

map

对每个元素进行操作
【注意:会将函数分发给每一个元素,会浪费时间在网络传输上】

rdd3.map(_*2)

mapPartitions

对元素进行操作,不同的是,如果分区,spark会将函数分发给每个区,而不用分发给每个元素,减少网络传输,但也面临着内存溢出危险,如果一个分区中的数据量巨大时,系统会将一个分区的数据读到内存,然后让他们执行函数,这样会造成内存溢出

同时处理的是分区的迭代器

mapPartitionsWithIndex

和mapPartitions一样,只不过加上了索引

max — 最大值

min — 最小值

mean — 平均值

name

返回用户给RDD标记的名称

setName

设置RDD名称

partitions

返回RDD分区信息

l1.partitions
res38: Array[org.apache.spark.Partition] = Array(org.apache.spark.rdd.ParallelCollectionPartition@bb1)

persist,cache

只是 persist(StorageLevel.MEMORY_ONLY) 的缩写,用于调整RDD的存储级别,一旦存储级别修改,就不能再次修改。

val c = sc.parallelize(List("Gnu", "Cat", "Rat", "Dog", "Gnu", "Rat"), 2)
c.getStorageLevel
//表示该RDD还没有设置存储级别,只是存储一份
res0: org.apache.spark.storage.StorageLevel = StorageLevel(1 replicas)
//cache 和persist功能一样,默认调整RDD的存储级别为 MEMORY
c.cache
c.getStorageLevel
res2: org.apache.spark.storage.StorageLevel = StorageLevel(memory, deserialized, 1 replicas)

pipe

对RDD中的每个分区进行shell命令操作
【注意:这里也可以直接指定shell文件路径】

//对每一个分区执行shell命令,并将结果返回到新的RDD中
val a = sc.parallelize(1 to 9, 3)
a.pipe("head -n 1").collect
//查找第一个元素
res1: Array[String] = Array(1, 4, 7)

popStdev — 标准差

和Stdev一样

popVariance — 方差

和Variance一样

randomSplit

randomSplit 把RDD中数据,根据用户指定的权重,随机切分成多个小的RDD,小RDD中元素的个数由权重来确定。在一些特殊情况下,我们需要让随机产生的数据相同,就可以初始化相同的种子。

reduce

和Scala一样,没有初始值,只有函数,之前的wordcount讲到过
把RDD中任意两个元素分层合并,根据用户指定的函数进行聚合

rdd3.reduce(_+_)
res49: Int = 27

reduceByKey

先在分区内聚合好后,再分组聚合
代替 groupByKey,groupByKey是直接将所有元素分配到对应的分区内,然后再聚合

repartition — 重新分区

重新设置分区数

rdd3.repartition(3)

sample — 抽样

sample 随机挑选RDD中的一部分产生新的RDD,withReplacement表示是否放回,fraction表示挑选比例(0-1之间),seed表示随机初始化种子,一般没用,除非检查的时候,会写个定值,查看数据是否有问题

//可放回
scala> rdd1.sample(true,0.4,1).collect
res58: Array[Int] = Array(3, 4, 5, 10)
//不可放回
scala> rdd1.sample(false,0.2,2).collect
res62: Array[Int] = Array(1, 4)

sampleStdev — 抽样标准差

sampleVariance — 抽样方差

saveAsObjectFile — 保存为二进制格式

saveASTextFile — 保存为文本格式

sortBy — 排序

第一个参数用于指定将待排序的RDD元素转换成要排序的key,根据这个指定的key来排序,第二个参数如果为true表示升序,false为降序

rdd1.sortBy(x=>x,false).collect
res66: Array[Int] = Array(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

stats

统计RDD中的平均值,方差,标准差,最大值和最小值

rdd1.stats
res63: org.apache.spark.util.StatCounter = (count: 10, mean: 5.500000, stdev: 2.872281, max: 10.000000, min: 1.000000)

stdev — 标准差

subtract — 差集

rdd1.subtract(rdd2).collect
res67: Array[Int] = Array(1, 2, 3, 4)

sum — 求和

take — 取前几个元素

takeOrdered

先对RDD数据进行排序,然后take

rdd4.takeOrdered(3)
res75: Array[String] = Array(hello, helloWorld, javascript)

takeSample

takeSample 基本功能是随机返回用户数量的元素。与sample不同的是,(1) takeSample 返回的是确定数量的值,而sample返回的数的个数不确定。(2) takeSample返回的是一个数组,而sample返回的是一个RDD

rdd2.takeSample(false,3,1)
res77: Array[Int] = Array(10, 5, 9)

toDF

转成DateFrameWork

toDS

转成DateStream类型

toDebugString

获取RDD的依赖及其Debug信息

toJavaRDD

toLocalIterator

toString

获取RDD信息

top

top 获取RDD按照降序排序后,获取前几个

treeAggregate

treeAggregate 和aggregateByKey 非常类似,不会在每个分区之间再应用初始值。不同的是,他可以指定每次聚合元素的个数,这样能够减少聚合的次数,减少计算,默认聚合的是两个。

treeReduce

treeReduce 和reduce 非常类似。不同的是,他可以指定每次聚合元素的个数,这样能够减少聚合的次数,减少计算,默认聚合的是两个

union

和++ 一样

unpersist

unpersist 撤销已经实现的RDD的内容,但是产生的RDD编号不会消失,如果再次使用这个编号,就会将消失的数据复原

//撤销已经生成的RDD数据
val y = sc.parallelize(1 to 10, 10)
val z = (y++y)
z.collect
z.unpersist(true)

variance — 方差

zip

将两个RDD联合起来,形成一个key-value类型的RDD

val x = sc.parallelize(1 to 10)
val y = sc.parallelize(21 to 30)
//x和y的长度必须相同,否则报错,x作为key,y作为value
x.zip(y).collect

zipPartitions

zipParititions 和zip的功能类似,但是用户可以自定义实现多个RDD之间进行zip,但是用户必须自定义实现函数

zipWithIndex

zipWithIndex 和zip的功能类似,是zip 的简化版,他使用前者所在的位置作为其value,不需要用户再定义一个RDD

rdd2.zipWithIndex.collect
res90: Array[(Int, Long)] = Array((11,0), (12,1), (13,2), (14,3), (15,4), (16,5), (17,6), (18,7), (19,8), (20,9))

zipWithUniqueId

zipWithUniqueId 和zip的功能类似,是zip 的简化版,他使用一个系统分配的唯一Id作为其value ,这与序号并不对应,但是这个更加安全,不同分区之间也不会导致相同的value

rdd2.zipWithUniqueId.collect
res91: Array[(Int, Long)] = Array((11,0), (12,1), (13,2), (14,3), (15,4), (16,5), (17,6), (18,7), (19,8), (20,9))
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值