从0开始学习pyspark--用户自定义函数(UDF)和 pandas UDF[第6节]

在大数据处理中,数据的清洗和转换是不可避免的过程。PySpark 作为一个强大的分布式数据处理框架,提供了许多内置的函数来处理数据。然而,有时候我们需要执行一些更复杂或特定的逻辑,这时用户自定义函数(UDF)就派上用场了。在这篇博客中,我们将详细介绍 PySpark UDF 和 pandas UDF 的应用,并通过几个示例展示如何使用这些 UDF 处理数据。

什么是 PySpark UDF 和 pandas UDF?

PySpark UDF(User Defined Function,用户自定义函数)允许用户在 Spark SQL 查询中使用自定义的 Python 函数,从而增强数据处理的灵活性和功能。UDF 使我们能够实现复杂的逻辑,处理 Spark SQL 内置函数无法覆盖的场景。

pandas UDF(也称为 Vectorized UDF)是 PySpark 中的一种特殊类型的 UDF,利用了 Apache Arrow 来加速数据传输和处理。与传统的 PySpark UDF 不同,pandas UDF 在处理数据时能够利用 pandas 的高效数据操作能力,因此在处理大型数据集时具有显著的性能优势。

如何创建和使用 PySpark UDF

创建 PySpark UDF

创建 UDF 包括定义一个 Python 函数并使用 pyspark.sql.functions.udf 将其转换为 UDF。转换时还需要指定返回值的数据类型。

python 复制代码
from pyspark.sql import SparkSession
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType

# 创建 SparkSession
spark = SparkSession.builder.appName("PySpark UDF Example").getOrCreate()

# 定义一个 Python 函数
def convert_case(text):
    return text.upper()

# 将 Python 函数转换为 UDF,并

指定返回值的数据类型
convert_case_udf = udf(lambda z: convert_case(z), StringType())
使用 PySpark UDF

创建 UDF 后,可以通过 withColumnselect 方法将 UDF 应用于 DataFrame。

python 复制代码
# 创建一个示例 DataFrame
data = [("James", "Smith"), ("Anna", "Rose"), ("Robert", "Williams")]
columns = ["First_Name", "Last_Name"]
df = spark.createDataFrame(data, columns)

# 使用 UDF 在 DataFrame 上创建新列
df_with_udf = df.withColumn("Uppercase_First_Name", convert_case_udf(df["First_Name"]))

# 显示结果
df_with_udf.show()

如何创建和使用 pandas UDF

创建 pandas UDF

要在 PySpark 中创建 pandas UDF,需要导入 pyspark.sql.functions.pandas_udf 模块,并使用 pandas_udf 装饰器将函数转换为 pandas UDF。与传统 UDF 不同,pandas UDF 可以处理 pandas Series 作为输入和输出。

python 复制代码
from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import StringType

# 定义一个 Python 函数
def convert_case_pandas(s: pd.Series) -> pd.Series:
    return s.str.upper()

# 将 Python 函数转换为 pandas UDF,并指定返回值的数据类型
convert_case_pandas_udf = pandas_udf(convert_case_pandas, StringType())
使用 pandas UDF

与 PySpark UDF 类似,pandas UDF 也可以通过 withColumnselect 方法应用于 DataFrame。

python 复制代码
# 使用 pandas UDF 在 DataFrame 上创建新列
df_with_pandas_udf = df.withColumn("Uppercase_First_Name", convert_case_pandas_udf(df["First_Name"]))

# 显示结果
df_with_pandas_udf.show()

实战案例

案例 1:字符串操作

假设我们有一个客户姓名的 DataFrame,需要将客户的姓和名合并成一个新的列,并将其转换为大写。

使用 PySpark UDF
python 复制代码
# 定义合并姓名的函数
def merge_names(first_name, last_name):
    return f"{first_name} {last_name}".upper()

# 将函数转换为 UDF
merge_names_udf = udf(lambda x, y: merge_names(x, y), StringType())

# 使用 UDF 合并姓名并创建新列
df_with_merged_names = df.withColumn("Full_Name", merge_names_udf(df["First_Name"], df["Last_Name"]))

