本文参考

在阅读了《Spark快速大数据分析》动物书后,大概了解到了spark常用的api,不过书中并没有给予所有api具体的示例,而且现在spark的最新版本已经上升到了2.4.5,动物书中的spark版本还停留在1.2.0,所以就有了这篇文章,在最新的2.4.5版本下测试常用的api

由于spark的惰性计算特性,RDD只有在第一次行动操作中被用到时才会真正进行计算,因此我打算将文章内容分为"转化操作API"和"行动操作API"两部分,同时因为pair RDD(RDD中的元素是键值对)的部分api较为特殊,所以我打算单独再写一篇文章

本文仅介绍转化操作API,前5个api —— map()、flatMap()、filter()、distinct()、sample()是针对一个RDD的转化操作,后续的api —— union()、intersection()、subtract()、cartesion()是针对两个RDD的转化操作

RDD行动操作API归纳:https://www.cnblogs.com/kuluo/p/12550938.html

Pair RDD转化操作API归纳:https://www.cnblogs.com/kuluo/p/12558563.html

Pair RDD行动操作API归纳:https://www.cnblogs.com/kuluo/p/12567221.html

环境

idea + spark 2.4.5 + scala 2.11.12

RDD均通过SparkContext的parallelize()函数创建

map()函数

目的:

将函数应用于RDD中的每个元素,将返回值构成新的RDD

转化前后的RDD的元素类型可以不同(比如经典的WordCount示例中转化为了键值对元素)

代码:

val testList = List(1, 2, 3, 3)
val testRdd = sc.parallelize(testList)
testRdd.map(ele => ele * ele).foreach(ele => print(s"$ele "))

输出:

1 4 9 9

更高效的操作:

每个RDD被分为多个分区,这些分区在集群的不同节点上运行,可以使用mapPartitions()函数,将转化操作作用于每个分区的元素上,这种方法还可以为每个分区创建一个JDBC连接,而不是为每一个元素创建一个连接(此处不做示例)

mapPartitions()函数有两个参数,第一个参数接收一个函数,和map()函数相同,第二个参数为preservesPartitioning,默认值为false,仅当我们对pair RDD进行转化操作,并且没有修改键时设置为true

val testList = List(1, 2, 3, 3)
val testRdd = sc.parallelize(testList)
testRdd.mapPartitions(partition =>

  partition.map(

    ele => {

      ele * ele

    }
)).foreach(ele => print(s"$ele "))

flatMap()函数

目的:

将函数应用于RDD中的每个元素,将返回的迭代器的所有内容构成新的RDD,我们也常常说成是"压扁"

"压扁"这个词可能听上去不大好理解,我们提供给flatMap()的函数分别应用到RDD的那个元素上,不过返回的不是一个元素,而是一个返回值序列的迭代器,但输出的RDD不是由迭代器组成,得到的是一个包含各个迭代器可以访问的所有元素的RDD

转化前后的RDD的元素类型不变

代码:

val testList = List(1, 2, 3, 3)
val testRdd = sc.parallelize(testList)
testRdd.flatMap(ele => {

  ele.to(5)
}).foreach(ele => print(s"$ele "))

我们也可以手动返回迭代器,这段代码也类似于

val testList = List(Range(1, 6), Range(2, 6), Range(3, 6), Range(3, 6))
val testRdd = sc.parallelize(testList)
testRdd.flatMap(_.iterator).foreach(ele => print(s"$ele "))

输出:

1 2 3 4 5 2 3 4 5 3 4 5 3 4 5

filter()函数

目的:

返回一个由传给filter()函数的元素组成的RDD,当函数返回值为true时,保留该元素,可以理解为 "被过滤"出来

代码:

val testList = List(1, 2, 3, 3)
val testRdd = sc.parallelize(testList)
testRdd.filter(ele => ele > 2).foreach(ele => print(s"$ele "))

输出:

3 3

更高效的操作:

通过过滤操作后,RDD中的元素减少,可以在filter()操作后执行coalesce()函数进行分区合并,第一个参数指定分区数,当指定的分区数大于当前RDD的分区数时不会进行合并,当前分区数不变(除非指定第二参数shuffle为true,默认为false),当指定的分区数小于当前的RDD的分区数时会进行合并,并且不会进行shuffle(尽量不要指定极端的情况,如指定合并后的分区数为1)

