spark 学习随笔(二)

版权声明:转载请注明出处。 https://blog.csdn.net/b285795298/article/details/85341428

RDD 编程

弹性分布式数据集(Resilient Distributed Dataset,简称 RDD。RDD 其实就是分布式的元素集合。
在 Spark 中,对数据的所有操作不外乎创建 RDD转化已有 RDD 以及调用 RDD 操作进行求值
而在这一切背后,Spark 会自动将RDD 中的数据分发到集群上,并将操作并行化执行

RDD到底是什么?

RDD是一个只读的有属性的数据集。属性用来描述当前数据集的状态,数据集是由数据的分区(partition)组成,并(由block)映射成真实数据。RDD属性包括名称、分区类型、父RDD指针、数据本地化、数据依赖关系等,主要属性可以分为3类:

  1. 与其他RDD 的关系(parents)
    Spark 使用谱系图(lineage graph)来记录不同 RDD 之间的依赖关系(比如转化操作)。
    在这里插入图片描述

  2. 数据(partitioner,checkpoint,storagelevel,iterator)

  3. RDD自身属性(rddname,sparkcontext,sparkconf)

转载一个例子https://www.jianshu.com/p/dd7c7243e7f9?from=singlemessage
scala:
在这里插入图片描述

简单解释下这几行代码:
第一行,从HDFS上读取in.txt文件,创建了第一个RDD
第二行,按空格分词,扁平化处理,生成第二个RDD,(转化已有RDD)每个词计数为1,生成了第三个RDD(转化已有RDD)。这里可能有人会问,为什么生成了两个RDD呢,因为此行代码RDD经过了两次算子转换(transformation)操作。
第三行,按每个词分组,累加求和,生成第四个RDD
第四行,将Wordcount统计结果输出到HDFS

整个产生过程如下图所示:
在这里插入图片描述
转化操作和行动操作的区别在于 Spark 计算 RDD 的方式不同。虽然你可以在任何时候定义新的 RDD,但 Spark 只会惰性计算这些 RDD。它们只有第一次在一个行动操作中用到时,才会真正计算。一旦 Spark 了解了完整的转化操作链之后,它就可以只计算求结果时真正需要的数据
默认情况下,Spark 的 RDD 会在你每次对它们进行行动操作时重新计算。如果想在多个行动操作中重用同一个 RDD,可以使用 RDD.persist() 让 Spark 把这个 RDD 缓存下来(以分区方式存储到集群中的各机器上)。

总的来说,每个 Spark 程序或 shell 会话都按如下方式工作。
(1) 从外部数据创建出输入 RDD。
(2) 使用诸如 filter() 这样的转化操作对 RDD 进行转化,以定义新的 RDD。
(3) 告诉 Spark 对需要被重用的中间结果 RDD 执行 persist() 操作。
(4) 使用行动操作(例如 count() 和 first() 等)来触发一次并行计算,Spark 会对计算进行优化后再执行。

创建 RDD

Spark 提供了两种创建 RDD 的方式:
1、读取外部数据集。(如之前的例子)
2、在驱动器程序中对一个集合进行并行化。
方式2用得并不多,毕竟这种方式需要把你的整个数据集先放在一台机器的内存中。

#把程序中一个已有的集合传给 SparkContext 的 parallelize()方法
lines = sc.parallelize(["pandas", "i don't like pandas"])

RDD 操作

RDD 支持两种操作:转化操作和行动操作
RDD 的转化操作是返回一个新的 RDD 的操作,比如 map() 和 filter() ,

#用 Python 进行 union() 转化操作
errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD)

union() 与 filter() 的不同点在于它操作两个 RDD 而不是一个。转化操作可以操作任意
数量的输入 RDD。

而行动操作则是向驱动器程序返回结果或把结果写入外部系统的操作,会触发实际的计算,比如 count() 和 first() 。Spark 对待转化操作和行动操作的方式很不一样

