Spark RDD 创建与转换算子详解

1. 引言

本文通过五个完整的 PySpark 示例,系统讲解 RDD 的三种创建方式以及 Value 类型转换算子的核心用法,帮助读者快速掌握 Spark 编程的基础操作。

2. RDD 的创建方式

Spark 中创建 RDD 有三种方法:从集合中创建、从外部存储创建、从其他 RDD 创建。转换算子返回的一定是 RDD,行动算子返回的一定不是 RDD。

2.1 从集合中创建 RDD

使用 parallelize() 方法可以分发本地 Python 集合以形成 RDD。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo01_RDD_Create").setMaster("local[*]")
sc = SparkContext(conf=conf)
2、RDD的创建
(1)从集合中创建RDD
parallelize():分发本地 Python 集合以形成 RDD
rdd1 = sc.parallelize([1, 2, 3, 4, 5])
print(type(rdd1))  # <class 'pyspark.rdd.RDD'>
关闭环境
sc.stop()

2.2 从外部存储系统创建 RDD

可以从 HDFS、Hive、MySQL 等外部存储系统的数据集创建 RDD。以 HDFS 为例,使用 textFile() 方法。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo01_RDD_Create").setMaster("local[*]")
sc = SparkContext(conf=conf)
(2)由外部存储系统[HDFS、Hive、MySQL....]的数据集创建RDD
以HDFS为例创建RDD
textFile()
rdd2 = sc.textFile("hdfs://master:9000/wc.txt")
print(type(rdd2))  # <class 'pyspark.rdd.RDD'>
关闭环境
sc.stop()

2.3 从其他 RDD 创建

可以通过转换操作从已有 RDD 创建新的 RDD,例如使用 flatMap 算子。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo01_RDD_Create").setMaster("local[*]")
sc = SparkContext(conf=conf)
(3)从其他RDD创建(在后面讲)
wordRDD = rdd2.flatMap(lambda x: x.split(" "))
打印数据
print(wordRDD.collect())  # ['hadoop', 'hdfs', 'mapreduce', 'hadoop', 'hdfs', 'mapreduce']
查看类型
print(type(wordRDD))  # <class 'pyspark.rdd.PipelinedRDD'>
关闭环境
sc.stop()

3. Value 类型转换算子

RDD 整体上分为 Value 类型和 Key-Value 类型。本节重点讲解 Value 类型的四个常用转换算子。

3.1 map() 算子

map() 返回一个新的 RDD,该 RDD 是由每一个输入元素经过 func 函数转换后组成。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo02_TR_map").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建一个1-10数组的RDD,将所有元素都乘以2并形成一个新的RDD
rdd1 = sc.parallelize(range(1, 10), 2)
new_rdd2 = rdd1.map(lambda x: x * 2)
打印数据
list1 = new_rdd2.collect()
print(list1)
关闭环境
sc.stop()

3.2 mapPartitions() 算子

mapPartitions() 通过将一个函数应用到此 RDD 的每个分区来返回一个新的 RDD。类似于 map,但独立地在 RDD 的每一个分区上运行,因此在类型为 T 的 RDD 上运行时,func 的函数类型必须是 IteratorT => IteratorU。

假设有 N 个元素,有 M 个分区,那么 map 的函数将被调用 N 次,而 mapPartitions 将被调用 M 次,一个函数一次处理所有分区。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo03_TR_apPartitions").setMaster("local[*]")
sc = SparkContext(conf=conf)
定义一个函数
def func01(x):
l1 = []
for i in x:
l1.append(i * 2)
return l1
创建RDD
rdd1 = sc.parallelize(range(1, 11), 2)
rdd2 = rdd1.mapPartitions(func01)
打印结果
list1 = rdd2.collect()
print(list1)
关闭环境
sc.stop()

map VS mapPartitions:map() 每次处理一条数据;mapPartitions() 每次处理一个分区的数据。

3.3 mapPartitionsWithIndex() 算子

mapPartitionsWithIndex() 通过将函数应用于此 RDD 的每个分区,同时跟踪原分区的索引,返回一个新的 RDD。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo04_TR_apPartitions").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建一个RDD,使每个元素所在分区形成一个元组组成一个新的RDD
定义一个函数
def func02(index, x):
l1 = []
for i in x:
l1.append((index, i * 2))
return l1
创建RDD
rdd1 = sc.parallelize(range(1, 11), 2)
[1,2,3,4,5,6,7,8,9,10]
使每个元素跟所在分区形成一个元组组成一个新的RDD
rdd2 = rdd1.mapPartitionsWithIndex(func02)
打印
list1 = rdd2.collect()
print(list1)
关闭环境
sc.stop()