val testList = List(1, 2, 3, 3)
val testRdd = sc.parallelize(testList)
testRdd.filter(ele => ele > 2).coalesce(5).foreach(ele => print(s"$ele "))

distinct()函数

目的:

去重,因为会进行shuffle,所以不推荐此操作

代码:

val testList = List(1, 2, 3, 3)
val testRdd = sc.parallelize(testList)
testRdd.distinct().foreach(ele => print(s"$ele "))

输出:

1 2 3

sample()函数

目的:

对RDD进行采样

代码:

val testList = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
val testRdd = sc.parallelize(testList)
testRdd.sample(false, 0.9).foreach(ele => print(s"$ele "))

第一个参数withReplacement指定false时,第二个参数fraction必须为 [ 0 , 1 ] 之间,表示每个元素被选中的可能性

按照该示例,也有人将该函数理解为,从所有元素中抽取90%返回,但是在源码中我们可以看到:

without replacement: probability that each element is chosen; fraction must be [0, 1]

This is NOT guaranteed to provide exactly the fraction of the count of the given [[RDD]]

因此这种理解方式我认为是错误的

输出:

0 1 2 3 5 6 8 9(不一定)

疑点:

当第一个参数withReplacement指定true时,第二个参数fraction并不要求一定小于1,源码中注释为"with replacement: expected number of times each element is chosen; fraction must be greater than or equal to 0"

val testList = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
val testRdd = sc.parallelize(testList)
testRdd.sample(true, 3).foreach(ele => print(s"$ele "))

输出:0 0 1 1 1 1 2 2 2 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5 6 6 7 7 8 9 9

目前不大理解是如何在采样的,希望各位看官大大能在评论区发表看法哈

unon()函数

目的:

相当于集合运算的并集,生成一个包含两个RDD中所有元素的RDD,要求两个RDD的元素类型相同

代码:

val testList1 = List(1, 2, 3)
val testList2 = List(3, 4, 5)
val testRdd1 = sc.parallelize(testList1)
val testRdd2 = sc.parallelize(testList2)
testRdd1.union(testRdd2).foreach(ele => print(s"$ele "))

输出:

1 2 3 3 4 5

intersection()函数

目的:

相当于集合运算的交集,求两个RDD共同的元素的RDD,要求两个RDD的元素类型相同

代码:

val testList1 = List(1, 2, 3)
val testList2 = List(3, 4, 5)
val testRdd1 = sc.parallelize(testList1)
val testRdd2 = sc.parallelize(testList2)
testRdd1.intersection(testRdd2).foreach(ele => print(s"$ele "))

输出:

3

subtract()函数

目的:

想当于集合运算的差集,移除一个RDD中的内容,要求两个RDD的元素类型相同

代码:

val testList1 = List(1, 2, 3)
val testList2 = List(3, 4, 5)
val testRdd1 = sc.parallelize(testList1)
val testRdd2 = sc.parallelize(testList2)
testRdd1.subtract(testRdd2).foreach(ele => print(s"$ele "))

输出:

1 2

cartesion()函数

目的:

与另一个RDD的笛卡尔积

代码:

val testList1 = List(1, 2, 3)
val testList2 = List('a', 'b', 'c')
val testRdd1 = sc.parallelize(testList1)
val testRdd2 = sc.parallelize(testList2)
testRdd1.cartesian(testRdd2).foreach(ele => print(s"$ele "))

输出:

(1,a) (1,b) (1,c) (2,a) (2,b) (2,c) (3,a) (3,b) (3,c)

