PySpark 中字符串与空值比较的结果难以理解

Incomprehensible result of a comparison between a string and null value in PySpark

任何人都可以向我解释字符串和空值之间比较的行为。

数据帧:

import pyspark.sql.functions as F

df = spark.createDataFrame([
["text_1,","text_1"],
["text_1,","text_2"],
["text_1,",None]
]).toDF("col1", "col2")


df1 = df.withColumn("equal", F.when(F.col("col1") == F.col("col2"), "equal").otherwise("not equal")
   
+-------+-------+----------+
|col1   |col2   |equal     |
+-------+-------+----------+
|text_1 |text_1 |equal     |
|text_1 |text_2 |not equal |
|text_1 |null   |not equal |  <*
+-------+-------+----------+


df2 = df.withColumn("equal", F.when(F.col("col1") != F.col("col2"), "equal").otherwise("not equal")

+-------+-------+----------+
|col1   |col2   |equal     |
+-------+-------+----------+
|text_1 |text_1 |equal     |
|text_1 |text_2 |not equal |
|text_1 |null   |equal     |   <*
+-------+-------+----------+

is equal 比较似乎很顺利,但 is not equal 却出错了。

任何人都可以向我解释一下吗?我如何在不检查 .isNotNull 或用空字符串填充 null 值(如果可能)的情况下解决这个问题。

之所以得到equal与null比较是因为text1 != null给出的是null,被when语句解释为false,所以得到了意想不到的equal 来自 otherwise 语句。

您可以使用 eqNullSafe,当其中一列为 null 时,returns False 而不是 null。如果要比较不等式,使用eqNullSafe.

的否定~
import pyspark.sql.functions as F

df3 = df.withColumn("equal",
    F.when(~F.col("col1").eqNullSafe(F.col("col2")), "not equal")
     .otherwise("equal")
)

df3.show()
+------+------+---------+
|  col1|  col2|    equal|
+------+------+---------+
|text_1|text_1|    equal|
|text_1|text_2|not equal|
|text_1|  null|not equal|
+------+------+---------+

如果要用空字符串填充空值,可以使用coalesce:

import pyspark.sql.functions as F

df4 = df.withColumn("equal",
    F.when(F.col("col1") != F.coalesce(F.col("col2"), F.lit("")), "not equal")
     .otherwise("equal")
)

df4.show()
+------+------+---------+
|  col1|  col2|    equal|
+------+------+---------+
|text_1|text_1|    equal|
|text_1|text_2|not equal|
|text_1|  null|not equal|
+------+------+---------+

NULL不是数值而是代表没有数值。您不能将它与一个值或另一个 null 进行比较,该比较在 ifwhen 函数中始终被解释为 false。对于 null 安全相等检查,您还可以使用 SQL 表达式 is distinct from:

from pyspark.sql import functions as F

df1 = df.withColumn(
    "equal",
    F.when(
        F.expr("col1 is distinct from col2"), "not equal"
    ).otherwise("equal")
)

df1.show()

# +------+------+---------+
# |  col1|  col2|    equal|
# +------+------+---------+
# |text_1|text_1|    equal|
# |text_1|text_2|not equal|
# |text_1|  null|not equal|
# +------+------+---------+

或使用<=>运算符:

df1 = df.withColumn(
    "equal",
    F.when(
        ~F.expr("col1 <=> col2"), "not equal"
    ).otherwise("equal")
)