# 显示结果
df_with_merged_names.show()
使用 pandas UDF
python 复制代码
# 定义合并姓名的函数
def merge_names_pandas(first_name: pd.Series, last_name: pd.Series) -> pd.Series:
    return (first_name + " " + last_name).str.upper()

# 将函数转换为 pandas UDF
merge_names_pandas_udf = pandas_udf(merge_names_pandas, StringType())

# 使用 pandas UDF 合并姓名并创建新列
df_with_merged_names_pandas = df.withColumn("Full_Name", merge_names_pandas_udf(df["First_Name"], df["Last_Name"]))

# 显示结果
df_with_merged_names_pandas.show()
案例 2:数值计算

假设我们有一个包含产品价格和税率的 DataFrame,现在需要计算每个产品的最终价格。

使用 PySpark UDF
python 复制代码
from pyspark.sql.types import FloatType

# 定义计算最终价格的函数
def calculate_final_price(price, tax):
    return price + (price * tax)

# 将函数转换为 UDF
calculate_final_price_udf = udf(lambda x, y: calculate_final_price(x, y), FloatType())

# 创建一个示例 DataFrame
data = [(100, 0.2), (200, 0.1), (300, 0.15)]
columns = ["Price", "Tax"]
df = spark.createDataFrame(data, columns)

# 使用 UDF 计算最终价格并创建新列
df_with_final_price = df.withColumn("Final_Price", calculate_final_price_udf(df["Price"], df["Tax"]))

# 显示结果
df_with_final_price.show()
使用 pandas UDF
python 复制代码
# 定义计算最终价格的函数
def calculate_final_price_pandas(price: pd.Series, tax: pd.Series) -> pd.Series:
    return price + (price * tax)

# 将函数转换为 pandas UDF
calculate_final_price_pandas_udf = pandas_udf(calculate_final_price_pandas, FloatType())

# 使用 pandas UDF 计算最终价格并创建新列
df_with_final_price_pandas = df.withColumn("Final_Price", calculate_final_price_pandas_udf(df["Price"], df["Tax"]))

# 显示结果
df_with_final_price_pandas.show()

性能优化建议

虽然 PySpark UDF 提供了强大的功能,但由于其在 Python 解释器中运行,性能可能较低。为了提高性能,建议尽量使用 Spark SQL 内置函数或 pandas UDF(vectorized UDF),后者在处理速度上更加高效。

结论

PySpark UDF 和 pandas UDF 是处理复杂数据转换和清洗任务的有力工具。通过将自定义逻辑封装为 UDF,我们可以扩展 Spark SQL 的功能,满足特定的数据处理需求。在使用 UDF 时需要注意性能问题,并考虑使用更高效的替代方案。希望这篇博客能帮助你更好地理解和使用 PySpark UDF 和 pandas UDF。


相关推荐
旻璿gg9 分钟前
paddleocr、paddleocrvl、ppocrv5
python
清水白石00811 分钟前
手写超速 CSV 解析器:利用 multiprocessing 与 mmap 实现 10 倍 Pandas 加速
python·pandas
Corleo27 分钟前
记录一次复杂的 ONNX 到 TensorRT 动态 Shape 转换排错过程
python·ai
shughui42 分钟前
Python基础面试题:语言定位+数据类型+核心操作+算法实战(含代码实例)
开发语言·python·算法
No0d1es1 小时前
2025年12月电子学会青少年软件编程Python六级等级考试真题试卷
开发语言·python·青少年编程·等级考试·电子学会
Blossom.1181 小时前
Transformer架构优化实战:从MHA到MQA/GQA的显存革命
人工智能·python·深度学习·react.js·架构·aigc·transformer
溪海莘1 小时前
如何部署使用uv管理依赖的python项目 ?
开发语言·python·uv
我送炭你添花1 小时前
Python与串口:从基础到实际应用——以Pelco KBD300A模拟器项目为例
开发语言·python·自动化·运维开发
效率客栈老秦1 小时前
Python Trae提示词开发实战(12):AI实现API自动化批量调用与数据处理让效率提升10倍
人工智能·python·ai·prompt·trae
高洁012 小时前
CLIP 的双编码器架构是如何优化图文关联的?(2)
python·深度学习·机器学习·知识图谱