Разделение словаря в фрейме данных Pyspark на отдельные столбцы - PullRequest
0 голосов
/ 30 октября 2018

У меня есть фрейм данных (в Pyspark), который имеет одно из значений строки в качестве словаря:

df.show()

И это выглядит так:

+----+---+-----------------------------+
|name|age|info                         |
+----+---+-----------------------------+
|rob |26 |{color: red, car: volkswagen}|
|evan|25 |{color: blue, car: mazda}    |
+----+---+-----------------------------+

На основе комментариев, чтобы дать больше:

df.printSchema()

Типы являются строками

root
 |-- name: string (nullable = true)
 |-- age: string (nullable = true)
 |-- dict: string (nullable = true)

Можно ли взять ключи из словаря (цвет и автомобиль) и сделать их столбцами в кадре данных, а значения должны быть строками для этих столбцов?

Ожидаемый результат:

+----+---+-----------------------------+
|name|age|color |car                   |
+----+---+-----------------------------+
|rob |26 |red   |volkswagen            |
|evan|25 |blue  |mazda                 |
+----+---+-----------------------------+

Я не знал, что мне пришлось использовать df.withColumn () и каким-то образом перебирать словарь, чтобы выбрать каждый из них, а затем сделать из него столбец? До сих пор я пытался найти некоторые ответы, но большинство из них использовали Pandas, а не Spark, поэтому я не уверен, что смогу применить ту же логику.

1 Ответ

0 голосов
/ 31 октября 2018

Ваши строки:

"{color: red, car: volkswagen}"
"{color: blue, car: mazda}"

не в дружественном для Python формате. Они не могут быть проанализированы с помощью json.loads, а также не могут быть оценены с помощью ast.literal_eval.

Однако, если вы знали ключи заранее и можете предполагать, что строки всегда в этом формате, вы должны использовать pyspark.sql.functions.regexp_extract:

Например:

from pyspark.sql.functions import regexp_extract

df.withColumn("color", regexp_extract("info", "(?<=color: )\w+(?=(,|}))", 0))\
    .withColumn("car", regexp_extract("info", "(?<=car: )\w+(?=(,|}))", 0))\
    .show(truncate=False)
#+----+---+-----------------------------+-----+----------+
#|name|age|info                         |color|car       |
#+----+---+-----------------------------+-----+----------+
#|rob |26 |{color: red, car: volkswagen}|red  |volkswagen|
#|evan|25 |{color: blue, car: mazda}    |blue |mazda     |
#+----+---+-----------------------------+-----+----------+

Узор:

  • (?<=color: ): положительный взгляд на буквальную строку "color: "
  • \w+: один или несколько символов слова
  • (?=(,|})): положительный прогноз для буквальной запятой или закрытой фигурной скобки.

Вот как можно обобщить это для более чем двух ключей и обработать случай, когда ключ не существует в строке.

from pyspark.sql.functions import regexp_extract, when, col
from functools import reduce

keys = ["color", "car", "year"]
pat = "(?<=%s: )\w+(?=(,|}))"

df = reduce(
    lambda df, c: df.withColumn(
        c,
        when(
            col("info").rlike(pat%c),
            regexp_extract("info", pat%c, 0)
        )
    ),
    keys,
    df
)

df.drop("info").show(truncate=False)
#+----+---+-----+----------+----+
#|name|age|color|car       |year|
#+----+---+-----+----------+----+
#|rob |26 |red  |volkswagen|null|
#|evan|25 |blue |mazda     |null|
#+----+---+-----+----------+----+

В этом случае мы используем pyspark.sql.functions.when и pyspark.sql.Column.rlike, чтобы проверить, содержит ли строка шаблон, прежде чем пытаться извлечь совпадение.


Если вы не знаете ключей заранее, вам придется написать собственный синтаксический анализатор или попытаться изменить данные в восходящем потоке.

...