3.4 flatMap() 算子

flatMap() 将键值对 RDD 中的每个值通过 flatMap 函数,而不改变键,这也保留了原始 RDD 的分区。类似 map,但是每一个输入元素可以被映射为 0 或多个输出元素。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_flatMap").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建一个元素为1-5RDD,运用flatMap函数创建一个新的RDD
rdd1 = sc.parallelize(range(1, 6), 2)
print(rdd1.collect())  # [1, 2, 3, 4, 5]
rdd2 = rdd1.flatMap(lambda x: range(1, x + 1))
print(rdd2.collect())  # [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5]
关闭环境
sc.stop()

3.5 groupBy() 算子

groupBy() 用于分组,按照传入函数的返回值进行分组,将相同的 key 对应的值放入一个迭代器。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_groupBy").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建RDD,按照元素模2的值进行分组
rdd1 = sc.parallelize(range(1, 11), 2)
rdd2 = rdd1.groupBy(lambda x: x % 2)
list1 = rdd2.collect()
for e in list1:
print(e[0], *e[1])  # * 解包操作
关闭环境
sc.stop()

3.6 filter() 算子

filter() 返回一个只包含满足谓词的元素的新 RDD。过滤后返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入元素组成。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_groupBy").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建一个RDD(由字符串组成),过滤出一个新的RDD [包含xiao]
rdd1 = sc.parallelize(["xiaoming", "xiaohong", "xiaobai", "zhangsan", "lisi", "wangwu"])
rdd2 = rdd1.filter(lambda v: "xiao" in v)
print(rdd2.collect())
关闭环境
sc.stop()

3.7 sortBy() 算子

sortBy() 对这个 RDD 进行排序,假设它由 (key, value) 对组成。排序默认正序。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_groupBy").setMaster("local[*]")
sc = SparkContext(conf=conf)
按照自身大小排序
rdd1 = sc.parallelize([1, 13, 4, 5, 2])
rdd2 = rdd1.sortBy(lambda x: x)
print(rdd2.collect())  # [1, 2, 4, 5, 13]
关闭环境
sc.stop()

3.8 union() 算子

union() 返回此 RDD 与另一个 RDD 的并集,对原 RDD 和参数 RDD 求并集并返回一个新的集合。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_groupBy").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建两个RDD求并集
rdd1 = sc.parallelize([1, 4, 6, 31, 21], 2)
rdd2 = sc.parallelize([2, 3, 4, 6, 31, 21], 3)
计算并集
rdd3 = rdd1.union(rdd2)
print(rdd3.getNumPartitions())  # 5
print(rdd3.collect())
关闭环境
sc.stop()

4. Key-Value 类型算子

RDD 中的元素必须是二元组,才能调用如下算子。

4.1 groupByKey() 算子

groupByKey() 对每个 key 进行操作,但只生成一个序列。需求:创建 RDD,将相同 key 对应值聚合到一个 Seq 中,并计算相同 key 对应的值的相加结果。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_groupBy").setMaster("local[*]")
sc = SparkContext(conf=conf)
rdd1 = sc.parallelize(["one", "two", "two", "three", "three", "three"])
[('one', 1), ('two', 1), ('two', 1), ('three', 1), ('three', 1), ('three', 1)]
rdd2 = rdd1.map(lambda x: (x, 1)).groupByKey().map(lambda x: (x[0], sum(x[1])))
print(rdd2.collect())  # [('two', 2), ('three', 3), ('one', 1)]
关闭环境
sc.stop()

4.2 reduceByKey() 算子

reduceByKey() 在一个 (K, V) 的 RDD 上调用,返回一个 (K, V) 的 RDD,使用指定的 reduce 函数,将相同 Key 的值聚合到一起,reduce 任务的个数可以通过第二个参数来设置。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo05_TR_groupBy").setMaster("local[*]")
sc = SparkContext(conf=conf)
rdd1 = sc.parallelize([("female", 1), ("male", 5), ("female", 5), ("male", 2)])
rdd2 = rdd1.reduceByKey(lambda x1, x2: x1 + x2)
print(rdd2.collect())
关闭环境
sc.stop()

reduceByKey VS groupByKey:reduceByKey 按照 key 进行聚合,在 shuffle 之前有预聚合(combine),返回的结果是 RDDk, v;groupByKey 按照 key 进行分组,直接进行 shuffle。

4.3 aggregateByKey() 算子

aggregateByKey() 用于在分区内和分区之间使用不同的聚合逻辑。reduceByKey() 的执行过程是:先在上游 RDD 的每个分区内进行分组聚合(预聚合),再在 shuffle 之后的 reduce task 中进行分组聚合。如果希望上游预聚合的逻辑和下游最终聚合的逻辑不一样,就可以使用 aggregateByKey。

