spark基础3(RDD的特性与算子)

上文介绍了SPARK读入文件,RDD与DATAFRAME,本文主要介绍RDD的一些相关知识。

1、RDD

1.1 宽依赖与窄依赖

窄依赖:父RDD中,每个分区内的数据,都只会被子RDD中特定的分区所消费,为窄依赖:
宽依赖:父RDD中,分区内的数据,会被子RDD内多个分区消费,则为宽依赖:

在这里插入图片描述
Spark的这种依赖关系设计,使其具有了天生的容错性,大大加快了Spark的执行速度。因为,RDD数据集通过“血缘关系”记住了它是如何从其它RDD中演变过来的,血缘关系记录的是粗颗粒度的转换操作行为,当这个RDD的部分分区数据丢失时,它可以通过血缘关系获取足够的信息来重新运算和恢复丢失的数据分区,由此带来了性能的提升。相对而言,在两种依赖关系中,窄依赖的失败恢复更为高效,它只需要根据父RDD分区重新计算丢失的分区即可(不需要重新计算所有分区),而且可以并行地在不同节点进行重新计算。而对于宽依赖而言,单个节点失效通常意味着重新计算过程会涉及多个父RDD分区,开销较大。此外,Spark还提供了数据检查点和记录日志,用于持久化中间RDD,从而使得在进行失败恢复时不需要追溯到最开始的阶段。在进行故障恢复时,Spark会对数据检查点开销和重新计算RDD分区的开销进行比较,从而自动选择最优的恢复策略。

1.2 stage的划分

Spark通过分析各个RDD的依赖关系生成了DAG,再通过分析各个RDD中的分区之间的依赖关系来决定如何划分阶段,具体划分方法是:在DAG中进行反向解析,遇到宽依赖就断开,遇到窄依赖就把当前的RDD加入到当前的阶段中;将窄依赖尽量划分在同一个阶段中,可以实现流水线计算(具体的阶段划分算法请参见AMP实验室发表的论文《Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing》)。例如,如图9-11所示,假设从HDFS中读入数据生成3个不同的RDD(即A、C和E),通过一系列转换操作后再将计算结果保存回HDFS。对DAG进行解析时,在依赖图中进行反向解析,由于从RDD A到RDD B的转换以及从RDD B和F到RDD G的转换,都属于宽依赖,因此,在宽依赖处断开后可以得到三个阶段,即阶段1、阶段2和阶段3。可以看出,在阶段2中,从map到union都是窄依赖,这两步操作可以形成一个流水线操作,比如,分区7通过map操作生成的分区9,可以不用等待分区8到分区9这个转换操作的计算结束,而是继续进行union操作,转换得到分区13,这样流水线执行大大提高了计算的效率。
在这里插入图片描述
由上述论述可知,把一个DAG图划分成多个“阶段”以后,每个阶段都代表了一组关联的、相互之间没有Shuffle依赖关系的任务组成的任务集合。每个任务集合会被提交给任务调度器(TaskScheduler)进行处理,由任务调度器将任务分发给Executor运行。

2算子

2.1Spark的算子的分类

https://www.cnblogs.com/feiyumo/p/7874641.html
   从大方向来说,Spark 算子大致可以分为以下两类:
1)Transformation 变换/转换算子:这种变换并不触发提交作业,完成作业中间过程处理。
     Transformation 操作是延迟计算的,也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行,需要等到有 Action 操作的时候才会真正触发运算。

2)Action 行动算子:这类算子会触发 SparkContext 提交 Job 作业。
     Action 算子会触发 Spark 提交作业(Job),并将数据输出 Spark系统。

从小方向来说,Spark 算子大致可以分为以下三类:

1)Value数据类型的Transformation算子,这种变换并不触发提交作业,针对处理的数据项是Value型的数据。
2)Key-Value数据类型的Transfromation算子,这种变换并不触发提交作业,针对处理的数据项是Key-Value型的数据对。
3)Action算子,这类算子会触发SparkContext提交Job作业。

1)Value数据类型的Transformation算子

一、输入分区与输出分区一对一型

