PySpark Сравнить пустую карту Литерал - PullRequest
2 голосов
/ 29 сентября 2019

Я хочу удалить строки в PySpark DataFrame, где определенный столбец содержит пустую карту. Как мне это сделать? Я не могу объявить типизированный пустой MapType для сравнения моего столбца. Я видел, что в Scala вы можете использовать typedLit, но в PySpark такого эквивалента нет. Я также пытался использовать lit(...) и приведение к struct<string,int>, но я не нашел приемлемого аргумента для lit() (попытался использовать None, который возвращает ноль, и {}, что является ошибкой).

Я уверен, что это тривиально, но я не видел никаких документов по этому поводу!

1 Ответ

3 голосов
/ 29 сентября 2019

Вот решение с использованием встроенной функции 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, так как в случае нулевого значения функция возвращает -1 (если spark.sql.legacy.sizeOfNull значение true, в противном случае возвращается ноль). Здесь вы можете найти более подробную информацию.

Общее решение: сравнение столбца Map и литерала Map

Для более общего решения мы можем использовать встроенную функцию size в сочетании с UDF, который добавляетстрока key + value каждого элемента в отсортированном списке (спасибо @jxc за указание на проблему с предыдущей версией). Гипотеза здесь будет состоять в том, что две карты равны, когда:

  1. они имеют одинаковый размер
  2. строковое представление ключ + значение одинаково между элементами карт

Буквенная карта создается из произвольного словаря python, объединяющего ключи и значения с помощью map_from_arrays:

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 == работает довольно хорошо дляСравнение массивов.

...