PySpark 比较空地图文字

PySpark Compare Empty Map Literal

我想在 PySpark DataFrame 中删除行,其中特定列包含一个空映射。我该怎么做呢?我似乎无法声明一个类型化的空 MapType 来与我的专栏进行比较。我已经看到在 Scala 中,您可以使用 typedLit,但在 PySpark 中似乎没有这样的等价物。我也尝试过使用 lit(...) 并转换为 struct<string,int> 但我发现 lit() 没有可接受的参数(尝试使用 None which returns null and {} 这是一个错误)。

我确定这是微不足道的,但我还没有看到这方面的任何文档!

这是一个使用 pyspark size 内置函数的解决方案:

from pyspark.sql.functions import col, size

df = spark.createDataFrame(
  [(1, {1:'A'} ),
  (2, {2:'B'} ), 
  (3, {3:'C'} ),
  (4, {}),
  (5, None)]
).toDF("id", "map")

df.printSchema()
# root
#  |-- id: long (nullable = true)
#  |-- map: map (nullable = true)
#  |    |-- key: long
#  |    |-- value: string (valueContainsNull = true)

df.withColumn("is_empty", size(col("map")) <= 0).show()

# +---+--------+--------+
# | id|     map|is_empty|
# +---+--------+--------+
# |  1|[1 -> A]|   false|
# |  2|[2 -> B]|   false|
# |  3|[3 -> C]|   false|
# |  4|      []|    true|
# |  5|    null|    true|
# +---+--------+--------+

注意条件是 size <= 0 因为在 null 的情况下函数 returns -1(如果 spark.sql.legacy.sizeOfNull 设置为真,否则它将 return无效的)。 Here您可以找到更多详细信息。

通用解决方案:比较 Map 列和文字 Map

对于更通用的解决方案,我们可以将内置函数 size 与 UDF 结合使用,将每个项目的字符串 key + value 附加到排序列表中(感谢@jxc指出以前版本的问题)。这里的假设是两个地图在以下情况下相等:

  1. 它们的大小相同
  2. key + value 的字符串表示在映射项之间是相同的

文字映射是通过 map_from_arrays:

结合键和值的任意 python 字典创建的
from pyspark.sql.functions import udf, lit, size, when, map_from_arrays, array

df = spark.createDataFrame([
   [1, {}],
   [2, {1:'A', 2:'B', 3:'C'}],
   [3, {1:'A', 2:'B'}]
  ]).toDF("key", "map")

dict = { 1:'A' , 2:'B' }

map_keys_ = array([lit(k) for k in dict.keys()])
map_values_ = array([lit(v) for v in dict.values()])
tmp_map = map_from_arrays(map_keys_, map_values_) 

to_strlist_udf = udf(lambda d: sorted([str(k) + str(d[k]) for k in d.keys()]))

def map_equals(m1, m2):
  return when(
            (size(m1) == size(m2)) & 
            (to_strlist_udf(m1) == to_strlist_udf(m2)), True
          ).otherwise(False)

df = df.withColumn("equals", map_equals(df["map"], tmp_map))

df.show(10, False)

# +---+------------------------+------+
# |key|map                     |equals|
# +---+------------------------+------+
# |1  |[]                      |false |
# |2  |[1 -> A, 2 -> B, 3 -> C]|false |
# |3  |[1 -> A, 2 -> B]        |true  |
# +---+------------------------+------+

注意:如您所见,pyspark == 运算符也非常适合数组比较。