1、map算子
map将原来 RDD 的每个数据项通过 map 中的用户自定义函数 f 映射转变为一个新的元素。
的输入变换函数应用于RDD中所有元素,而mapPartitions应用于所有分区。区别于mapPartitions主要在于调用粒度不同。如parallelize(1 to 10, 3),map函数执行10次,而mapPartitions函数执行3次。
valb=a.map(.length)
valb=a.map(
.split(" "))

输入分区与输出分区一对一,即:有多少个输入分区,就有多少个输出分区【分区数不会改变】

2、flatMap算子
将原来 RDD 中的每个元素通过函数 f 转换为新的元素,并将生成的 RDD 的每个集合中的元素合并为一个集合

val data=spark.sparkContext.parallelize(List("tomtom","jerryhehe","hehe"),2)
val fm=data.flatMap(x=>x.split(""))
fm.foreach(println)
println(fm.count())

同Map算子一样,最后将所有元素放到同一集合中;【分区数不会改变】

3、mapPartitions算子
mapPartitions 函 数 获 取 到 每 个 分 区 的 迭 代器,在 函 数 中 通 过 这 个 分 区 整 体 的 迭 代 器 对整 个 分 区 的 元 素 进 行 操 作。
类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,
func的函数类型必须是Iterator[T] => Iterator[U]

map: 比如一个partition中有1万条数据;那么你的function要执行和计算1万次。
MapPartitions:一个task仅仅会执行一次function,function一次接收所有的partition数据。只要执行一次就可以了,性能比较高。

scala> val a = sc.parallelize(1 to 9, 3)
a: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[105] at parallelize at <console>:26
 
scala> def doubleFunc(iter: Iterator[Int]) : Iterator[(Int,Int)] = {
     |     var res = List[(Int,Int)]()
     |     while (iter.hasNext)
     |     {
     |       val cur = iter.next;
     |       res .::= (cur,cur*2)
     |     }
     |     res.iterator//iterator循环


     |   }
doubleFunc: (iter: Iterator[Int])Iterator[(Int, Int)]
 
scala> val result = a.mapPartitions(doubleFunc)
result: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[106] at mapPartitions at <console>:30
 
scala> println(result.collect().mkString)
(3,6)(2,4)(1,2)(6,12)(5,10)(4,8)(9,18)(8,16)(7,14)
valrdd1=spark.sparkContext.parallelize(1to10,2)
//rdd1有两个分区
varrdd3=rdd1.mapPartitions{x=>{
varresult=List[Int]()
vari=0
while(x.hasNext){i+=x.next()}
result.::(i).iterator
}}

如果在map过程中需要频繁创建额外的对象(例如将rdd中的数据通过jdbc写入数据库,map需要为每个元素创建一个链接而mapPartition为每个partition创建一个链接),则mapPartitions效率比map高的多。

4、mapPartitionsWithIndex
与mapPartitions类似,但需要提供一个表示分区索引值的整型值作为参数,
因此function必须是(int, Iterator)=>Iterator类型的。
mapPartitionsWithIndex()传入的方法需要两个参数,一个为分区Id,另一个为分区数据,该方法可用来查看各分区的数据

val data=spark.sparkContext.parallelize(1to10,2)
var nn=data.mapPartitionsWithIndex{(x,i)=>//x:分区ID  i:分区数据
var result=List[String]()
var va=0
while(i.hasNext){va=i.next();result::=("目前是"+x+"-"+va)}
result.toIterator

}

4、glom算子
glom的作用是将同一个分区里的元素合并到一个array里,也就是
将每一个分区形成一个数组

glom之后元素已经变成了分片映射的列表

二、输入分区与输出分区多对一型

5、union算子

val a=spark.sparkContext.parallelize(List(("a",1),("b",2)))
val a11=spark.sparkContext.parallelize(List(("c",1),("d",2)))
a.union(a11).foreach(println)
去重
a.union(a11).distinct().foreach(println)

6、cartesian算子
求笛卡尔积,将(arr2)里的数值分别对应到arr1的第一个值上,然后分别对应第二个,第三个,组成一个个的新集合,也就是我们常说的笛卡尔积

object CartesianTest {
  def main(args: Array[String]): Unit = {
    val conf=new SparkConf().setAppName("cratesian").setMaster("local")
    val sc=new SparkContext(conf)

    val arr1=sc.parallelize(Array(1,3,5))
    val arr2=sc.parallelize(Array(2,4,6))
    arr1.cartesian(arr2).collect.foreach(println)
  }
}

