PySpark中如何根据DataFrame的qname与qval匹配另一DataFrame对应列并获取符合条件的reqId
PySpark中如何根据DataFrame的qname与qval匹配另一DataFrame对应列并获取符合条件的reqId
我来给你梳理一下实现思路和具体代码,完全贴合你的需求:
首先,我们得明确核心需求:对于dataframe_1中同一个Id下的所有qname,要确保dataframe_2中对应列的值落在该qname的qval列表里,然后提取符合条件的reqId,最后把Id和reqId配对输出。
步骤1:创建示例DataFrame(方便测试)
先把你给出的示例数据转换成PySpark可处理的DataFrame:
from pyspark.sql import SparkSession from pyspark.sql.functions import col, collect_set, create_map, explode, array_contains from pyspark.sql.types import StructType, StructField, StringType, ArrayType, IntegerType from functools import reduce spark = SparkSession.builder.appName("MatchReqId").getOrCreate() # 构建dataframe_1 data1 = [("01", "Mango", [100, 200]), ("01", "Banana", [500, 400, 800])] schema1 = StructType([ StructField("Id", StringType(), True), StructField("qname", StringType(), True), StructField("qval", ArrayType(IntegerType()), True) ]) df1 = spark.createDataFrame(data1, schema1) # 构建dataframe_2 data2 = [(1000, 100, 500, None, None), (1001, 200, 500, None, None), (1002, 200, 800, None, None), (1003, 900, 1100, None, None)] schema2 = StructType([ StructField("reqId", IntegerType(), True), StructField("Mango", IntegerType(), True), StructField("Banana", IntegerType(), True), StructField("Orange", IntegerType(), True), StructField("Apple", IntegerType(), True) ]) df2 = spark.createDataFrame(data2, schema2)
步骤2:清理dataframe_2的无效列
先把dataframe_2中全为NULL的列(比如Orange、Apple)去掉,这些列不参与匹配:
# 筛选出非全NULL的列 non_null_cols = [col_name for col_name in df2.columns if df2.filter(col(col_name).isNotNull()).count() > 0] df2_clean = df2.select(non_null_cols)
步骤3:处理dataframe_1,整理匹配规则
把dataframe_1按Id和qname分组,收集每个qname对应的允许值集合,再按Id聚合生成匹配规则的映射:
# 先把qval数组展开成单行值,再分组收集允许值集合 df1_expanded = df1.select("Id", "qname", explode("qval").alias("val")) df1_grouped = df1_expanded.groupBy("Id", "qname").agg(collect_set("val").alias("allowed_vals")) # 按Id聚合,生成{qname: allowed_vals}的映射 df1_match_rules = df1_grouped.groupBy("Id").agg( create_map("qname", "allowed_vals").alias("match_map") )
步骤4:交叉匹配并过滤符合条件的行
将匹配规则和清理后的dataframe_2做交叉连接,然后动态生成过滤条件,筛选出所有qname都匹配的行:
# 获取需要参与匹配的qname列表(同时存在于df1和df2_clean中) match_qnames = [qname for qname in df1.select("qname").distinct().rdd.flatMap(lambda x: x).collect() if qname in df2_clean.columns] # 交叉连接,让每个Id和所有reqId做匹配检查 cross_join_df = df1_match_rules.crossJoin(df2_clean) # 生成过滤条件:每个qname对应的列值必须在允许集合中 filter_conditions = [] for qname in match_qnames: filter_conditions.append(array_contains(col(f"match_map.{qname}"), col(qname))) # 所有条件必须同时满足 final_condition = reduce(lambda a, b: a & b, filter_conditions) # 筛选结果并选择需要的列 result_df = cross_join_df.filter(final_condition).select("Id", "reqId") # 查看结果 result_df.show()
运行后你会得到预期的输出:
+---+-----+ | Id|reqId| +---+-----+ | 01| 1000| | 01| 1001| | 01| 1002| +---+-----+
思路补充
- 动态生成过滤条件的好处是不管你有多少个qname,代码都能自动适配,不用硬编码每个条件
- 用
collect_set是为了去重qval里的重复值,避免重复检查 - 交叉连接后过滤的方式,能确保每个Id都能匹配到所有符合条件的reqId
备注:内容来源于stack exchange,提问作者sys
相关产品推荐
相关产品推荐

