文本文件:通过PySpark单词计数案例带你学习map、flatMap、reduceByKey方法(Python)资源-CSDN下载
一、题目要求
请使用 PySpark 完成一个单词计数程序,具体要求如下:
- 配置并初始化 Spark 执行环境
- 读取本地文件 "D:/test1.txt"
- 对文件内容进行单词拆分
- 将每个单词转换为 (单词,1) 的二元元组形式
- 对相同单词进行分组并计算出现次数
- 输出最终的单词计数结果
二、实现代码:
#1.构建执行环境入口对象
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/Python/Python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
#2.读取文件
rdd = sc.textFile("D:/test1.txt")
#3.取出全部单词
word_rdd = rdd.flatMap(lambda x: x.split())
#4.将所有单词都转换成二元元组,单词为Key,value设置为1
#(spark,1)
word_with_one_rdd = word_rdd.map(lambda word:(word,1))
#5.分组并求和
result_rdd = word_with_one_rdd.reduceByKey(lambda a,b:a+b)
#6.打印输出
print(result_rdd.collect())
三、实现效果图:
四、三个RDD转换算子讲解
在这段 PySpark 单词计数代码中,map()
、flatMap()
和reduceByKey()
是三个核心的 RDD 转换算子。
1、map () 方法:一对一映射转换
1.核心原理
map()
是最基础的转换算子,它对 RDD 中的每个元素独立应用一个函数,每个元素都会被转换为一个新元素,最终生成一个新的 RDD。
2.关键特点
- 输入输出数量一致:原始 RDD 有 N 个元素,转换后仍有 N 个元素
- 转换独立性:每个元素的转换不影响其他元素
- 函数灵活性:可以返回任意类型的结果(与输入类型可不同)
word_with_one_rdd = word_rdd.map(lambda word: (word, 1))
假设 word_rdd
中的元素是:["spark", "hadoop", "spark"]
经过 map()
转换后:
- 第一个元素 "spark" → ("spark", 1)
- 第二个元素 "hadoop" → ("hadoop", 1)
- 第三个元素 "spark" → ("spark", 1)
最终word_with_one_rdd
的元素为:[("spark", 1), ("hadoop", 1), ("spark", 1)]
3.更多示例
# 示例1:数字翻倍
nums = sc.parallelize([1, 2, 3, 4])
doubled = nums.map(lambda x: x * 2) # 结果: [2, 4, 6, 8]
# 示例2:字符串长度计算
words = sc.parallelize(["apple", "banana", "cat"])
lengths = words.map(lambda s: len(s)) # 结果: [5, 6, 3]
2、flatMap () 方法:先映射后扁平化
1.核心原理
flatMap()
可以理解为 map () + 扁平化 的组合操作:
- 先对每个元素应用映射函数(同 map ()),生成一个集合 / 列表
- 再将所有集合 / 列表 "摊平",把其中的元素逐个提取出来作为新 RDD 的元素
2.关键特点
- 输入输出数量可变化:1 个元素可生成 0 到多个元素
- 扁平化处理:会自动解除嵌套结构(将列表中的元素 "拉平")
- 适合拆分操作:尤其适合文本拆分、数据展开等场景
word_rdd = rdd.flatMap(lambda x: x.split())
假设 rdd
中的元素是两行文本:["hello spark", "hello hadoop"]
处理过程:
- 第一步(类似 map):
- 第一行 "hello spark" → 拆分为 ["hello", "spark"]
- 第二行 "hello hadoop" → 拆分为 ["hello", "hadoop"]
- 第二步(扁平化):将两个列表合并为一个列表
最终word_rdd
的元素为:["hello", "spark", "hello", "hadoop"]
3.与 map () 的对比
如果用 map()
处理上述文本:
# 用map()的结果(未扁平化)
map_result = rdd.map(lambda x: x.split())
# 结果: [["hello", "spark"], ["hello", "hadoop"]] (仍是嵌套列表)
这正是 flatMap()
与 map()
的核心区别:flatMap()
会移除一层嵌套结构。
4.更多示例
# 示例1:将句子拆分为单词(最典型用法)
sentences = sc.parallelize(["I love python", "python is great"])
words = sentences.flatMap(lambda s: s.split()) # 结果: ["I", "love", "python", "python", "is", "great"]
# 示例2:展开嵌套列表
data = sc.parallelize([[1, 2], [3, 4, 5], [6]])
flattened = data.flatMap(lambda x: x) # 结果: [1, 2, 3, 4, 5, 6]
3、reduceByKey () 方法:按 Key 聚合计算
1.核心原理
reduceByKey()
是 Pair RDD(键值对 RDD) 的专用算子,它的工作流程是:
- 按照 Key 对 RDD 中的元素进行分组(相同 Key 的元素分为一组)
- 对每个组内的 Value 应用聚合函数(通常是求和、求积、求最大值等)
- 每个组最终生成一个 (Key, 聚合结果) 的元素
2.关键特点
- 仅适用于 Pair RDD:输入必须是 (Key, Value) 形式的二元元组
- 分组聚合:先分组后聚合,聚合逻辑由用户定义
- 优化特性:会在 Map 阶段进行局部聚合(减少网络传输数据量)
在单词计数中:
result_rdd = word_with_one_rdd.reduceByKey(lambda a, b: a + b)
word_with_one_rdd
中的元素是:[("spark", 1), ("hadoop", 1), ("spark", 1)]
处理过程:
- 分组:按 Key 分组得到两组
- "spark" 组:[(1), (1)]
- "hadoop" 组:[(1)]
- 聚合:对每组应用求和函数
- "spark" 组:1 + 1 = 2
- "hadoop" 组:1(只有一个元素,直接保留)
最终result_rdd
的元素为:[("spark", 2), ("hadoop", 1)]
3.聚合函数详解
reduceByKey()
的参数是一个二元函数(接收两个参数,返回一个结果),这个函数需要满足:
- 交换律:
a + b = b + a
- 结合律:
(a + b) + c = a + (b + c)
常见的聚合逻辑:
# 求和(单词计数用的就是这个)
rdd.reduceByKey(lambda a, b: a + b)
# 求积
rdd.reduceByKey(lambda a, b: a * b)
# 求最大值
rdd.reduceByKey(lambda a, b: a if a > b else b)
4.更多示例
# 示例:统计每个用户的消费总额
transactions = sc.parallelize([
("Alice", 100), ("Bob", 50), ("Alice", 200), ("Bob", 150)
])
total_spent = transactions.reduceByKey(lambda a, b: a + b)
# 结果: [("Alice", 300), ("Bob", 200)]
五、总结
1.三者综合对比与协作关系
算子 | 输入类型 | 输出类型 | 核心作用 | 典型应用场景 |
---|---|---|---|---|
map() | 任意类型 | 任意类型 | 一对一转换 | 数据格式转换、值计算 |
flatMap() | 任意类型 | 任意类型 | 拆分并扁平化 | 文本分词、列表展开 |
reduceByKey() | 键值对 (K,V) | 键值对 (K,V') | 按 Key 聚合 | 分组统计、求和 / 最大值计算 |
这三个算子共同完成了从原始文本到单词计数结果的转换,体现了 Spark 的核心思想:将复杂计算分解为一系列简单的转换操作。