三、输入分区与输出分区多对多型

7、grouBy算子
将元素通过函数生成相应的 Key,数据就转化为 Key-Value 格式,之后将 Key 相同的元素分为一组。

val a = sc.parallelize(1 to 9, 3)
a.groupBy(x => { if (x % 2 == 0) "even" else "odd" }).collect

或者

val a = sc.parallelize(1 to 9, 3)
def myfunc(a: Int) : Int =
{
  a % 2
}
a.groupBy(myfunc).collect //同样的,返回的是0的时候,表示的是偶数值,返回的是1的时候表示的是奇数。
res3: Array[(Int, Seq[Int])] = Array((0,ArrayBuffer(2, 4, 6, 8)), (1,ArrayBuffer(1, 3, 5, 7, 9)))

四、输出分区为输入分区子集型

8、filter算子

过滤

 dataframe
valpd=spark.sparkContext.textFile("E:/emp1.txt").map{
line=>person(line.split(",")(0),line.split(",")(1).toInt)
}.toDF("X1","X2")
pd.show()
valx=pd.filter($"X2">60)
x.show()

Rdd
valpd=spark.sparkContext.textFile("E:/emp1.txt").map{
line=>{
valf=line.split(",")
valname=f(0)
valsc=f(1).toInt
(name,sc)
}
}
valx=pd.filter(t=>t._2.toInt>60)
x.foreach(println)

9、distinct算子
去重

a.union(a11).distinct().foreach(println)

10、subtract算子
subtract相当于进行集合的差操作,
RDD 1去除RDD 1和RDD 2交集中的所有元素,不去重

vala=spark.sparkContext.parallelize(List(("a",1),("a",1),("b",2)))
vala11=spark.sparkContext.parallelize(List(("b",2),("c",1),("d",2)),2)
valx=a.subtract(a11)//.collect()
x.foreach(println)

x是(“a”,1),(“a”,1)

11、sample算子
sample 将 RDD 这个集合内的元素进行采样,获取所有元素的子集。用户可以设定是否有放回的抽样、百分比、随机种子,进而决定采样方式。内部实现是生成 SampledRDD(withReplacement, fraction, seed)。
函数参数设置:
参数说明:
withReplacement :取样后是否将元素放回。
true:元素放回 ,返回的子集会有重复,可以被多次抽样;
false:元素不放回 ,返回的子集没有重复
fraction:期望样本的大小作为RDD大小的一部分,
seed:随机数生成器的种子

    val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9))
    rdd1.sample(false,0.6).collect().mkString(",").map(print)// 输出 2,3,6,8,9
    rdd1.sample(true,0.6).collect().mkString(".").map(print) // 输出 2.4.4.6
    12、takeSample算子

takeSample()函数和上面的sample函数是一个原理,但是不使用相对比例采样,而是按设定的采样个数进行采样,同时返回结果不再是RDD,而是相当于对采样后的数据进行
Collect(),返回结果的集合为单机的数组。
rdd1.sample(false,5).collect().mkString(".").map(print)

sortBy

该函数最多可以传三个参数:
第一个参数是一个函数,该函数的也有一个带T泛型的参数,返回类型和RDD中元素的类型是一致的;
第二个参数是ascending,从字面的意思大家应该可以猜到,是的,这参数决定排序后RDD中的元素是升序还是降序,默认是true,也就是升序;
第三个参数是numPartitions,该参数决定排序后的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

sortByKey函数实现以及使用
sortByKey函数作用于Key-Value形式的RDD,并对Key进行排序。
varpairs=spark.sparkContext.parallelize(Array(("a",0),("b",0),("c",3),("d",6),("e",0),("f",0),("g",3),("h",6)),2);
pairs.sortByKey(true,3).collect().foreach(println);