aggregateByKey 的参数说明:zeroValue 给每一个分区中的每一个 key 一个初始值;seqFunc 函数用于在每一个分区中用初始值逐步迭代 value;combFunc 函数用于合并每个分区的结果。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo12_TR_aggregateByKey").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建一个RDD,取出每个分区相同key对应值的最大值,然后相加
rdd1 = sc.parallelize([("a", 3), ("a", 2), ("c", 4), ("b", 3), ("c", 6), ("c", 8)], 2)
[[('a', 3), ('a', 2), ('c', 4)], [('b', 3), ('c', 6), ('c', 8)]]
rdd2 = rdd1.aggregateByKey(0, lambda x1, x2: x1 if x1 > x2 else x2, lambda x1, x2: x1 + x2)
print(rdd2.glom().collect())
[[('c', 12), ('b', 3)], [('a', 3)]]
关闭环境
sc.stop()

4.4 sortByKey() 算子

sortByKey() 对这个 RDD 进行排序,假设它由 (key, value) 对组成。可以通过 ascending 参数控制正序或倒序排序。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo12_TR_aggregateByKey").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建RDD,按照key的正序和倒序进行排序
rdd1 = sc.parallelize([(3, "aa"), (6, "cc"), (2, "bb"), (1, "dd")], 2)
rdd2 = rdd1.sortByKey(ascending=True)
rdd3 = rdd1.sortByKey(ascending=False)
print(rdd2.collect())
print(rdd3.collect())
关闭环境
sc.stop()

4.5 join() 算子

join() 在类型为 (K, V) 和 (K, W) 的 RDD 上调用,返回一个相同 key 对应的所有元素在一起的 (K, (V, W)) 的 RDD。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo12_TR_aggregateByKey").setMaster("local[*]")
sc = SparkContext(conf=conf)
需求:创建两个RDD,并将key相同的数据聚合到一个元组
rdd1 = sc.parallelize([(1, "a"), (2, "b"), (3, "c")])
rdd2 = sc.parallelize([(1, 4), (2, 5), (3, 6)])
rdd3 = rdd1.join(rdd2)
print(rdd3.collect())
[(1, ("a", 4)), (2, ("b", 5)), (3, ("c", 6))]
关闭环境
sc.stop()

4.6 mapValues() 算子

mapValues() 将键值对 RDD 中的每个值通过 map 函数处理,而不改变键,这也保留了原 RDD 的分区。

python 复制代码
from pyspark import SparkContext, SparkConf
1、创建环境
conf = SparkConf().setAppName("Demo15_TR_mapValues").setMaster("local[*]")
sc = SparkContext(conf=conf)
rdd_kv = sc.parallelize([("zs", 11), ("lisi", 22), ("ww", 33), ("zl", 44)], 2)
rdd2 = rdd_kv.mapValues(lambda age: age + 1)
rdd2.foreach(lambda x: print(x))
关闭环境
sc.stop()

5. 总结

本文通过多个完整示例,详细演示了 RDD 的三种创建方式以及 map、mapPartitions、mapPartitionsWithIndex、flatMap、groupBy、filter、sortBy、union 等 Value 类型转换算子,以及 groupByKey、reduceByKey、aggregateByKey、sortByKey、join、mapValues 等 Key-Value 类型算子的使用方法。掌握这些基础操作是深入学习 Spark 数据处理的关键一步。

相关推荐
ha_lydms1 小时前
Vector Functions(向量函数)
大数据·hadoop·spark·数据处理·dataworks·maxcompute·数据中台
ha_lydms1 小时前
阿里云大数据组件
大数据·hadoop·阿里云·spark·云计算·数据处理·dataworks
张张123y5 小时前
后端第十四课:Agent 记忆系统(让 Agent 记住上下文和用户,跨会话不健忘)
java·大数据·人工智能·python·agent·记忆
找方案10 小时前
从用户画像到智能定价:81页方案拆解“大数据决策与支撑平台”怎么落地
大数据·人工智能·数据分析·智慧城市·用户画像
liliangcsdn11 小时前
DeepSeek DeepInfer开源项目的功能分析和探索
大数据·人工智能
xiaogai_gai12 小时前
钉钉月结报销到金蝶付款单同步方案实战教程
大数据·数据库·钉钉
Elastic 中国社区官方博客13 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
商业看点解说14 小时前
支持私有化部署的 AI 助手有哪些?把“私有化“拆成四层,排除一类常被错放的产品
大数据·人工智能