#在 Python 中使用行动操作对错误进行计数
print "Input had " + badLinesRDD.count() + " concerning lines"
print "Here are 10 examples:"
for line in badLinesRDD.take(10):
print line

这个例子在驱动器程序中使用 take() 获取了 RDD 中的少量元素。然后在本地遍历这些元素,并在驱动器端打印出来。

RDD 有一个 collect() 函数,可以用来获取整个 RDD 中的数据。如果你的程序把 RDD 筛选到一个很小的规模,并且你想在本地处理这些数据时,就可以使用它。记住,只有当你的整个数据集能在单台机器的内存中放得下时,才能使用 collect() ,因此, collect() 不能用在大规模数据集上。
在大多数情况下,RDD 不能通过 collect() 收集到驱动器进程中,因为它们一般都很大。通常要把数据写到诸如 HDFS 或 Amazon S3 这样的分布式的存储系统中。你可以使用 saveAsTextFile() 、 saveAsSequenceFile() ,或者任意的其他行动操作来把 RDD 的数据内容以各种自带的格式保存起来。

如果对于一个特定的函数是属于转化操作还是行动操作感到困惑,你可以看看它的返回值类型:转化操作返回的是 RDD,而行动操作返回的是其他的数据类型。

基本 RDD
转化操作:map() 、flatMap()和 filter()
在这里插入图片描述若希望对每个输入元素生成多个输出元素。实现该功能的操作叫作 flatMap() 。
和 map() 类似,flatMap() 的函数被分别应用到了输入 RDD 的每个元素上

lines = sc.parallelize(["hello world", "hi"])
words = lines.flatMap(lambda line: line.split(" "))
words.first() # 返回"hello",若用map(),返回则是“hello” “world”

在这里插入图片描述

  1. union(other) ,返回一个包含两个 RDD 中所有元素的 RDD

  2. intersection(other) 方法,只返回两个 RDD 中都有的元素( 单 个 RDD 内 的 重 复 元 素 也 会 一 起
    移 除 )。intersection() 与 union() 的概念相似, intersection() 的性能却要差很多,因为它需要通过网络混洗数据来发现共有的元素。

  3. subtract(other) 函 数 接 收 另 一 个 RDD 作 为 参 数, 返 回一个由只存在于第一个 RDD
    中而不存在于第二个 RDD 中的所有元素组成的 RDD。和intersection() 一样,它也需要数据混洗。

  4. cartesian(other) 转化操作会返回所有可能的 (a, b) 对,其中 a 是源 RDD 中的元素,而 b 则来自另一个
    RDD。笛卡儿积在我们希望考虑所有可能的组合的相似度时比较有用,比如计算各用户对各种产品的预期兴趣程度。我们也可以求一个 RDD
    与其自身的笛卡儿积,这可以用于求用户相似度的应用中。不过要特别注意的是,求大规模 RDD 的笛卡儿积开销巨大。

在这里插入图片描述在这里插入图片描述在这里插入图片描述

行动操作

在这里插入图片描述fold() 和 reduce() 都要求函数的返回值类型需要和我们所操作的 RDD 中的元素类型相同。这很符合像 sum 这种操作的情况。但有时我们确实需要返回一个不同类型的值。例如,在计算平均值时,需要记录遍历过程中的计数以及元素的数量,这就需要我们返回一个二元组。可以先对数据使用 map() 操作,来把元素转为该元素和 1 的二元组,也就是我们所希望的返回类型。这样 reduce() 就可以以二元组的形式进行归约了。

可以用 aggregate() 来计算 RDD 的平均值,来代替 map() 后面接 fold() 的方式。

sumCount = nums.aggregate((0, 0),
(lambda acc, value: (acc[0] + value, acc[1] + 1),
(lambda acc1, acc2: (acc1[0] + acc2[0], acc1[1] + acc2[1]))))
return sumCount[0] / float(sumCount[1])

猜你喜欢

转载自blog.csdn.net/b285795298/article/details/85341428
今日推荐