如何在PySpark中基于数值列筛选列表列数据:保留超集及列值大于其超集的子集
解决PySpark DataFrame的超集/子集筛选问题
这个需求挺清晰的,我来一步步帮你实现!首先明确我们要保留两类行:
- 所有超集行:即没有其他列表能完全包含当前行的
column a的行 - 符合条件的子集行:当前行的
column a是某个超集的子集,且它的column b值大于该超集的column b值
第一步:创建示例输入DataFrame
先把你给出的示例数据转换成PySpark DataFrame,方便后续测试:
from pyspark.sql import SparkSession from pyspark.sql import functions as F spark = SparkSession.builder.appName("subset_superset_filter").getOrCreate() # 输入数据 data = [ (["A", "B", "C"], 10), (["A", "C"], 15), (["B", "C"], 7), (["J", "S", "K"], 8), (["J", "S"], 9), (["J", "K"], 8) ] df = spark.createDataFrame(data, schema=["a", "b"]) df.show(truncate=False)
运行后会得到你的输入DataFrame:
+---------+---+ |a |b | +---------+---+ |[A,B,C] |10 | |[A,C] |15 | |[B,C] |7 | |[J,S,K] |8 | |[J,S] |9 | |[J,K] |8 | +---------+---+
第二步:找出所有子集-超集对应关系
我们通过自连接DataFrame,找出所有子集和其对应的超集(即列表被完全包含且长度更短的行):
# 自连接,匹配子集和超集 subset_superset_pairs = df.alias("subset").join( df.alias("superset"), # 判断条件:subset的列表是superset的子集,且长度更小(避免自己匹配自己) F.expr("size(subset.a) < size(superset.a) AND array_except(subset.a, superset.a) == array()") ).select( F.col("subset.a").alias("subset_a"), F.col("subset.b").alias("subset_b"), F.col("superset.b").alias("superset_b") ) subset_superset_pairs.show(truncate=False)
这一步会得到所有符合子集-超集关系的配对:
+--------+---------+----------+ |subset_a|subset_b |superset_b| +--------+---------+----------+ |[A,C] |15 |10 | |[B,C] |7 |10 | |[J,S] |9 |8 | |[J,K] |8 |8 | +--------+---------+----------+
第三步:筛选符合条件的子集行
从上面的配对中,筛选出subset_b大于superset_b的子集行,并且去重(避免同一个子集对应多个超集时重复保留):
# 筛选有效子集行:subset_b > superset_b valid_subsets = subset_superset_pairs.filter( F.col("subset_b") > F.col("superset_b") ).select("subset_a", "subset_b").withColumnRenamed("subset_a", "a").withColumnRenamed("subset_b", "b").distinct() valid_subsets.show(truncate=False)
运行后得到有效的子集行:
+-------+---+ |a |b | +-------+---+ |[A,C] |15 | |[J,S] |9 | +-------+---+
第四步:筛选所有超集行
超集行是指没有任何其他列表能完全包含它的行,我们可以通过判断是否存在更大的超集来筛选:
# 筛选超集行:不存在其他行的列表包含当前行的列表且长度更长 superset_rows = df.filter( F.expr("NOT EXISTS (SELECT 1 FROM df other WHERE size(other.a) > size(a) AND array_except(a, other.a) == array())") ) superset_rows.show(truncate=False)
得到的超集行:
+---------+---+ |a |b | +---------+---+ |[A,B,C] |10 | |[J,S,K] |8 | +---------+---+
第五步:合并结果
最后把超集行和有效子集行合并,去重后就是我们需要的最终结果:
# 合并超集行和有效子集行 final_result = superset_rows.union(valid_subsets).distinct() final_result.show(truncate=False)
最终输出和你预期的一致:
+---------+---+ |a |b | +---------+---+ |[A,B,C] |10 | |[A,C] |15 | |[J,S,K] |8 | |[J,S] |9 | +---------+---+
补充说明
- 如果一个子集对应多个超集,只要其中一个超集的
b值小于它的b值,就会被保留 - 用
array_except判断子集关系是PySpark中比较简洁的方式,确保子集的所有元素都在超集中 - 整个流程都是基于PySpark的原生函数,适合处理大数据量的场景
内容的提问来源于stack exchange,提问作者Sandhyalakshmi K
相关产品推荐
相关产品推荐

