在Spark中,数据以弹性分布式数据集(Resilient Distributed Dataset)的形式存在。对RDD的操作主要分为转化操作和行动操作两种。Spark可以将RDD中的数据分发到集群上,以此并行化执行相关的操作。
最直接的创建方式就是通过SprakContext的parallelize()方法,将一个已有集合变为RDD:
>>> lines = sc.parallelize(['word1', 'word2', 'word3'])
当我们读取一个外部数据的时候,它在Spark中就是以RDD的形式存在的。比如我们读取一个Iris数据集:
>>> iris = spark.read.csv('/Users/data/iris.csv', header=True)
>>> iris.show(5)
+------------+-----------+------------+-----------+-----------+
|Sepal Length|Sepal Width|Petal Length|Petal Width| Label|
+------------+-----------+------------+-----------+-----------+
| 5.1| 3.5| 1.4| 0.2|Iris-setosa|
| 4.9| 3.0| 1.4| 0.2|Iris-setosa|
| 4.7| 3.2| 1.3| 0.2|Iris-setosa|
| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 5.0| 3.6| 1.4| 0.2|Iris-setosa|
+------------+-----------+------------+-----------+-----------+
only showing top 5 rows
此时的iris就是一个RDD。接下来分别对这个RDD进行转化操作以及求值操作。
RDD的转化操作会返回一个新的RDD,比如之前用来进行条件过滤的filter()。行动操作指的是操作结果需要返回给驱动器或者写入外部系统,比如count()。一般返回值为RDD的操作都是转化操作,返回值是其他类型的操作是行动操作。
RDD一旦创建就无法修改。因此转化操作并不会影响到原本的RDD,它只会产生一个新的RDD。因此需要一个的变量去接收这个RDD,或者直接将结果覆盖到原始RDD上。比如修改iris的列名,调用withColumnRenamed()修改列名之后,需要将结果覆盖到iris上。如果我们想筛选出Label为“Iris-setosa”,同时花萼宽度为3.1的数据,我们需要一个新的变量来接收filter()针对两个条件进行过滤的结果:
>>> iris = iris.withColumnRenamed('Sepal Width', 'Sepal_Width')
>>> setosa = iris.filter((iris.Label == 'Iris-setosa') & (iris.Sepal_Width == 3.1))
>>> setosa.show(5)
+------------+-----------+------------+-----------+-----------+
|Sepal Length|Sepal_Width|Petal Length|Petal Width| Label|
+------------+-----------+------------+-----------+-----------+
| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
| 4.8| 3.1| 1.6| 0.2|Iris-setosa|
| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
+------------+-----------+------------+-----------+-----------+
转化操作可以一次对多个RDD进行操作,比如使用union()对两个RDD取交集:
>>> versicolor = iris.filter(iris.Label == 'Iris-versicolor')
>>> virginica = iris.filter(iris.Label == 'Iris-virginica')
>>> vers_and_virg = versicolor.union(virginica)
转化操作会基于已有RDD生成新的RDD,Spark会使用血缘图(lineage graph)记录这个过程中的依赖关系。血缘图可以用于优化Spark的工作流程,同时也可以在持久化的RDD丢失部分数据时进行恢复。
之所以要区分转化操作和行动操作,很重要的一个原因是Spark对RDD的转化操作都是惰性求值的。比如上面的操作中,只有在show()运行时,Spark才会执行之前基于iris的所有转化操作。如果用户有多个转化操作,Spark会将这些操作的相关信息都记录下来,在需要执行的时候一起执行。因此,惰性求值可以将一些操作进行合并,从而减少计算数据的步骤。从这个角度来看,RDD其实更应该被看作一种数据的“转化操作计划”,而不是一个数据集。
行动操作会将最终的结果返回给驱动器,或者写入外部的存储系统。因此行动操作会在计算时,运行它所依赖的所有转化操作。
比如对之前通过转化操作生成的RDD进行行数统计:
>>> vers_and_virg.count()
100
每一次的行动操作都会重新计算RDD,因此最好将需要复用的中间结果持久化。
上一篇:PySpark大数据分析(1):Spark的安装与文件读取
下一篇:PySpark大数据分析(3):使用Python操作RDD