文章目录
- 1、转换算子
- 1.1 map
- 1.2 mapPartitions
- 1.3 mapPartitionsWithIndex
- 1.4 flatMap
- 1.5 glom
- 1.6 groupBy
- 1.7 filter
- 1.8 sample
- 1.9 distinct
- 1.10 coalesce
- 1.11 repartition
- 1.12 sortBy
- 1.13 intersection
- 1.14 union
- 1.15 subtract
- 1.16 zip
- 1.17 partitionBy
- 1.18 reduceByKey
- 1.19 groupByKey
- 思考:reduceByKey 和 groupByKey 的区别?
- 1.20 aggregateByKey
- 1.21 foldByKey
- 1.22 combineByKey
- 思考:reduceByKey、foldByKey、aggregateByKey、combineByKey 的区别?
- 1.23 sortByKey
- 1.24 join
- 1.25&1.26 leftOuterJoin&rightOuterJoin
- 1.27 cogroup
- 2、行动算子
- 3、Spark rdd中求行差值的几种方法整理
1、转换算子
1.1 map
val sc = SparkContext.getOrCreate(new SparkConf().setMaster("local[2]").setAppName(this.getClass.getSimpleName))
val a1 = sc.makeRDD(List(2, 4, 5, 7, 8, 9), 2)
a1.map(x => {
println(">>>>>>>>>>>")
x * 2
}).collect.foreach(println(_))
1.2 mapPartitions
map和mapPartition的区别:map 算子主要目的将数据源中的数据进行转换和改变。但是不会减少或增多数据。是串行,效率低
mapPartitions 算子需要传递一个迭代器,返回一个迭代器,没有要求的元素的个数保持不变,所以可以增加或减少数据。并行,效率高,但消耗内存,容易造成内存溢出
// 1.每个分区计算一次的测试
println("每个分区计算一次")
a1.mapPartitions(x => {
println(">>>>>>>>>>>")
x.map(_ * 2)
}).foreach(println)
//2.返回每个分区的最大值
println("返回每个分区的最大值")
sc.makeRDD(List(1, 2, 3, 4)).mapPartitions(x => {
List(x.max).toIterator
}).collect.foreach(println)
//3、将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据。
println("过滤数据")
a1.mapPartitions(x => x.filter(_ == 2)).foreach(println(_))
1.3 mapPartitionsWithIndex
将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。
在使用中,前一个值为分区号,后一个值为分区的迭代器
//只保留分区号为1的分区数据
println("只保留分区号为1的分区数据")
sc.makeRDD(List(1, 2, 3, 4), 2).mapPartitionsWithIndex((x, y) => {
if (x == 1) {
y
} else {
Nil.toIterator
}
}).collect.foreach(println)
//输出对应分区号
println("输出对应分区号")
a1.mapPartitionsWithIndex({
case (x, y) =>
y.map((_, "分区号为:" + x))
}).foreach(println(_))
println()
1.4 flatMap
将处理的数据进行扁平化后再进行映射处理
println("扁平映射1")
sc.makeRDD(List(
List(1, 2), List(3, 4)
)).flatMap(x => x.map((_, 1))).collect.foreach(println)
println("扁平映射2")
sc.makeRDD(List("aaa bbb", "ccc ddd", "ccc eee")).flatMap(x => {
x.split(" ")
}).map((_, 1)).reduceByKey(_ + _).foreach(println(_))
1.5 glom
将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变
println("同分区的所有数据转为数组")
sc.makeRDD(List(1, 2, 3, 4, "a"), 2).glom().collect.foreach(x => {
println(x.mkString(","))
})
println("计算所有分区最大值求和(分区内取最大值,分区间最大值求和)")
val max = sc.makeRDD(List(1, 2, 3, 4), 2).glom().map(x => x.max).collect.sum
println(max)
1.6 groupBy
将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中一个组的数据在一个分区中,但是并不是说一个分区中只有一个组
println("根据首字母进行分组")
sc.makeRDD(List("hello", "hadoop", "spark", "hive")).groupBy(_.charAt(0)).collect.foreach(println)
println("根据时段进行分组")
sc.makeRDD(List("81.220.24.207 - - 17/05/2015:10:05:21 +0000 GET /favicon.ico"
, "66.249.73.135 - - 17/05/2015:11:05:17 +0000 GET /blog/geekery/vmware-cpu-performance.html"
, "46.105.14.53 - - 17/05/2015:11:05:42 +0000 GET /blog/tags/puppet?flav=rss20"
, "218.30.103.62 - - 17/05/2015:11:05:11 +0000 GET /robots.txt")).map(x => {
val datas = x.split(" ")
val time = datas(3)
val sdf = new SimpleDateFormat("dd/MM/yy:HH:mm:ss")
val date = sdf.parse(time)
val sdf2 = new SimpleDateFormat("HH")
val hour = sdf2.format(date)
(hour, 1)
}).groupBy(_._1).map {
case (hour, iter) => (hour, iter.size)
}.collect.foreach(println(_))
// .mapValues(_.size).foreach(println(_))
1.7 filter
将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出现数据倾斜。
sc.makeRDD(List("81.220.24.207 - - 17/04/2015:10:05:21 +0000 GET /favicon.ico"
, "66.249.73.135 - - 17/05/2015:11:05:17 +0000 GET /blog/geekery/vmware-cpu-performance.html"
, "46.105.14.53 - - 17/05/2015:11:05:42 +0000 GET /blog/tags/puppet?flav=rss20"
, "218.30.103.62 - - 17/05/2015:11:05:11 +0000 GET /robots.txt")).filter(x => {
val datas = x.split(" ")
val time = datas(3)
time.startsWith("17/05/2015")
}).collect.foreach(println(_))
1.8 sample
根据指定的规则从数据集中抽取数据
1.8.1 抽取数据不放回(伯努利算法)
- 伯努利算法:又叫 0、1 分布。例如扔硬币,要么正面,要么反面。
具体实现:根据种子和随机算法算出一个数和第二个参数设置几率比较,小于第二个参数要,大于不要 - 第一个参数:抽取的数据是否放回,false:不放回
- 第二个参数:抽取的几率,范围在[0,1]之间,0:全不取;1:全取;
- 第三个参数:随机数种子,如果不写该参数,那么使用的是当前系统时间作为随机数种子
1.8.2 抽取数据放回(泊松算法)
- 第一个参数:抽取的数据是否放回,true:放回;false:不放回
- 第二个参数:重复数据的几率,范围大于等于 0.表示每一个元素被期望抽取到的次数
- 第三个参数:随机数种子,如果不写该参数,那么使用的是当前系统时间作为随机数种子
val dataRDD = sc.makeRDD(List(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
), 1)
//抽取数据不放回
dataRDD.sample(false, 0.5).foreach(x => print(x + "\t"))
println()
//抽取数据放回
dataRDD.sample(true, 2).foreach(x => print(x + "\t"))
println()
1.9 distinct
将数据集中重复的数据去重
-
distinctList底层是个HashSet
-
distinctRDD底层方法如下
map(x => (x, null)).reduceByKey((x, ) => x, numPartitions).map(._1)
val str = sc.makeRDD(List(1, 2, 3, 4, 1, 2, 3, 4)).distinct.collect.mkString(",")
println(str)
1.10 coalesce
根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率
当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少分区的个数,减小任务调度成本
coalesce方法默认不会打乱分区重新组合,容易导致数据倾斜
但coalease还有第二个参数,默认为false,不shuffle,设置为true即可进行shuffle让数据变的均衡
//缩减分区用coalease+shuffle
sc.makeRDD(List(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 8, 6), 6).coalesce(2)
.mapPartitions(x => x.filter(_ > 0)).glom().map(x => x.sum).collect.foreach(x => println(x))
1.11 repartition
该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的
RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition
操作都可以完成,因为无论如何都会经 shuffle 过程。
//扩大分区用reparation默认是shuffle
sc.makeRDD(List(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 8, 6), 2).repartition(4)
.mapPartitions(x => x.filter(_ > 0)).glom().map(x => x.sum).collect.foreach(x => println(x))
1.12 sortBy
该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理的结果进行排序,默认为升序排列。排序后新产生的 RDD 的分区数与原 RDD 的分区数一致。中间存在 shuffle 的过程。
可以有第二个参数,Boolean值,倒序
val functionToString = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 8, 6)).map(x => x).sortBy(x => x).collect().mkString(",")
val functionToString2 = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 8, 6)).map(x => x).sortBy(x => x, false).collect().mkString(",")
println(functionToString)
println(functionToString2)
1.13 intersection
对源 RDD 和参数 RDD 求交集后返回一个新的 RDD,要求类型相同
sc.makeRDD(List(1, 2, 3, 4)).intersection(sc.makeRDD(List(3, 4, 5, 6))).foreach(println(_))
1.14 union
对原 RDD 和参数 RDD 求并集后返回一个新的 RDD,要求类型相同
sc.makeRDD(List(1, 2, 3, 4)).union(sc.makeRDD(List(3, 4, 5, 6))).foreach(println(_))
1.15 subtract
以前一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集。要求类型相同
sc.makeRDD(List(1, 2, 3, 4)).subtract(sc.makeRDD(List(3, 4, 5, 6))).foreach(println(_))
1.16 zip
将两个 RDD 中的元素,以键值对(元组)的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD中的元素,Value 为第 2 个 RDD 中的相同位置的元素。要求拉链分区数量相同,分区数据长度相同
sc.makeRDD(List(1, 2, 3, 4)).zip(sc.makeRDD(List(3, 4, 5, 6))).foreach(println(_))
1.17 partitionBy
将数据按照指定 Partitioner 重新进行分区。隐式转换为PairRDDFunctions,二次编译。
区别于coalease和repartition是数量上的改变,二partitionBy是功能上的改变,比如说默认的分区器是 HashPartitioner,或按照key的奇偶分区等规则
1、如果重分区的分区器和当前RDD 的分区器一样怎么办? 源码方法里类型相同,数量相等,则会认为是同一个分区器,返回自己不会产生新的RDD
2、Spark 还有其他分区器吗?三个,Hash和Range(排序时用)和Python(private)
3、如果想按照自己的方法进行数据分区怎么办?自定义
println(sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc")), 3).partitionBy(new HashPartitioner(2)).getNumPartitions)
println(sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc")), 3).getNumPartitions)
1.18 reduceByKey
可以将数据按照相同的 Key 对 Value 进行聚合,和scala语言中一样,都是两两聚合。如果Key只有一个,只返回不计算。还可以指定分区task数
sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3), ("a", 4), ("b", 5), ("c", 6))).reduceByKey(_ + _).foreach(println(_))
println("-------分割线-----------")
sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3), ("a", 4), ("b", 5), ("c", 6))).reduceByKey(_ + _, 2).foreach(println(_))
1.19 groupByKey
将数据源的数据根据 key 对 value 进行分组,形成一个对偶元组,元组的第二个元素是value,groupBy分组后的元组第二个元素是KV
println(sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)), 3).groupByKey().getNumPartitions)
sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)), 3).groupByKey(2).foreach(x => x._2.foreach(y => println((x._1, y))))
sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)), 3).groupByKey(new HashPartitioner(2)).foreach(x => x._2.foreach(y => println((x._1, y))))
思考:reduceByKey 和 groupByKey 的区别?
从 shuffle 的角度:reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少落盘的数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题,reduceByKey 性能比较高。
从功能的角度:reduceByKey 其实包含分组和聚合的功能。GroupByKey 只能分组,不能聚合,所以在分组聚合的场合下,推荐使用 reduceByKey,如果仅仅是分组而不需要聚合。那么还是只能使用 groupByKey
1.20 aggregateByKey
reduceByKey分区内和分区之间的计算规则是相同的,而aggregateByKey是将数据根据不同的规则分别进行不同的分区内计算和分区间计算,规则分别设立。aggregateByKey的返回值和初始化值必须相同
aggregateByKey 算子是函数柯里化,存在两个参数列表:
- 第一个参数列表中的参数表示初始值
- 第二个参数列表中含有两个参数
- 2.1 第一个参数表示分区内的计算规则
- 2.2 第二个参数表示分区间的计算规则
sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("b", 4))).aggregateByKey(0)(
(x, y) => math.max(x, y),
(x, y) => x + y
).collect.foreach(println(_))
println("求相同key的平均值")
val newRDD = sc.makeRDD(List(("a", 88), ("b", 95), ("a", 91), ("b", 93),
("a", 95), ("b", 98)), 2).aggregateByKey((0, 0))(
(x, y) => {
(x._1 + y, x._2 + 1)
},
(x1, x2) => {
(x1._1 + x2._1, x1._2 + x2._2)
}
)
newRDD.mapValues {
case (num, count) => num / count
}.foreach(println(_))
1.21 foldByKey
当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey
sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)))
.foldByKey(0)(_ + _).foreach(println(_))
1.22 combineByKey
最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于aggregate(),combineByKey()允许用户返回值的类型与输入不一致。
combineByKey : 方法需要三个参数
第一个参数表示:将相同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)
//acc: (Int, Int)数据类型要动态识别
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)
)
combineRdd.mapValues {
case (num, count) => num / count
}.foreach(println(_))
思考:reduceByKey、foldByKey、aggregateByKey、combineByKey 的区别?
四者源码如下:
reduceByKey:
combineByKeyWithClassTag[V](
(v: V) => v, // 第一个值不会参与计算
func, // 分区内计算规则
func, // 分区间计算规则
partitioner
)
aggregateByKey :
combineByKeyWithClassTag[U](
(v: V) => cleanedSeqOp(createZero(), v), // 初始值和第一个key的value值进行的分区内数据操作
cleanedSeqOp, // 分区内计算规则
combOp, // 分区间计算规则
partitioner
)
foldByKey:
combineByKeyWithClassTag[V](
(v: V) => cleanedFunc(createZero(), v), // 初始值和第一个key的value值进行的分区内数据操作
cleanedFunc, // 分区内计算规则
cleanedFunc, // 分区间计算规则
partitioner
)
combineByKey :
combineByKeyWithClassTag(
createCombiner, // 相同key的第一条数据进行的处理函数
mergeValue, // 表示分区内数据的处理函数
mergeCombiners, // 表示分区间数据的处理函数
partitioner
)
以WordCount为例的简单解释
-
reduceByKey: 相同 key 的第一个数据不进行任何计算,分区内和分区间计算规则相同
-
FoldByKey: 相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则相同
-
AggregateByKey:相同 key 的第一个数据和初始值进行分区内计算,分区内和分区间计算规则可以不相同
-
CombineByKey:当计算时,发现数据结构不满足要求时,可以让第一个数据转换结构。分区内和分区间计算规则不相同。
rdd.reduceByKey(_+_) rdd.aggregateByKey(0)(_+_, _+_) rdd.foldByKey(0)(_+_) rdd.combineByKey(v=>v,(x:Int,y)=>x+y,(x:Int,y:Int)=>x+y)
1.23 sortByKey
在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口(特质),返回一个按照 K 进行排序的RDD
val dataRDD1 = sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)))
dataRDD1.sortByKey(true).collect.foreach(println(_))
dataRDD1.sortByKey(false).foreach(println(_))
1.24 join
在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接为tuple类型的(K,(V,W))的 RDD。
Key类型应该是相同的,如果两个数据源中的key没有匹配上,那么数据不会出现在结果中;如果两个数据源中的key有多个相同的,会依次匹配,出现笛卡尔积,对性能有影响
val rdd3: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c")))
val rdd4: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (3, 6)))
rdd3.join(rdd4).collect().foreach(println)
1.25&1.26 leftOuterJoin&rightOuterJoin
类似于 SQL 语句的左外连接
val dr1 = sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)))
val dr2 = sc.makeRDD(List(("e", 1), ("b", 2), ("c", 3)))
println("leftOuterJoin")
dr1.leftOuterJoin(dr2).collect().foreach(println)
println("rightOuterJoin")
dr1.rightOuterJoin(dr2).collect().foreach(println)
1.27 cogroup
cogroup=connect+group,分组连接,有点像根据重复K拉平后的join操作
在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable,Iterable))类型的 RDD
val cgRDD1 = sc.makeRDD(List(("a", 1), ("a", 2), ("c", 3)))
val cgRDD2 = sc.makeRDD(List(("a", 1), ("c", 2), ("c", 3), ("d", 3)))
cgRDD1.cogroup(cgRDD2).collect.foreach(println)
2、行动算子
2.1 reduce
聚集RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据
val r1 = sc.makeRDD(List(1, 2, 3)).reduce(_ + _)
println(r1)
2.2 collect
在驱动程序中,以数组Array 的形式返回数据集的所有元素
val ints11: Array[Int] = sc.makeRDD(List(4,2,3,1)).collect()
println(ints11.mkString(","))
2.3 count
返回RDD 中元素的个数
val cnt = sc.makeRDD(List(4,2,3,1)).count()
println(cnt)
2.4 first
返回RDD 中的第一个元素
val first = sc.makeRDD(List(4,2,3,1)).first()
println(first)
2.5 take
返回一个由RDD 的前 n 个元素组成的数组
val ints: Array[Int] = sc.makeRDD(List(4,2,3,1)).take(3)
println(ints.mkString(","))
2.6 takeOrdered
返回该 RDD 排序后的前 n 个元素组成的数组
val rddt = sc.makeRDD(List(4,2,3,1))
val ints1: Array[Int] = rddt.takeOrdered(3)
println(ints1.mkString(","))
2.7 aggregate
分区的数据通过初始值和分区内的数据进行聚合,然后再和初始值进行分区间的数据聚合
- aggregate:初始值在参与分区内计算后,再参与分区间的计算
- aggregateByKey:初始值仅参与分区内计算,不参与分区间的计算
val i = sc.makeRDD(List(1, 2, 3, 4), 2).aggregate(10)(_ + _, _ + _)
println(i)
2.8 fold
折叠操作,aggregate 的简化版操作
println(sc.makeRDD(List(1, 2, 3, 4), 2).fold(10)(_ + _))
2.9 countByKey
统计每种 key 的个数
sc.makeRDD(List(("a", 1), ("a", 5), ("b", 2), ("b", 2))).countByKey().foreach(println)
2.10 countByValue
用于没有key的单值情况,如果有key则把KV看作组合的单值
sc.makeRDD(List(("a", 1), ("a", 5), ("b", 2), ("b", 2))).countByValue().foreach(println)
思考:WordCount有多少种RDD实现方法
- groupBy+mapValues
- map+groupByKey+mapValues
- map+reduceByKey
- map+aggregateByKey
- map+foldByKey
- map+combineByKey
- map+countByKey
- countByValue
- map+reduce
- map+fold
- map+aggregate
val rdd = sc.makeRDD(List("hello scala", "hello spark"))
val words = rdd.flatMap(_.split(" "))
println("第1种")
words.groupBy(word => word).mapValues(_.size).foreach(println)
println("第2种")
words.map((_, 1)).groupByKey().mapValues(_.size).foreach(println)
println("第3种")
words.map((_, 1)).reduceByKey(_ + _).foreach(println)
println("第4种")
words.map((_, 1)).aggregateByKey(0)(_ + _, _ + _).foreach(println)
println("第5种")
words.map((_, 1)).foldByKey(0)(_ + _).foreach(println)
println("第6种")
words.map((_, 1)).combineByKey(v => v, (x: Int, y) => x + y, (x: Int, y: Int) => x + y).foreach(println)
println("第7种")
words.map((_, 1)).countByKey().foreach(println)
println("第8种")
words.countByValue().foreach(println)
println("第9种")
import scala.collection.mutable
words.map(
word => {
mutable.Map[String, Long]((word,1))
}
).reduce(
(x,y)=>{
y.foreach{
case (word,count)=>{
val newCount = x.getOrElse(word,0L)+count
x.update(word,newCount)
}
}
x
}
).foreach(println)
println("第10种")
words.map(
x=>{
mutable.Map[String,Long]((x,1))
}
).fold(mutable.Map[String,Long]())(
(x,y)=>{
y.foreach{
case (word,count)=>{
val newCount = x.getOrElse(word,0L)+count
x.update(word,newCount)
}
}
x
}
).foreach(println)
println("第11种")
words.map(
x=>{
mutable.Map[String,Long]((x,1))
}
).aggregate(mutable.Map[String,Long]())(
(x,y)=>{
y.foreach{
case (word,count)=>{
val newCount = x.getOrElse(word,0L)+count
x.update(word,newCount)
}
}
x
},
(x,y)=>{
y.foreach{
case (word,count)=>{
val newCount = x.getOrElse(word,0L)+count
x.update(word,newCount)
}
}
x
}
).foreach(println(_))
2.11 saveAsTextFile、saveAsObjectFile、saveAsSequenceFile
将数据保存到不同格式的文件中。文件目录不能已存在。saveAsSequenceFile要求必须是KV键值类型
sc.makeRDD(List(("a", 1), ("a", 5), ("b", 2), ("b", 2))).saveAsTextFile("files/output/04saveAsTextFile")
sc.makeRDD(List(("a", 1), ("a", 5), ("b", 2), ("b", 2))).saveAsObjectFile("files/output/05saveAsObjectFile")
sc.makeRDD(List(("a", 1), ("a", 5), ("b", 2), ("b", 2))).saveAsSequenceFile("files/output/06saveAsSequenceFile")
2.14 foreach
分布式遍历RDD 中的每一个元素,调用指定函数
collect以分区为单位采集回到driver端再通过foreach打印,foreach是在executor端分布式采集打印
算子 : Operator(操作)
RDD的方法和Scala集合对象的方法不一样
集合对象的方法都是在同一个节点的内存中完成的。
RDD的方法可以将计算逻辑发送到Executor端(分布式节点)执行
为了区分不同的处理效果,所以将RDD的方法称之为算子。
RDD的方法外部的操作都是在Driver端执行的,而方法内部的逻辑代码是在Executor端执行。
println("collect.foreach")
sc.makeRDD(List(1,2,3,4),2).collect.foreach(println)
println("直接foreach")
sc.makeRDD(List(1,2,3,4),2).foreach(println)
ObjectFile(“files/output/05saveAsObjectFile”)
sc.makeRDD(List((“a”, 1), (“a”, 5), (“b”, 2), (“b”, 2))).saveAsSequenceFile(“files/output/06saveAsSequenceFile”)
### 2.14 foreach
分布式遍历RDD 中的每一个元素,调用指定函数
collect以分区为单位采集回到driver端再通过foreach打印,foreach是在executor端分布式采集打印
算子 : Operator(操作)
RDD的方法和Scala集合对象的方法不一样
集合对象的方法都是在同一个节点的内存中完成的。
RDD的方法可以将计算逻辑发送到Executor端(分布式节点)执行
为了区分不同的处理效果,所以将RDD的方法称之为算子。
RDD的方法外部的操作都是在Driver端执行的,而方法内部的逻辑代码是在Executor端执行。
```scala
println("collect.foreach")
sc.makeRDD(List(1,2,3,4),2).collect.foreach(println)
println("直接foreach")
sc.makeRDD(List(1,2,3,4),2).foreach(println)
3、Spark rdd中求行差值的几种方法整理
数据字段名称:省份,ID,日期,确诊人数(rdd.map(x => (x(12), (x(0).toInt, x(7), x(2).toInt))))
问题:统计每个省份,每天的确诊人数增量。
分析:求第二天减去第一天的确诊人数,即为确诊人数增量
3.1、删首位元素后,zipAll,然后求左右差值即可
val incrNum = rdd.map(x => (x(12), (x(0).toInt, x(7), x(2).toInt))).groupByKey(1)
.flatMapValues(x => x.toArray.sortWith(_._2 > _._2)
.zipAll(x.toArray.sortWith(_._2 > _._2).map(y => y._3).drop(1), (0, "", 0), 0))
.map(x => (x._2._1._1, x._1, x._2._1._2, x._2._1._3, x._2._1._3 - x._2._2)).sortBy(_._1)
3.2、使用sliding函数,滑动2步生成长度为2的数组,求两个元素的差值,再视情况在头部添加初始元素即可
rdd.map(x => (x(12), (x(0).toInt, x(7), x(2).toInt))).groupByKey()
.flatMapValues(x=>{
var arr = x.toArray.sortBy(_._1)
var arr2 = arr(0)._3+:arr.sliding(2).map(y=>y(1)._3-y(0)._3).toArray
arr.zip(arr2)
})
3.3、使用join+getOrElse方法求差值即可
val value = rdd.map(x => (x(12),( x(7),x(0).toInt, x(2).toInt)))
.groupByKey().flatMapValues(x=>x.zipWithIndex).map(x=>((x._1,x._2._2),(x._2._1._1,x._2._1._2,x._2._1._3))).cache()
val value2 = value.map(x=>((x._1._1,x._1._2+1),(x._2._1,x._2._2,x._2._3)))
val value3=value.leftOuterJoin(value2)
.map(x=>(x._1._1,x._2._1._1,x._2._1._2,x._2._1._3,x._2._1._3-x._2._2.map(y=>y._3).getOrElse(0)))
.sortBy(x=>(x._1,x._3))