You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PySpark中实现双层级的集合聚合操作?

实现PySpark两层分组的collect_set聚合

我明白你的需求啦——就是要做两层嵌套分组的聚合:先按第一列做“大分组”,再在每个大组里按第二列拆成“子分组”,最后把子分组里指定列的值用collect_set收集成集合对吧?我给你一步步演示怎么实现,先补全你的示例DataFrame,再上代码~

1. 先构造完整的示例数据

假设你的原始DataFrame是这样的(补全了你没写完的部分):

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("NestedCollectSet").getOrCreate()

df = spark.createDataFrame([
    ('a', 'x', 11, 22),
    ('a', 'x', 11, 33),
    ('a', 'y', 44, 55),
    ('b', 'x', 66, 77),
    ('b', 'y', 88, 99),
    ('b', 'y', 88, 100)
], ['col1', 'col2', 'col3', 'col4'])

2. 分步实现嵌套聚合

我们分两步走,先处理内层子分组的聚合,再整理外层大分组的结果:

第一步:内层子分组聚合

先按col1+col2联合分组,对col3和col4分别调用collect_set,得到每个子分组的集合结果:

from pyspark.sql import functions as F

# 内层聚合:得到每个(col1, col2)对应的col3、col4集合
inner_agg_df = df.groupBy("col1", "col2") \
    .agg(
        F.collect_set("col3").alias("col3_set"),
        F.collect_set("col4").alias("col4_set")
    )

inner_agg_df.show(truncate=False)

这一步的输出是:

+----+----+---------+-----------+
|col1|col2|col3_set |col4_set   |
+----+----+---------+-----------+
|a   |x   |[11]     |[22, 33]   |
|a   |y   |[44]     |[55]       |
|b   |x   |[66]     |[77]       |
|b   |y   |[88]     |[99, 100]  |
+----+----+---------+-----------+

第二步:外层大分组整理

接下来按col1分组,把每个col2对应的聚合结果打包成键值对,最终生成嵌套结构(两种常见格式可选):

格式1:单个Map存储所有子分组结果

把每个col2作为key,对应的col3_set和col4_set打包成结构体作为value:

final_df = inner_agg_df.groupBy("col1") \
    .agg(
        F.map_from_entries(
            F.collect_list(
                F.struct("col2", F.struct("col3_set", "col4_set"))
            )
        ).alias("subgroup_aggregations")
    )

final_df.show(truncate=False)

输出结果:

+----+-----------------------------------------------------------------------------+
|col1|subgroup_aggregations                                                         |
+----+-----------------------------------------------------------------------------+
|a   |{x -> {col3_set: [11], col4_set: [22, 33]}, y -> {col3_set: [44], col4_set: [55]}}|
|b   |{x -> {col3_set: [66], col4_set: [77]}, y -> {col3_set: [88], col4_set: [99, 100]}}|
+----+-----------------------------------------------------------------------------+

格式2:两个独立Map分别存储列的集合

如果需要把col3和col4的集合分开存储,可以这样写:

final_df = inner_agg_df.groupBy("col1") \
    .agg(
        F.map_from_entries(F.collect_list(F.struct("col2", "col3_set"))).alias("col2_to_col3_set"),
        F.map_from_entries(F.collect_list(F.struct("col2", "col4_set"))).alias("col2_to_col4_set")
    )

final_df.show(truncate=False)

输出结果:

+----+---------------------+-----------------------+
|col1|col2_to_col3_set     |col2_to_col4_set       |
+----+---------------------+-----------------------+
|a   |{x -> [11], y -> [44]}|{x -> [22, 33], y -> [55]}|
|b   |{x -> [66], y -> [88]}|{x -> [77], y -> [99, 100]}|
+----+---------------------+-----------------------+

核心思路

因为collect_set是分组级别的聚合函数,无法直接实现嵌套分组,所以我们需要先做「联合分组的内层聚合」,再通过collect_list+map_from_entries把内层结果按外层分组整理成嵌套结构,这样就能完美满足你的需求啦~

内容的提问来源于stack exchange,提问作者ajb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:35:38