从函数的实现可以看出,它主要接受两个函数,含义和sortBy一样,这里就不进行解释了。该函数返回的RDD一定是ShuffledRDD类型的,因为对源RDD进行排序,必须进行Shuffle操作,而Shuffle操作的结果RDD就是ShuffledRDD。其实这个函数的实现很优雅,里面用到了RangePartitioner,它可以使得相应的范围Key数据分到同一个partition中,然后内部用到了mapPartitions对每个partition中的数据进行排序,而每个partition中数据的排序用到了标准的sort机制,避免了大量数据的shuffle。下面对sortByKey的使用进行说明:

五、Cache型

13、cache算子  
cache 将 RDD 元素从磁盘缓存到内存。

14、persist算子
persist 函数对 RDD 进行缓存操作。数据缓存在哪里依据 StorageLevel 这个枚举类型进行确定。 有以下几种类型的组合(见10), DISK 代表磁盘,MEMORY 代表内存, SER 代表数据是否进行序列化存储。

2)Key-Value数据类型的Transfromation算子

一、输入分区与输出分区一对一

15、mapValues算子
mapValues :针对(Key, Value)型数据中的 Value 进行 Map 操作,而不对 Key 进行处理。

varpairs=spark.sparkContext.parallelize(Array((“a”,0),(“b”,0),(“c”,3),(“d”,6),(“e”,0),(“f”,0),(“g”,3),(“h”,6)),2);
pairs.mapValues(T=>T+3).collect().foreach(println);

二、对单个RDD或两个RDD聚集

单个RDD聚集

16、combineByKey算子

该函数用于将RDD[K,V]转换成RDD[K,C],这里的V类型和C类型可以相同也可以不同。
其中的参数:
createCombiner:组合器函数,用于将V类型转换成C类型,输入参数为RDD[K,V]中的V,输出为C
mergeValue:合并值函数,将一个C类型和一个V类型值合并成一个C类型,输入参数为(C,V),输出为C
mergeCombiners:合并组合器函数,用于将两个C类型值合并成一个C类型,输入参数为(C,C),输出为C
numPartitions:结果RDD分区数,默认保持原有的分区数
partitioner:分区函数,默认为HashPartitioner

val rdd1 = sc.makeRDD(Array((“A”, 1), (“A”, 2), (“B”, 3), (“B”, 1), (“B”, 2), (“C”, 1)), 2)
val rdd2 = rdd1.combineByKey(
(v: Int) => v +"",
(c: String, v:Int) => c + “@” + v,
(c1: String, c2: String) => c1 + “$” +c2
)
println(rdd2.collect().toBuffer)
结果:ArrayBuffer((B,3
$1_@2), (A,1_@2), (C,1_))

算子的计算过程:
(1)分析以A为key的元素:
元素(“A”, 1)在第一个分区中的key是第一次出现所以被第一个参数应用得到=>(“A”, 1_);
(“A”, 2)的key出现过一次并且被第一个参数应用过,所以使用第二个参数应用到(“A”, 1_)和(“A”, 2)上得到=> (A,1_@2);
然后以A为key的元素没有在其它分区中出现过了所以key为A的计算结果就是:(A,1_@2);
(2)分析以B为key的元素:
元素(“B”, 3)在第一个分区中的key是第一次出现所以被第一个参数应用得到=>(“B”, 3_);
以B为key的元素在第一个分区中已经没有,但是第二个分区中存在,(“B”, 1)以B为key第一次出现,得到=>(“B”, 1_)
(“B”, 2)在是以B为key第二个分区中出现过并且被第一个参数应用过,因此使用第二个参数应用已存在的和新加入的得到=>(B, 1_@2)
第一个分区得到的是(“B”, 3_),第二个分区得到的是(B, 1_@2),然后用第三个参数应用到这两个结果中(汇总分组统计),至于顺序问题就看哪个分区先计算完成,这里是第一个分区先计算完成因此得到=>(B, 3_$1_@2)
(3)分析以C为key的元素:
以C为key的元素只在第二个分区中出现过,因此只会被第一个参数所应用得到(C,1_)即是以C为key的计算结果

17、reduceByKey算子
对数据集key相同的值,都被使用指定的reduce函数聚合到一起.这说明reduceByKey的作用域是key-value类型的键值对,并且是只对每个key的value进行处理,如果含有多个key的话,那么就对多个values进行处理。这里的函数是我们自己传入的,也就是说是可人为控制的
varpairs=spark.sparkContext
.parallelize(Array((“A”,1),
(“A”,2),(“B”,3),(“B”,1),(“B”,2),(“C”,1)),2);
varrdd2=pairs.reduceByKey(+)

