如何在PySpark DataFrame分组后聚合多个字符串列?
解决方案:PySpark分组聚合多类型列并消除空值
问题场景
你有一个包含字符串(string)和数值(double)类型列的PySpark DataFrame,结构如下:
+---+---+--------+-----+------+----+ |tin|ecu|DcyStart|did1 |did2 |did3| +---+---+--------+-----+------+----+ |1 |1 |1 |34 |null |null| |1 |1 |2 |null |2 |null| |1 |1 |3 |null |null |b | |1 |1 |4 |null |null |null| |1 |2 |1 |40 |null |null| |1 |2 |2 |null |2 |null| |1 |2 |3 |null |null |f | |1 |2 |4 |null |null |null| +---+---+--------+----+-------+----+
需求是:
- 移除
DcyStart列 - 按
tin和ecu分组,每个did列保留任意一个非空值(部分列可能有多行非空,取任意即可) - 处理100+个
did列,避免手动逐个编写逻辑
之前尝试用first()、max()等聚合函数时遇到两个问题:聚合函数无法处理字符串类型、聚合函数不能直接接收多列参数。
解决方法
核心思路是对每个did列单独应用支持多类型的聚合函数,并跳过空值,利用列表推导式批量生成聚合逻辑,无需手动处理每一列。
步骤1:导入PySpark函数库
from pyspark.sql import functions as F
步骤2:定义需要聚合的列列表
list_of_dids = ["did1", "did2", "did3"] # 替换为你的100+列名称列表
步骤3:批量生成聚合表达式
使用first()函数并设置ignorenulls=True,该函数支持字符串和数值类型,会自动跳过空值,取第一个非空值(符合"任意非空值"的需求):
agg_exprs = [F.first(col, ignorenulls=True).alias(col) for col in list_of_dids]
步骤4:执行分组聚合
# 先移除不需要的DcyStart列,再分组聚合 result = data.drop("DcyStart").groupBy("tin", "ecu").agg(*agg_exprs)
验证结果
执行后得到的DataFrame结构如下:
+---+----+-----+----+----+ |tin|ecu |did1 |did2|did3| +---+----+-----+----+----+ |1 |1 |34 |2 |b | |1 |2 |40 |2 |f | +---+----+-----+----+----+
为什么之前的代码出错?
你之前的写法data.groupBy("tin", "ecu").first(*list_of_dids)存在两个问题:
first()方法直接调用在GroupedData对象上时,不带参数是取每组的第一行完整数据,不能直接传入多列参数;- 未设置
ignorenulls=True,导致空值被优先返回,且未针对每个列单独处理聚合逻辑。
可选替代方案
如果部分列需要取其他规则的非空值(比如字符串取字典序最大/最小值),可以替换聚合函数:
- 数值类型:
F.max(col)、F.min(col) - 字符串类型:
F.max(col)(取字典序最大)、F.min(col)(取字典序最小)
只需修改聚合表达式即可:
# 示例:用max()替代first() agg_exprs = [F.max(col).alias(col) for col in list_of_dids]
内容的提问来源于stack exchange,提问作者RichardMartin
相关产品推荐
相关产品推荐

