如何在Apache Spark - Pyspark中匹配2列

时间:2018-04-07 08:33:03

标签: apache-spark pyspark apache-spark-sql spark-dataframe

我有一个数据框,所以假设我的数据是表格格式。

|ID   |       Serial               |    Updated
-------------------------------------------------------
|10   |pers1                       |                  |
|20   |                            |                  |
|30   |entity_1, entity_2, entity_3|entity_1, entity_3|

现在使用withColumn(" Serial",explode(split(",")" Serial")))。我已经将列分成多行,如下所示。这是要求的第1部分。

   |ID   |       Serial    |    Updated
    -------------------------------------------------------
    |10   |pers1           |                  |
    |20   |                |                  |
    |30   |entity_1        |entity_1, entity_3|
    |30   |entity_2        |entity_1, entity_3|
    |30   |entity_3        |entity_1, entity_3| 

现在对于没有值的列,它应该是0, 对于'序列' 中的值,应在'更新' 列中搜索列。如果该值出现在已更新'然后它应该显示' 1'别的' 2'

所以在这种情况下对于entity_1&& entity_3 - > 1必须显示& for entity_2 - >应该显示2

如何实现这个目标..?

1 个答案:

答案 0 :(得分:0)

AFAIK,无法使用udf直接检查是否包含一列或是否是另一列的子字符串。

但是,如果您想避免使用udf,则一种方法是展开"Updated"列。然后,您可以检查"Serial"列与展开的"Updated"列之间的相等性并应用您的条件(如果匹配则为1,否则为2) - 调用此"contains"

最后,您可以groupBy("ID", "Serial", "Updated")并选择"contains"列的最小值。

例如,在两次调用explode()并检查您的情况后,您将拥有如下所示的DataFrame:

df.withColumn("Serial", f.explode(f.split("Serial", ",")))\
    .withColumn("updatedExploded", f.explode(f.split("Updated", ",")))\
    .withColumn(
        "contains",
        f.when(
            f.isnull("Serial") | 
            f.isnull("Updated") | 
            (f.col("Serial") == "") | 
            (f.col("Updated") == ""),
            0
        ).when(
            f.col("Serial") == f.col("updatedExploded"),
            1
        ).otherwise(2)
    )\
    .show(truncate=False)
#+---+--------+-----------------+---------------+--------+
#|ID |Serial  |Updated          |updatedExploded|contains|
#+---+--------+-----------------+---------------+--------+
#|10 |pers1   |                 |               |0       |
#|20 |        |                 |               |0       |
#|30 |entity_1|entity_1,entity_3|entity_1       |1       |
#|30 |entity_1|entity_1,entity_3|entity_3       |2       |
#|30 |entity_2|entity_1,entity_3|entity_1       |2       |
#|30 |entity_2|entity_1,entity_3|entity_3       |2       |
#|30 |entity_3|entity_1,entity_3|entity_1       |2       |
#|30 |entity_3|entity_1,entity_3|entity_3       |1       |
#+---+--------+-----------------+---------------+--------+

"技巧"按("ID", "Serial", "Updated")进行分组并使"contains"的最小值起作用是因为:

  • 如果"Serial""Updated"为空(或在此情况下等于空字符串),则该值将为0.
  • 如果"Updated"中至少有一个值与"Serial"匹配,则其中一列将为1。
  • 如果没有匹配项,则只有2个

最终输出:

df.withColumn("Serial", f.explode(f.split("Serial", ",")))\
    .withColumn("updatedExploded", f.explode(f.split("Updated", ",")))\
    .withColumn(
        "contains",
        f.when(
            f.isnull("Serial") |
            f.isnull("Updated") |
            (f.col("Serial") == "") |
            (f.col("Updated") == ""),
            0
        ).when(
            f.col("Serial") == f.col("updatedExploded"),
            1
        ).otherwise(2)
    )\
    .groupBy("ID", "Serial", "Updated")\
    .agg(f.min("contains").alias("contains"))\
    .sort("ID")\
    .show(truncate=False)
#+---+--------+-----------------+--------+
#|ID |Serial  |Updated          |contains|
#+---+--------+-----------------+--------+
#|10 |pers1   |                 |0       |
#|20 |        |                 |0       |
#|30 |entity_3|entity_1,entity_3|1       |
#|30 |entity_2|entity_1,entity_3|2       |
#|30 |entity_1|entity_1,entity_3|1       |
#+---+--------+-----------------+--------+

chaining callspyspark.sql.functions.when()检查条件。第一部分检查任一列是null还是等于空字符串。我相信您可能只需要在实际数据中检查null,但我会根据您显示示例DataFrame的方式检查空字符串。