Spark学习摘记 —— RDD转化操作API归纳的更多相关文章

  1. Spark学习摘记 —— RDD行动操作API归纳

    本文参考 参考<Spark快速大数据分析>动物书中的第三章"RDD编程",前一篇文章已经概述了转化操作相关的API,本文再介绍行动操作API 和转化操作API不同的是, ...

  2. Spark学习摘记 —— Pair RDD转化操作API归纳

    本文参考 参考<Spark快速大数据分析>动物书中的第四章"键值对操作",由于pair RDD的一些特殊操作,没有和前面两篇的API归纳放在一起做示例 前面的几个api ...

  3. Spark学习摘记 —— Pair RDD行动操作API归纳

    本文参考 参考<Spark快速大数据分析>动物书中的第四章"键值对操作",本篇是对RDD转化操作和行动操作API归纳的最后一篇 RDD转化操作API归纳:https:/ ...

  4. Spark学习之RDD编程(2)

    Spark学习之RDD编程(2) 1. Spark中的RDD是一个不可变的分布式对象集合. 2. 在Spark中数据的操作不外乎创建RDD.转化已有的RDD以及调用RDD操作进行求值. 3. 创建RD ...

  5. Spark学习之RDD编程总结

    Spark 对数据的核心抽象——弹性分布式数据集(Resilient Distributed Dataset,简称 RDD).RDD 其实就是分布式的元素集合.在 Spark 中,对数据的所有操作不外 ...

  6. spark学习(10)-RDD的介绍和常用算子

    RDD(弹性分布式数据集,里面并不存储真正要计算的数据,你对RDD的操作,他会在Driver端转换成Task,下发到Executor计算分散在多台集群上的数据) RDD是一个代理,你对代理进行操作,他 ...

  7. Spark学习之RDD

    RDD概述 什么是RDD RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.里面的元素可并行计算的集合 ...

  8. Spark学习笔记——RDD编程

    1.RDD——弹性分布式数据集(Resilient Distributed Dataset) RDD是一个分布式的元素集合,在Spark中,对数据的操作就是创建RDD.转换已有的RDD和调用RDD操作 ...

  9. Spark学习(2) RDD编程

    什么是RDD RDD(Resilient Distributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.弹性.里面的元素可并行计算的集合 RDD允 ...

随机推荐

  1. 2020.10.6 ThreadLocal

    在多线程环境下,每个线程都有自己的数据.一个线程使用自己的局部变量比使用全局变量要好,因为局部变量不会被其他线程改变. 但是局部变量也存在问题--在函数调用的时候,传递起来很麻烦: def proce ...

  2. 非关系型数据库redis

    Redis初始 redis中不区分字母的大小写 查看端口 ss tnlp 安装 编译安装(需要先配置epel源) yum install -y redis 编译安装 wget https://down ...

  3. @Async的用法和示例

    @Async 注解的用法和示例 目录 @Async 注解的用法和示例 背景 异步调用 @Async介绍 在Spring中启用@Async 示例一:基本使用方式 示例二:在同一个类中调用异步方法 示例三 ...

  4. 『现学现忘』Docker相关概念 — 6、虚拟化技术分类

    目录 1.按照虚拟化的程度分类 (1)完全虚拟化技术 (2)半虚拟化技术 (3)完全虚拟化与半虚拟化优缺点 2.从虚拟化架构分类 (1)寄居架构 (2)裸金属架构 虚拟化是一个广义的术语,是指计算元件 ...

  5. 二进制部署1.23.4版本k8s集群-2-安装DNS服务

    2.安装DNS服务 为什么要安装bind9? K8S中,使用Ingress进行7层流量调度,需要使用域名,进行7层调度. 以前使用绑定host的方法,来进行域名和IP地址的解析. 在K8S里,没有好的 ...

  6. PHP实现二维数组(或多维数组)转换成一维数组

    1 array_reduce函数法 用array_reduce()函数是较为快捷的方法: $result = array_reduce($user, function ($result, $value ...

  7. 微信退款通知,退款回调数据解密.SHA256签名AEAD_AES_256_GCM解密

    $xmlResult = file_get_contents("php://input");//获取微信的数据$result = $this->xmlToArray($xml ...

  8. egg项目eslint不识别链式操作符的解决方法

    项目用到链式?.结果eslint一直提示 const permissionHandleArr = positionPermissionHandle.map(item => item.permis ...

  9. LGP2414题解

    难不成是我后缀自动机学魔怔了,AC 自动机都能套上线段树 题意:给你一颗 Trie,每次询问两个节点 \(u,v\),\(u\) 代表的字符串在 \(v\) 代表的字符串中出现了多少次. 让我们思考一 ...

  10. 关于C#窗体应用程序的一点总结

    1.在窗体Form在Form_Load函数中写了过多的界面初始化语句导致界面卡顿时 解决方法为:将一些初始化语句写在public Form()函数中,会大大加快程序的初始化加载速度. 2.为butto ...