PySpark案例实战
一、前言介绍
(一)Spark和PySpark概述
- Spark
- Spark是Apache基金会旗下用于大规模数据处理的统一分析引擎,是一款分布式计算框架,可调度大量服务器集群处理海量数据。
- PySpark
- PySpark是Spark为Python开发者提供的编程入口,是Spark的Python实现。它不仅可作为Python第三方库使用,还能将程序提交到Spark集群环境中执行。
(二)为什么学习PySpark
- Python就业方向
- Python应用场景丰富,其中大数据开发和人工智能是亮点方向。
- 大数据开发需求
- 大数据开发是Python的高薪就业方向,Spark(PySpark)技术是大数据开发的核心技术栈,很多大数据相关职位要求熟悉Spark相关技术。
(三)与大数据方向的衔接
- 课程学习方式
- 课程以Python第三方库的视角学习使用PySpark,不涉及专业大数据知识,有Python基础即可学习。
- 深入学习途径
- 若要深入大数据开发领域,可进一步学习相关大数据知识,如Hadoop等。
二、基础准备
(一)PySpark库的安装
- 使用pip安装
- 在命令提示符中输入
pip install pyspark
。 - 也可使用国内代理镜像网站(如清华大学源):
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
。
- 在命令提示符中输入
(二)构建PySpark执行环境入口对象
- 创建SparkConf对象
- 首先从
pyspark
导入SparkConf
和SparkContext
,然后创建SparkConf
类对象并设置相关属性。 - 示例:
- 首先从
from pyspark import SparkConf, SparkContext
conf = SparkConf().setmaster("local[*]").setAppName("test_spark_app")
- 创建SparkContext对象
- 基于
SparkConf
类对象创建SparkContext
类对象,它是PySpark编程中一切功能的入口。 - 示例:
- 基于
sc = SparkContext(conf=conf)
print(sc.version)
sc.stop()
三、数据输入
(一)RDD对象
- 定义和作用
- RDD全称为弹性分布式数据集(Resilient Distributed Datasets),是PySpark中数据计算的载体。它可存储数据,提供各类数据计算方法,且计算方法的返回值依旧是RDD对象。
(二)数据输入方法
- Python数据容器转RDD对象
- 通过
SparkContext
对象的parallelize
成员方法,可将list
、tuple
、set
、dict
、str
等Python数据容器转换为RDD对象。注意字符串会被拆分字符,字典仅有key
会被存入RDD对象。 - 示例:
- 通过
from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5])
print(rdd.collect())
- 读取文件转RDD对象
- 使用
SparkContext
入口对象的textFile
成员方法读取文件构建RDD对象。 - 示例:
- 使用
from pyspark import SparkConf, SparkContext
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
sc = SparkContext(conf=conf)
rdd = sc.textFile("文件路径")
print(rdd.collect())
四、数据计算
(一)map方法
- 功能和使用
map
算子接受一个处理函数(可用lambda
表达式快速编写),对RDD内的元素逐个处理,并返回一个新的RDD。- 示例:
from pyspark import SparkConf, SparkContext
if __name__ == '__main__':
conf = SparkConf().setAppName("create rdd").setMaster("local[*]")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5])
def map_func(data):
return data * 10
print(rdd.map(map_func).collect())
print(rdd.map(lambda data: data * 10).collect())
(二)flatMap方法
- 功能和使用
flatMap
算子的计算逻辑和map
类似,但可以解除一层嵌套的功能。- 示例:
# 嵌套的list
lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
rdd = sc.parallelize(lst)
print(rdd.flatMap(lambda x: x).collect())
(三)reduceByKey方法
- 功能和使用
- 针对KV型RDD,自动按照
key
分组,然后根据提供的聚合逻辑,完成组内数据(value
)的聚合操作。接受一个处理函数,对数据进行两两计算。 - 示例:
- 针对KV型RDD,自动按照
rdd = sc.parallelize([('a', 1), ('a', 1), ('b', 1), ('b', 1), ('b', 1)])
result = rdd.reduceByKey(lambda a, b: a + b)
print(result.collect())
(四)filter方法
- 功能和使用
filter
算子接受一个处理函数(可用lambda
快速编写),函数对RDD数据逐个处理,得到True
的保留至返回值的RDD中。- 示例:
rdd = sc.parallelize([1, 2, 3, 4, 5])
filtered_rdd = rdd.filter(lambda x: x > 2)
print(filtered_rdd.collect())
(五)distinct方法
- 功能和使用
- 完成对RDD内数据的去重操作。
- 示例:
rdd = sc.parallelize([1, 1, 3, 3, 5, 5, 6, 6, 9, 9])
print(rdd.distinct().collect())
(六)sortBy方法
- 功能和使用
- 接收一个处理函数(可用
lambda
快速编写),函数表示用来决定排序的依据,可以控制升序或降序,全局排序需要设置分区数为1。 - 示例:
- 接收一个处理函数(可用
rdd = sc.parallelize([(1, 3), (2, 1), (3, 2)])
sorted_rdd = rdd.sortBy(lambda x: x[1], ascending=False)
print(sorted_rdd.collect())
五、数据输出
(一)输出为Python对象
- collect方法
- 功能:将RDD各个分区内的数据统一收集到Driver中,形成一个
List
对象。 - 示例:
- 功能:将RDD各个分区内的数据统一收集到Driver中,形成一个
rdd = sc.parallelize([1, 2, 3, 4, 5])
print(rdd.collect())
- reduce方法
- 功能:对RDD数据集按照传入的逻辑进行聚合。
- 示例:
rdd = sc.parallelize(range(1, 10))
print(rdd.reduce(lambda a, b: a + b))
- take方法
- 功能:取出RDD的前
N
个元素组成list
。 - 示例:
- 功能:取出RDD的前
rdd = sc.parallelize([3, 2, 1, 4, 5, 6])
print(rdd.take(3))
- count方法
- 功能:计算RDD有多少条数据,返回值是一个数字。
- 示例:
rdd = sc.parallelize([3, 2, 1, 4, 5, 6])
print(rdd.count())
(二)输出到文件中
- saveAsTextFile方法
- 功能:将RDD的数据写入文本文件中,支持本地写出、
hdfs
等文件系统。 - 示例:
- 功能:将RDD的数据写入文本文件中,支持本地写出、
rdd = sc.parallelize([1, 2, 3, 4, 5])
rdd.saveAsTextFile("../data/output/test.txt")
- 注意事项:调用保存文件的算子需要配置Hadoop依赖,步骤如下:
- 下载Hadoop安装包(如http://archive.apache.org/dist/hadoop/common/hadoop - 3.0.0/hadoop - 3.0.0.tar.gz)并解压到电脑任意位置。
- 在Python代码中使用
os
模块配置:os.environ[‘HADOOP_HOME’] = ‘HADOOP解压文件夹路径’
。 - 下载winutils.exe,并放入Hadoop解压文件夹的bin目录内(如https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop - 3.0.0/bin/winutils.exe)。
- 下载hadoop.dll,并放入
C:/Windows/System32
文件夹内(如https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop - 3.0.0/bin/hadoop.dll)。
六、综合案例
(一)单词计数案例
- 步骤
- 构建Spark环境的执行入口对象
SparkContext
。 - 读取数据文件。
- 使用
flatMap
将所有单词取出。 - 使用
map
将所有单词都加上1作为value
。 - 使用
reduceByKey
分组并求和。 - 打印输出计算结果。
- 构建Spark环境的执行入口对象
- 代码示例
from pyspark import SparkConf, SparkContext
# 1. 构建Spark环境的执行入口对象 SparkContext对象
conf = SparkConf().setAppName("HelloSpark").setMaster("local[*]")
sc = SparkContext(conf = conf)
#2. 读取数据文件
file = sc.textFile("../data/words.txt", 3)
#3. 将所有的单词都取出来
words = file.flatMap(lambda line: line.split(' '))
# 4. 将所有单词都加上1作为value
word_one = words.map(lambda x: (x, 1))
#5. 分组并求和
result = word_one.reduceByKey(lambda a, b: a + b)
#打印输出计算结果
print(result.collect())
(二)商品销售数据分析案例
- 需求
- 各个城市销售额排名,从大到小。
- 全部城市,有哪些商品类别在售卖。
- 北京市有哪些商品类别在售卖。
- 步骤
- 构建Spark执行环境。
- 读取文件。
- 将每条
json
数据取出。 - 将每个
json
字符串变成字典对象。 - 过滤数据,只保留北京。
- 组合北京和商品类型并输出。
- 代码示例
from pyspark import SparkConf, SparkContext
import json
if __name__ == '__main__':
# 0: 构建Spark执行环境
conf = SparkConf().setAppName("create rdd").setMaster("local[*]")
sc = SparkContext(conf=conf)
# 1. 读取文件
rdd = sc.textFile("../data/order.text")
# 2. 将每条json数据取出
json_str_rdd = rdd.flatMap(lambda x: x.split("|"))
# 3.将每个json字符串,变成字典对象
json_dict_rdd = json_str_rdd.map(lambda x: json.loads(x))
#4.过滤数据,只保留北京
beijing_rdd = json_dict_rdd.filter(lambda x: x['areaName'] == "北京")
#5.组合北京 和商品类型
result_rdd = beijing_rdd.map(lambda x: x['areaName'] + "_" + x['category'])
print(result_rdd.collect())
(三)搜索引擎日志分析案例
- 需求
- 打印输出:热门搜索时间段(小时精度)Top3。
- 打印输出:热门搜索词Top3。
- 打印输出:统计黑马程序员关键字在哪个时段被搜索最多。
- 将数据转换为JSON格式,写出为文件。
- 代码示例(未完整给出具体实现细节)
from pyspark import SparkConf, SparkContext
# 构建Spark环境的执行入口对象 SparkContext对象
conf = SparkConf().setAppName("search_log_analysis").setMaster("local[*]")
sc = SparkContext(conf = conf)
# 读取文件转换成RDD
rdd = sc.textFile("search_log.txt")
# 以下步骤需根据具体需求进一步实现,例如:
### 提取热门搜索时间段(小时精度)Top3
1. **思路**
- 从日志数据中提取搜索时间的小时信息,然后使用`map`和`reduceByKey`进行计数,最后通过`sortBy`进行排序并取前3。
2. **代码示例(部分关键步骤)**
```python
from pyspark import SparkConf, SparkContext
# 构建Spark环境的执行入口对象 SparkContext对象
conf = SparkConf().setAppName("search_log_analysis").setMaster("local[*]")
sc = SparkContext(conf = conf)
# 读取文件转换成RDD
rdd = sc.textFile("search_log.txt")
# 提取小时信息
hour_rdd = rdd.map(lambda line: line.split()[0][:2])
# 计数
count_rdd = hour_rdd.map(lambda x: (x, 1)).reduceByKey(lambda a, b: a + b)
# 排序并取前3
top3_hours = count_rdd.sortBy(lambda x: x[1], ascending=False).take(3)
print(top3_hours)
提取热门搜索词Top3
- 思路
- 从日志数据中提取搜索词,然后使用
map
和reduceByKey
进行计数,最后通过sortBy
进行排序并取前3。
- 从日志数据中提取搜索词,然后使用
- 代码示例(部分关键步骤)
from pyspark import SparkConf, SparkContext
# 构建Spark环境的执行入口对象 SparkContext对象
conf = SparkConf().setAppName("search_log_analysis").setMaster("local[*]")
sc = SparkContext(conf = conf)
# 读取文件转换成RDD
rdd = sc.textFile("search_log.txt")
# 提取搜索词
word_rdd = rdd.map(lambda line: line.split()[1])
# 计数
count_rdd = word_rdd.map(lambda x: (x, 1)).reduceByKey(lambda a, b: a + b)
# 排序并取前3
top3_words = count_rdd.sortBy(lambda x: x[1], ascending=False).take(3)
print(top3_words)
统计黑马程序员关键字在哪个时段被搜索最多
- 思路
- 先提取包含“黑马程序员”的日志行,然后提取时间信息进行计数统计。
- 代码示例(部分关键步骤)
from pyspark import SparkConf, SparkContext
# 构建Spark环境的执行入口对象 SparkContext对象
conf = SparkConf().setAppName("search_log_analysis").setMaster("local[*]")
sc = SparkContext(conf = conf)
# 读取文件转换成RDD
rdd = sc.textFile("search_log.txt")
# 提取包含黑马程序员的日志行
itheima_rdd = rdd.filter(lambda line: "黑马程序员" in line)
# 提取时间信息并计数
time_count_rdd = itheima_rdd.map(lambda line: line.split()[0][:2]).map(lambda x: (x, 1)).reduceByKey(lambda a, b: a + b)
# 找到搜索最多的时段
most_searched_time = time_count_rdd.sortBy(lambda x: x[1], ascending=False).first()
print(most_searched_time)
将数据转换为JSON格式并写出为文件
- 思路
- 先将数据处理为合适的格式,然后使用
json
模块将数据转换为JSON字符串,最后使用saveAsTextFile
将数据写出到文件。
- 先将数据处理为合适的格式,然后使用
- 代码示例(部分关键步骤)
import json
from pyspark import SparkConf, SparkContext
# 构建Spark环境的执行入口对象 SparkContext对象
conf = SparkConf().setAppName("search_log_analysis").setMaster("local[*]")
sc = SparkContext(conf = conf)
# 读取文件转换成RDD并进行相关处理(此处省略处理步骤,假设已经处理好数据在result_rdd中)
result_rdd =...
# 将数据转换为JSON格式
json_rdd = result_rdd.map(lambda data: json.dumps(data))
# 写出到文件
json_rdd.saveAsTextFile("output_path")
七、分布式集群运行
- 提交命令示例
- 将案例提交到YARN集群中运行的命令示例:
bin/spark - submit --master yarn --num - executors 3 --queue root.--executor - cores 4 --executor - memory 4g /home/hadoop/demo.py
。这行命令中各个参数的含义如下: --master yarn
:指定使用YARN作为集群管理器。--num - executors 3
:指定要启动的执行器数量为3。--queue root.teach
:指定任务要提交到的队列。--executor - cores 4
:指定每个执行器使用的核心数为4。--executor - memory 4g
:指定每个执行器的内存为4GB。
- 将案例提交到YARN集群中运行的命令示例:
通过这些参数的设置,可以根据集群的资源情况和任务需求来合理地分配资源,确保任务能够高效地在分布式集群环境中运行。同时,在将PySpark代码提交到集群运行之前,需要确保集群环境已经正确配置,包括相关软件的安装和环境变量的设置等。例如,要在Windows环境下运行涉及文件操作的PySpark任务,可能需要配置Hadoop依赖,具体步骤如下:
- 下载Hadoop安装包(如
http://archive.apache.org/dist/hadoop/common/hadoop - 3.0.0/hadoop - 3.0.0.tar.gz
)并解压到电脑任意位置。 - 在Python代码中使用
os
模块配置:os.environ[‘HADOOP_HOME’] = ‘HADOOP解压文件夹路径’
。 - 下载
winutils.exe
,并放入Hadoop解压文件夹的bin
目录内(如https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop - 3.0.0/bin/winutils.exe
)。 - 下载
hadoop.dll
,并放入C:/Windows/System32
文件夹内(如https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop - 3.0.0/bin/hadoop.dll
)。
这样才能确保在分布式集群环境中正确地运行PySpark任务,实现对大规模数据的高效处理。