在大数据处理、海量数据分析、分布式计算场景中,PySpark 是最受欢迎的工具之一。它将 Python 的简洁易用与 Apache Spark 的分布式计算能力相结合,无需深入掌握 Scala 即可快速上手大数据处理,广泛应用于数据清洗、数据分析、机器学习、日志挖掘等领域。
一、前置准备:环境搭建与安装验证
PySpark 依赖 Java 环境和 Apache Spark 核心包,搭建过程需遵循「先依赖后核心」的顺序,支持 Windows、Linux、Mac 三大平台,以下步骤详细且可落地,新手可直接跟随操作。
1. 核心依赖与版本匹配(避坑关键)
PySpark 对 Java 版本有严格要求,推荐使用 Java 8(OpenJDK 8 或 Oracle JDK 8),高版本 Java(如 Java 17)可能与低版本 Spark 不兼容,具体版本对应关系如下:
- Spark 3.0+ 支持 Java 8/11
- Spark 2.x 仅支持 Java 8
本文选用Spark 3.5.0 + Java 8 + Python 3.8+,该组合稳定且兼容性强,适合入门与生产环境。
2. 分步安装流程(三大平台全覆盖)
(1)第一步:安装 Java 8 并配置环境变量
-
Windows 系统
- 下载 OpenJDK 8(无需注册,推荐):前往 Adoptium 官网,下载对应 Windows 64 位的
.msi安装包。 - 运行安装包,默认下一步安装,记录安装路径(如
C:\Program Files\Eclipse Adoptium\jdk8u402-b06)。 - 配置环境变量:
- 新建系统变量
JAVA_HOME,值为上述安装路径。 - 编辑系统变量
Path,添加%JAVA_HOME%\bin。
- 新建系统变量
- 验证:打开命令提示符,输入
java -version,若返回 Java 8 版本信息,说明安装成功。
- 下载 OpenJDK 8(无需注册,推荐):前往 Adoptium 官网,下载对应 Windows 64 位的
-
Linux/Mac 系统
- Linux(Ubuntu/Debian):通过 apt 直接安装
sudo apt update sudo apt install openjdk-8-jdk - Mac 系统:通过 Homebrew 直接安装
brew install adoptopenjdk8 - 配置环境变量(Linux/Mac 通用):
- 编辑
~/.bashrc或~/.zshrc(根据终端类型选择)vi ~/.bashrc - 添加以下内容(需替换为实际的 Java 安装路径,可通过
update-alternatives --config java查找)export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export PATH=$JAVA_HOME/bin:$PATH - 生效配置:
source ~/.bashrc
- 编辑
- 验证:输入
java -version,返回 Java 8 版本信息即成功。
- Linux(Ubuntu/Debian):通过 apt 直接安装
(2)第二步:下载并配置 Apache Spark
- 下载 Spark 核心包:前往 Apache Spark 官网,选择「Spark 3.5.0」,「Package Type」选择「Pre-built for Apache Hadoop 3.3 and later」,点击下载对应的
.tgz压缩包(Windows 也下载该包,无需单独下载 Windows 版本)。 - 解压压缩包:
- Windows:将压缩包解压到自定义路径(如
D:\Software\spark-3.5.0-bin-hadoop3.3,路径建议无中文、无空格)。 - Linux/Mac:将压缩包解压到
/usr/local/或~/Software/目录,示例命令:tar -zxvf spark-3.5.0-bin-hadoop3.3.tgz -C /usr/local/
- Windows:将压缩包解压到自定义路径(如
- 配置 Spark 环境变量:
- Windows:
- 新建系统变量
SPARK_HOME,值为 Spark 解压路径(如D:\Software\spark-3.5.0-bin-hadoop3.3)。 - 编辑系统变量
Path,添加%SPARK_HOME%\bin和%SPARK_HOME%\python\pyspark。
- 新建系统变量
- Linux/Mac:
- 编辑
~/.bashrc或~/.zshrc,添加以下内容(替换为实际解压路径):
export SPARK_HOME=/usr/local/spark-3.5.0-bin-hadoop3.3 export PATH=$SPARK_HOME/bin:$SPARK_HOME/python/pyspark:$PATH- 生效配置:
source ~/.bashrc
- 编辑
- Windows:
(3)第三步:安装 PySpark 包
打开命令提示符/终端,输入以下 pip 命令直接安装,会自动匹配对应版本的依赖包:
pip install pyspark
3. 验证安装成功(两种方式)
(1)方式一:运行 pyspark shell
打开命令提示符/终端,直接输入 pyspark,若出现 Spark 启动界面(包含 Spark 版本、Java 版本信息,以及 >>> 提示符),说明安装成功。
- 退出 shell:输入
exit()或按Ctrl+D。
(2)方式二:运行 Python 脚本验证
创建一个名为 pyspark_hello.py 的脚本,写入以下代码:
# 验证 PySpark 安装成功
from pyspark.sql import SparkSession
# 创建 Spark 会话(核心入口)
spark = SparkSession.builder \
.appName("PySpark_Hello_World") \
.master("local[*]") # 本地模式,使用所有可用核心
.getOrCreate()
# 打印 Spark 版本信息
print(f"PySpark 版本:{spark.version}")
# 停止 Spark 会话
spark.stop()
运行脚本:python pyspark_hello.py,若成功打印 PySpark 版本信息,无报错,说明环境搭建完成。
4. 核心概念:PySpark 运行模式说明
- 本地模式(local[*]):本文所有实操均使用该模式,无需搭建集群,适合开发、测试、小规模数据处理,
local[*]表示使用本地所有可用 CPU 核心。 - 集群模式(Standalone/YARN/Mesos):适合生产环境的海量数据处理,需额外搭建 Spark 集群或集成 Hadoop YARN,后续进阶可深入学习。
二、核心基础:PySpark 核心架构与数据结构
在进行实操前,先明确 PySpark 的核心架构与两大核心数据结构,避免后续操作只知其然不知其所以然。
1. PySpark 核心架构(分布式计算的核心)
PySpark 基于 Apache Spark 架构,核心分为「Driver 端」和「Executor 端」:
- Driver 端:运行 Python 脚本的主节点,负责创建 Spark 会话、提交任务、协调资源,本文中即本地运行脚本的电脑。
- Executor 端:负责执行具体的分布式计算任务,每个 Executor 对应一个计算节点,本地模式下 Executor 运行在本地进程中。
- 核心入口:
SparkSession(新版 PySpark 推荐),替代了传统的SparkContext和SQLContext,统一了结构化数据、非结构化数据的处理入口。
2. 两大核心数据结构:RDD vs DataFrame
PySpark 提供两种核心数据结构,分别适用于不同场景,两者的核心差异如下:
| 数据结构 | 核心特点 | 适用场景 | 效率 | 操作方式 |
|---|---|---|---|---|
| RDD(弹性分布式数据集) | 底层分布式数据结构,无结构化约束,支持复杂数据类型 | 非结构化数据处理、复杂算法实现、底层分布式操作 | 较低(无优化) | 转换操作(map/filter)+ 行动操作(count/collect) |
| DataFrame(结构化数据框) | 高层结构化数据结构,类似 Pandas DataFrame,带列名和数据类型 | 结构化数据处理、数据清洗、数据分析、Spark SQL | 较高(支持 Catalyst 优化器) | 面向列的操作、SQL 查询 |
实际工作中优先使用 DataFrame,效率更高、操作更简洁,RDD 仅在处理复杂非结构化数据(如文本、图片)时使用。
三、核心实操1:RDD 基础操作(创建、转换、行动)
RDD 是 PySpark 的底层数据结构,理解 RDD 的操作逻辑是掌握 PySpark 分布式计算的基础。RDD 操作分为两类:转换操作(懒执行,不立即返回结果)和行动操作(立即执行,返回结果到 Driver 端)。
1. RDD 核心操作函数与语法
(1)创建 RDD(两种常用方式)
- 并行化本地集合:
spark.sparkContext.parallelize(collection, numSlices=None)collection:本地列表、元组等可迭代对象。numSlices:分区数,默认根据本地核心数自动分配。
- 读取外部文件:
spark.sparkContext.textFile(file_path)file_path:本地文件路径或 HDFS 路径,支持.txt等文本文件。
(2)转换操作(常用)
| 函数 | 功能说明 | 示例 |
|---|---|---|
map(func) | 对 RDD 中每个元素应用 func 函数,返回新 RDD | rdd.map(lambda x: x*2) |
filter(func) | 筛选出 func 返回 True 的元素,返回新 RDD | rdd.filter(lambda x: x > 10) |
flatMap(func) | 先 map 再扁平化,将嵌套迭代对象展开 | rdd.flatMap(lambda x: x.split(" ")) |
reduceByKey(func) | 对 Key-Value 类型 RDD 按 Key 分组,对 Value 应用 func 聚合 | rdd.reduceByKey(lambda a, b: a + b) |
(3)行动操作(常用)
| 函数 | 功能说明 | 示例 |
|---|---|---|
count() | 返回 RDD 中元素的个数 | rdd.count() |
collect() | 返回 RDD 中所有元素(注意:数据量大时禁止使用,会撑爆 Driver 内存) | rdd.collect() |
first() | 返回 RDD 中第一个元素 | rdd.first() |
saveAsTextFile(file_path) | 将 RDD 结果保存为文本文件 | rdd.saveAsTextFile("./rdd_result.txt") |
reduce(func) | 对 RDD 中所有元素应用 func 进行聚合 | rdd.reduce(lambda a, b: a + b) |
2. 完整实操代码:RDD 综合操作演示
# PySpark RDD 核心操作演示
from pyspark.sql import SparkSession
# 1. 创建 Spark 会话(Driver 端核心)
spark = SparkSession.builder \
.appName("PySpark_RDD_Demo") \
.master("local[2]") # 本地模式,使用 2 个核心
.getOrCreate()
# 2. 获取 SparkContext(创建 RDD 所需)
sc = spark.sparkContext
# 3. 方式一:从本地列表创建 RDD
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
rdd_nums = sc.parallelize(nums, numSlices=2) # 分为 2 个分区
print("RDD 元素个数:", rdd_nums.count())
print("RDD 第一个元素:", rdd_nums.first())
print("RDD 所有元素:", rdd_nums.collect())
# 4. RDD 转换操作(map + filter)
# 步骤1:所有元素乘以 2
rdd_map = rdd_nums.map(lambda x: x * 2)
# 步骤2:筛选出大于 10 的元素
rdd_filter = rdd_map.filter(lambda x: x > 10)
print("转换后 RDD 所有元素:", rdd_filter.collect())
# 5. RDD 聚合操作(reduce + reduceByKey)
# (1)reduce 聚合:所有元素求和
sum_result = rdd_nums.reduce(lambda a, b: a + b)
print("RDD 元素求和结果:", sum_result)
# (2)reduceByKey 聚合:先创建 Key-Value 类型 RDD,再按 Key 求和
kv_data = [("a", 1), ("b", 2), ("a", 3), ("b", 4), ("c", 5)]
rdd_kv = sc.parallelize(kv_data)
rdd_kv_agg = rdd_kv.reduceByKey(lambda a, b: a + b)
print("Key-Value RDD 聚合结果:", rdd_kv_agg.collect())
# 6. 方式二:读取外部文本文件创建 RDD(创建一个 test.txt 文件放在脚本同一目录下)
try:
rdd_text = sc.textFile("./test.txt")
print("文本文件 RDD 前 5 行:", rdd_text.take(5)) # take(5) 返回前 5 个元素,避免数据量过大
except Exception as e:
print("读取文本文件失败:", e)
print("请确保脚本同一目录下存在 test.txt 文件")
# 7. 保存 RDD 结果到本地文件
rdd_filter.saveAsTextFile("./rdd_filter_result")
print("RDD 筛选结果已保存到 ./rdd_filter_result 目录")
# 8. 停止 Spark 会话,释放资源
spark.stop()
3. 运行结果说明
- 运行脚本后,会先创建 Spark 会话,从本地列表创建 RDD,打印 RDD 的元素个数、第一个元素和所有元素。
- 经过
map和filter转换操作后,返回所有乘以 2 且大于 10 的元素。 - 通过
reduce实现元素求和,通过reduceByKey实现按 Key 分组聚合。 - 尝试读取本地
test.txt文件(需自行创建),返回前 5 行内容。 - 最后将筛选后的 RDD 结果保存到
./rdd_filter_result目录(会生成多个分区文件和元数据文件,这是分布式存储的特性)。
4. 避坑提醒
collect()函数会将 RDD 所有数据拉取到 Driver 端,若 RDD 数据量过大(如千万级以上),会导致 Driver 内存溢出,生产环境中尽量避免使用,可使用take(n)或sample()查看部分数据。- 保存 RDD 结果时,指定的输出目录不能提前存在,否则会报错,需先删除原有目录。
- RDD 转换操作是「懒执行」,只有调用行动操作时,才会真正执行所有转换操作,这是 Spark 优化分布式计算的核心特性。
四、核心实操2:DataFrame 与 Spark SQL(实际工作首选)
DataFrame 是 PySpark 中处理结构化数据的核心,类似 Pandas DataFrame,支持列名、数据类型约束,且自带 Catalyst 优化器,效率远高于 RDD。Spark SQL 则是基于 DataFrame 的结构化查询语言,支持标准 SQL 语法,适合数据分析人员快速上手。
1. 核心操作:创建 DataFrame(三种常用方式)
(1)方式一:从本地列表/字典创建
# 从列表创建 DataFrame
from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StructField, StringType, IntegerType
spark = SparkSession.builder.appName("Create_DataFrame_Demo").master("local[*]").getOrCreate()
# 定义数据列表
data = [("Alice", 25, "Female", "New York"),
("Bob", 30, "Male", "London"),
("Charlie", 35, "Male", "Paris"),
("David", 40, "Male", "Tokyo"),
("Eve", 45, "Female", "Sydney")]
# 方式1:直接指定列名
df1 = spark.createDataFrame(data, schema=["Name", "Age", "Gender", "City"])
print("DataFrame 1 结构:")
df1.printSchema() # 打印数据结构(列名、数据类型)
print("DataFrame 1 前 3 行:")
df1.show(3) # 显示前 3 行数据,默认显示 20 行
# 方式2:自定义结构化 schema(更严谨,推荐生产环境使用)
schema = StructType([
StructField("Name", StringType(), nullable=False), # nullable=False 表示该列不允许为空
StructField("Age", IntegerType(), nullable=True),
StructField("Gender", StringType(), nullable=True),
StructField("City", StringType(), nullable=True)
])
df2 = spark.createDataFrame(data, schema=schema)
print("DataFrame 2 结构:")
df2.printSchema()
print("DataFrame 2 所有数据:")
df2.show()
spark.stop()
(2)方式二:读取外部文件(CSV/JSON/Parquet)
# 读取 CSV 文件创建 DataFrame(最常用)
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("Read_File_DataFrame").master("local[*]").getOrCreate()
# 读取 CSV 文件(带表头)
df_csv = spark.read \
.option("header", "true") # 第一行作为列名
.option("inferSchema", "true") # 自动推断数据类型(开发环境可用,生产环境推荐自定义 schema)
.csv("./users.csv") # 自定义 CSV 文件路径
print("CSV 文件 DataFrame 结构:")
df_csv.printSchema()
print("CSV 文件 DataFrame 前 5 行:")
df_csv.show(5)
# 读取 JSON 文件
try:
df_json = spark.read.json("./users.json")
print("JSON 文件 DataFrame 前 5 行:")
df_json.show(5)
except Exception as e:
print("读取 JSON 文件失败:", e)
spark.stop()
(3)方式三:从 Pandas DataFrame 转换
# 从 Pandas DataFrame 转换为 PySpark DataFrame
from pyspark.sql import SparkSession
import pandas as pd
spark = SparkSession.builder.appName("Pandas_To_PySpark").master("local[*]").getOrCreate()
# 创建 Pandas DataFrame
pd_df = pd.DataFrame({
"Name": ["Alice", "Bob", "Charlie"],
"Age": [25, 30, 35],
"City": ["New York", "London", "Paris"]
})
# 转换为 PySpark DataFrame
ps_df = spark.createDataFrame(pd_df)
print("从 Pandas 转换的 DataFrame 结构:")
ps_df.printSchema()
print("从 Pandas 转换的 DataFrame 数据:")
ps_df.show()
spark.stop()
2. DataFrame 基础操作(筛选、排序、分组聚合)
# PySpark DataFrame 基础操作演示
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, avg, count
spark = SparkSession.builder.appName("DataFrame_Basic_Operations").master("local[*]").getOrCreate()
# 1. 创建 DataFrame
data = [("Alice", 25, "Female", "New York"),
("Bob", 30, "Male", "London"),
("Charlie", 35, "Male", "Paris"),
("David", 40, "Male", "Tokyo"),
("Eve", 45, "Female", "Sydney"),
("Fiona", 28, "Female", "New York")]
df = spark.createDataFrame(data, schema=["Name", "Age", "Gender", "City"])
# 2. 数据筛选:筛选出年龄大于 30 且性别为 Female 的数据
df_filtered = df.filter((col("Age") > 30) & (col("Gender") == "Female"))
print("筛选后数据:")
df_filtered.show()
# 3. 数据排序:按年龄降序排序,再按姓名升序排序
df_sorted = df.sort(col("Age").desc(), col("Name").asc())
print("排序后数据:")
df_sorted.show()
# 4. 分组聚合:按城市分组,统计每个城市的人数和平均年龄
df_grouped = df.groupBy("City") \
.agg(
count("Name").alias("Person_Count"), # 统计人数,并重命名列
avg("Age").alias("Average_Age") # 计算平均年龄,并重命名列
)
print("分组聚合后数据:")
df_grouped.show()
# 5. 数据选择:只选择 Name 和 Age 两列
df_selected = df.select("Name", "Age")
print("选择指定列数据:")
df_selected.show(3)
# 6. 缺失值处理(填充/删除)
# 先添加一条含缺失值的数据
from pyspark.sql import Row
df_with_null = df.union(spark.createDataFrame([Row(Name="Grace", Age=None, Gender="Female", City="London")]))
print("含缺失值的数据:")
df_with_null.show()
# 填充缺失值(将 Age 列的 null 填充为 0)
df_fill_null = df_with_null.fillna({"Age": 0})
print("填充缺失值后的数据:")
df_fill_null.show()
# 删除含缺失值的行
df_drop_null = df_with_null.dropna(subset=["Age"])
print("删除缺失值后的数据:")
df_drop_null.show()
spark.stop()
3. Spark SQL 操作(标准 SQL 语法)
# PySpark Spark SQL 操作演示
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("Spark_SQL_Demo").master("local[*]").getOrCreate()
# 1. 创建 DataFrame
data = [("Alice", 25, "Female", "New York"),
("Bob", 30, "Male", "London"),
("Charlie", 35, "Male", "Paris")]
df = spark.createDataFrame(data, schema=["Name", "Age", "Gender", "City"])
# 2. 创建临时视图(支持 SQL 查询,视图仅在当前 Spark 会话中有效)
df.createOrReplaceTempView("user_info") # createOrReplaceTempView:若视图已存在则替换
# 3. 执行标准 SQL 查询
# 示例1:查询所有数据
sql_result1 = spark.sql("SELECT * FROM user_info")
print("SQL 查询所有数据:")
sql_result1.show()
# 示例2:查询年龄大于 28 的男性用户
sql_result2 = spark.sql("SELECT Name, Age, City FROM user_info WHERE Age > 28 AND Gender = 'Male'")
print("SQL 查询年龄大于 28 的男性用户:")
sql_result2.show()
# 示例3:按城市分组,统计人数和平均年龄
sql_result3 = spark.sql("""
SELECT City, COUNT(Name) AS Person_Count, AVG(Age) AS Average_Age
FROM user_info
GROUP BY City
""")
print("SQL 分组聚合结果:")
sql_result3.show()
# 4. 创建全局临时视图(跨 Spark 会话有效,需通过 global_temp. 前缀访问)
df.createGlobalTempView("global_user_info")
# 5. 访问全局临时视图
sql_result4 = spark.sql("SELECT * FROM global_temp.global_user_info")
print("访问全局临时视图数据:")
sql_result4.show()
spark.stop()
4. 运行结果说明
- DataFrame 操作中,通过
filter实现数据筛选,sort实现排序,groupBy结合agg实现分组聚合,支持缺失值的填充与删除。 - Spark SQL 操作中,先将 DataFrame 注册为临时视图,再通过
spark.sql()执行标准 SQL 语句,结果返回为 DataFrame,支持后续进一步处理。 - 临时视图(
createOrReplaceTempView)仅在当前 Spark 会话中有效,全局临时视图(createGlobalTempView)跨会话有效,需通过global_temp.前缀访问。
5. 核心优势总结
- DataFrame 支持面向列的操作,语法更简洁,比 RDD 更易上手,适合大多数结构化数据处理场景。
- Spark SQL 支持标准 SQL 语法,降低了数据分析人员的学习成本,可直接复用传统数据库的查询经验。
- DataFrame 自带 Catalyst 优化器,会自动优化查询计划,提升分布式计算效率,无需手动优化。
五、综合实战:用户行为数据大数据分析
结合前面的知识点,实现一个完整的 PySpark 大数据分析案例:用户电商行为数据清洗与分析,流程包括「数据读取→数据清洗→数据分析→结果保存→结果可视化」。
完整实战代码
# PySpark 综合实战:用户电商行为数据清洗与分析
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, count, sum, avg, datediff, current_date
# 1. 创建 Spark 会话
spark = SparkSession.builder \
.appName("Ecommerce_User_Behavior_Analysis") \
.master("local[*]") \
.getOrCreate()
# 2. 步骤1:模拟生成用户行为数据(实际场景中读取外部 CSV/Parquet 文件)
user_behavior_data = [
("U001", "P001", "2025-12-01", "click", 1, 99.9),
("U001", "P001", "2025-12-02", "purchase", 1, 99.9),
("U002", "P002", "2025-12-01", "click", 2, 199.9),
("U002", "P002", "2025-12-03", "purchase", 1, 199.9),
("U003", "P001", "2025-12-02", "click", 1, 99.9),
("U003", "P003", "2025-12-04", "click", 3, 299.9),
("U004", None, "2025-12-01", "click", 1, None), # 含缺失值
("U005", "P002", "2025-12-05", "purchase", 2, 199.9),
("U001", "P002", "2025-12-03", "click", 1, 199.9),
("U002", "P003", "2025-12-06", "click", 2, 299.9)
]
# 创建 DataFrame 并定义 schema
df_behavior = spark.createDataFrame(
user_behavior_data,
schema=["User_ID", "Product_ID", "Behavior_Date", "Behavior_Type", "Click_Count", "Product_Price"]
)
print("原始用户行为数据:")
df_behavior.show()
print("原始数据结构:")
df_behavior.printSchema()
# 3. 步骤2:数据清洗(处理缺失值、数据类型转换、筛选有效数据)
# 3.1 删除 Product_ID 或 Product_Price 为空的数据
df_cleaned = df_behavior.dropna(subset=["Product_ID", "Product_Price"])
# 3.2 转换数据类型(Behavior_Date 转为日期类型,Click_Count 转为整数类型)
df_cleaned = df_cleaned \
.withColumn("Behavior_Date", col("Behavior_Date").cast("date")) \
.withColumn("Click_Count", col("Click_Count").cast("int")) \
.withColumn("Product_Price", col("Product_Price").cast("double"))
# 3.3 筛选有效行为类型(仅保留 click 和 purchase)
df_cleaned = df_cleaned.filter(col("Behavior_Type").isin(["click", "purchase"]))
# 3.4 计算行为距离当前日期的天数
df_cleaned = df_cleaned.withColumn("Days_Since_Behavior", datediff(current_date(), col("Behavior_Date")))
print("数据清洗后的数据:")
df_cleaned.show()
# 4. 步骤3:数据分析(核心业务指标)
# 4.1 指标1:按用户分组,统计每个用户的点击次数和购买次数
df_user_analysis = df_cleaned.groupBy("User_ID") \
.agg(
count(col("Behavior_Type").when(col("Behavior_Type") == "click")).alias("Total_Click_Count"),
count(col("Behavior_Type").when(col("Behavior_Type") == "purchase")).alias("Total_Purchase_Count"),
sum(col("Product_Price").when(col("Behavior_Type") == "purchase")).alias("Total_Purchase_Amount")
) \
.fillna({"Total_Purchase_Amount": 0}) # 未购买的用户,购买金额填充为 0
print("用户点击与购买统计:")
df_user_analysis.show()
# 4.2 指标2:按商品分组,统计每个商品的点击量、购买量和销售额
df_product_analysis = df_cleaned.groupBy("Product_ID") \
.agg(
sum("Click_Count").alias("Total_Click_Volume"),
count(col("Behavior_Type").when(col("Behavior_Type") == "purchase")).alias("Total_Purchase_Volume"),
sum(col("Product_Price").when(col("Behavior_Type") == "purchase")).alias("Total_Sales_Amount")
)
print("商品销售统计:")
df_product_analysis.show()
# 4.3 指标3:计算商品转化率(购买量 / 点击量)
df_conversion = df_product_analysis \
.withColumn("Conversion_Rate", col("Total_Purchase_Volume") / col("Total_Click_Volume")) \
.orderBy(col("Conversion_Rate").desc())
print("商品转化率统计(降序):")
df_conversion.show()
# 5. 步骤4:保存分析结果(保存为 CSV 文件和 Parquet 文件)
# 5.1 保存用户分析结果
df_user_analysis.write \
.option("header", "true") \
.mode("overwrite") # 覆盖已有文件
.csv("./ecommerce_user_analysis.csv")
# 5.2 保存商品转化率结果(Parquet 格式,高效压缩,适合大数据存储)
df_conversion.write \
.mode("overwrite") \
.parquet("./ecommerce_product_conversion.parquet")
print("分析结果已保存:")
print("1. 用户分析结果:./ecommerce_user_analysis.csv")
print("2. 商品转化率结果:./ecommerce_product_conversion.parquet")
# 6. 步骤5:结果可视化(转换为 Pandas DataFrame,使用 Matplotlib 绘图)
try:
import pandas as pd
import matplotlib.pyplot as plt
# 转换 PySpark DataFrame 为 Pandas DataFrame
pd_conversion = df_conversion.toPandas()
# 绘制商品转化率柱状图
plt.rcParams["font.sans-serif"] = ["SimHei"] # 支持中文显示
plt.figure(figsize=(10, 6))
plt.bar(pd_conversion["Product_ID"], pd_conversion["Conversion_Rate"], color="skyblue")
plt.xlabel("商品 ID")
plt.ylabel("转化率")
plt.title("PySpark 商品转化率分析结果")
plt.grid(axis="y", alpha=0.7)
plt.savefig("./ecommerce_conversion_rate.png")
plt.show()
print("转化率可视化图表已保存为 ./ecommerce_conversion_rate.png")
except ImportError as e:
print("缺少可视化依赖包:", e)
print("请安装 pandas 和 matplotlib:pip install pandas matplotlib")
except Exception as e:
print("可视化失败:", e)
# 7. 停止 Spark 会话
spark.stop()
运行效果说明
- 脚本首先模拟生成用户电商行为数据,包含点击、购买等行为,以及部分缺失值。
- 数据清洗阶段,处理缺失值、转换数据类型、筛选有效行为,计算行为距离当前日期的天数。
- 数据分析阶段,统计用户点击/购买指标、商品销售指标、商品转化率,满足电商业务的核心分析需求。
- 分析结果保存为 CSV 文件(易读取)和 Parquet 文件(高效压缩,适合大数据场景)。
- 最后将转换结果转为 Pandas DataFrame,使用 Matplotlib 绘制商品转化率柱状图,实现可视化验证。
六、常见问题与避坑指南(新手必备)
-
环境配置错误:Java 版本不兼容
- 现象:运行
pyspark或脚本时,提示「Unsupported major.minor version」或 Java 相关报错。 - 解决:卸载高版本 Java,安装 Java 8,重新配置
JAVA_HOME环境变量。
- 现象:运行
-
Spark 会话创建失败:端口被占用
- 现象:提示「Address already in use」。
- 解决:关闭占用端口的进程,或重启电脑,也可在创建 Spark 会话时指定端口:
spark.driver.port=xxxx。
-
数据读取失败:文件路径问题
- 现象:读取本地文件时提示「File not found」。
- 解决:① 使用绝对路径(如
D:\ecommerce\users.csv);② 相对路径需确保脚本与文件在同一目录;③ Windows 路径使用/或\\转义。
-
Driver 内存溢出:
collect()滥用- 现象:处理大数据时,调用
collect()提示「OutOfMemoryError」。 - 解决:避免使用
collect(),改用take(n)、sample()查看部分数据,或直接在 Executor 端完成聚合后再返回结果。
- 现象:处理大数据时,调用
-
DataFrame 数据类型错误:无法进行聚合操作
- 现象:执行
sum()、avg()时提示「cannot resolve 'xxx' due to data type mismatch」。 - 解决:使用
withColumn().cast()转换数据类型为数值类型(int/double)。
- 现象:执行
-
中文乱码问题:保存 CSV 文件或可视化时中文显示异常
- 解决:① 保存 CSV 时添加编码配置:
.option("encoding", "UTF-8");② 可视化时设置 Matplotlib 支持中文。
- 解决:① 保存 CSV 时添加编码配置:
-
PySpark 版本与 Spark 核心包版本不一致
- 现象:运行脚本时提示「Version Mismatch」。
- 解决:确保
pip install pyspark的版本与下载的 Spark 核心包版本一致(如均为 3.5.0)。