18、partitionBy算子

partitionBy函数对RDD进行分区操作

varpairs=spark.sparkContext
.parallelize(Array((“B”,1),
(“A”,2),(“B”,3),(“B”,1),(“B”,2),(“A”,1)),2);
varrdd2=pairs.partitionBy(neworg.apache.spark.HashPartitioner(2))

两个RDD聚集

19、Cogroup算子
cogroup函数将两个RDD进行协同划分

val rdd1 = sc.parallelize(Array(("aa",1),("bb",2),("cc",6))) 
val rdd2 = sc.parallelize(Array(("aa",3),("dd",4),("aa",5)))

val rdd3 = rdd1.cogroup(rdd2).collect()

(aa,(CompactBuffer(1),CompactBuffer(3, 5)))
(dd,(CompactBuffer(),CompactBuffer(4)))
(bb,(CompactBuffer(2),CompactBuffer()))
(cc,(CompactBuffer(6),CompactBuffer()))

三、连接

20、join算子
join 对两个需要连接的 RDD 进行 cogroup函数操作,将相同 key 的数据能够放到一个分区,在 cogroup 操作之后形成的新 RDD 对每个key 下的元素进行笛卡尔积的操作,返回的结果再展平,对应 key 下的所有元组形成一个集合。最后返回 RDD[(K, (V, W))]。
join 的 函 数 实 现, 本 质 是通 过 cogroup 算 子 先 进 行 协 同 划 分, 再 通 过flatMapValues 将合并的数据打散。

spark中的join算子使用的是内连接的join,以某一个表为基础,KEY相同的打印出来,不相同的不打印

这几种 join 算子和 sql 中的 join 类似,join 相当于内连接,fullOuterJoin 相当于全连接,leftOuterJoin 相当于左连接,rightOuterJoin 相当于右连接。

val visit =
spark.sparkContext.parallelize(List(("index.html","1.2.3.4"),("about.html","3,4,5,6"),("index.html","1.3.3.1"),("hello.html","1,2,3,4")),2);

val page = spark.sparkContext.parallelize(List(("index.html","home"),("about.html","about"),("hi.html","2.3.3.3")),2);


visit.join(page).foreach(println(_))

(about.html,(3,4,5,6,about))

(index.html,(1.2.3.4,home))

(index.html,(1.3.3.1,home))

page.join(visit).foreach(println(_))

(about.html,(about,3,4,5,6))

(index.html,(home,1.2.3.4))

(index.html,(home,1.3.3.1))

21、leftOutJoin和 rightOutJoin算子
leftOuterJoin类似于SQL中的左外关联left outer join,返回结果以前面的RDD为主,关联不上的记录为空。只能用于两个RDD之间的关联,如果要多个RDD关联,多关联几次即可。

参数numPartitions用于指定结果的分区数

参数partitioner用于指定分区函数

var rdd1 = sc.makeRDD(Array(("A","1"),("B","2"),("C","3")),2)
var rdd2 = sc.makeRDD(Array(("A","a"),("C","c"),("D","d")),2)

scala> rdd1.leftOuterJoin(rdd2).collect
res11: Array[(String, (String, Option[String]))] = Array((B,(2,None)), (A,(1,Some(a))), (C,(3,Some(c))))

3)Action算子

一、无输出

22、foreach算子
foreach 对 RDD 中的每个元素都应用 f 函数操作,不返回 RDD 和 Array, 而是返回Uint

二、HDFS
    23、saveAsTextFile算子
函数将数据输出,存储到 HDFS 的指定目录

rdd1.saveAsTextFile(“file:///tmp/lxw1234.com”)将文件保存到本地文件系统,那么只会保存在Executor所在机器的本地目录。

24、saveAsObjectFile算子

三、Scala集合和数据类型
    25、collect算子
collect 相当于 toArray, toArray 已经过时不推荐使用, collect 将分布式的 RDD 返回为一个单机的 scala Array 数组。在这个数组上运用 scala 的函数式操作。

26、collectAsMap算子
collectAsMap对(K,V)型的RDD数据返回一个单机HashMap。 对于重复K的RDD元素,后面的元素覆盖前面的元素

collectAsMap:将结果一map方式展示

val rdd = sc.parallelize(List((“a”,2),(“b”,10),(“x”,22)), 2)
rdd.collectAsMap
scala.collection.Map[String,Int] = Map(b -> 10, a -> 2, x -> 22)

27、reduceByKeyLocally算子
实现的是先reduce再collectAsMap的功能,先对RDD的整体进行reduce操作,然后再收集所有结果返回为一个HashMap。

var rdd1 = sc.makeRDD(Array((“A”,0),(“A”,2),(“B”,1),(“B”,2),(“C”,1)))

rdd1.reduceByKeyLocally((x,y) => x + y)
res90: scala.collection.Map[String,Int] = Map(B -> 3, A -> 2, C -> 1)

28、lookup算子
lookup(key:K):Seq[V]
Lookup函数对(Key,Value)型的RDD操作,返回指定Key对应的元素形成的Seq。 这个函数处理优化的部分在于,如果这个RDD包含分区器,则只会对应处理K所在的分区,然后返回由(K,V)形成的Seq。 如果RDD不包含分区器,则需要对全RDD元素进行暴力扫描处理,搜索指定K对应的元素。

29、count算子
count 返回整个 RDD 的元素个数。

30、top算子

top可返回最大的k个元素。 函数定义如下。
top(num:Int)(implicit ord:Ordering[T]):Array[T]
相近函数说明如下。
·top返回最大的k个元素。
·take返回最小的k个元素。
·takeOrdered返回最小的k个元素,并且在返回的数组中保持元素的顺序。
·first相当于top(1)返回整个RDD中的前k个元素,可以定义排序的方式Ordering[T]。
返回的是一个含前k个元素的数组。

31、reduce算子
reduce函数相当于对RDD中的元素进行reduceLeft函数的操作。 函数实现如下。
通过函数func聚集数据集中的所有元素,这个函数必须是关联性的,确保可以被正确的并发执行
scala> val rdd1 = sc.makeRDD(1 to 10)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at makeRDD at :24
scala> rdd1.reduce(+)
res3: Int = 55

  1. scala> var rdd2 = sc.makeRDD(Array((“A”,0),(“A”,2),(“B”,1),(“B”,2),(“C”,1)))
  2. rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[38] at makeRDD at :21
  3. scala> rdd2.reduce((x,y) => {(x._1 + y._1,x._2 + y._2) })
  4. res21: (String, Int) = (CBBAA,6)

32、fold算子

用法:flod(num)(func)

println(sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8), 1).fold(10)(_+_)) //56
println(sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8), 2).fold(10)(_+_)) //66
println(sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8), 3).fold(10)(_+_)) //76

第一个就是一个分区,元素为:1,2,3,4,5,6,7,8 ,计算过程是现在该分区进行add求和(zeroValue参与计算),所以是1+2+3+4+5+6+7+8+10完成分区的计算,结果为46,最后在合并所有分区结果,由于只有一个分区,所以计算过程为46+10=56!

第二个两个分区:1,2,3,4和5,6,7,8两个分区,对分区分别进行计算为1+2+3+4+10=20,5+6+7+8+10=36,最后合并分区结果为20+36+10=66,

33、aggregate算子

aggregate先对每个分区的所有元素进行aggregate操作,再对分区的结果进行fold操作。
aggreagate与fold和reduce的不同之处在于,aggregate相当于采用归并的方式进行数据聚集,这种聚集是并行化的。 而在fold和reduce函数的运算过程中,每个分区中需要进行串行处理,每个分区串行计算完结果,结果再按之前的方式进行聚集,并返回最终聚集结果。

mkString

.mkString()方法的使用:

mkString(seq:String)方法是将原字符串使用特定的字符串seq分割。
mkString(statrt:String,seq:String,end:String)方法是将原字符串使用特定的字符串seq分割的同时,在原字符串之前添加字符串start,在其后添加字符串end。

withColumn

利用withColumn函数就能实现对dataframe中列的添加

val rawData_2 = rawData_1.withColumn(“auc”, addCol_2(rawData_1(“tag”)))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值