pyspark: преобразование строки в структуру - PullRequest
1 голос
/ 19 февраля 2020

У меня есть данные следующим образом -

{
    "Id": "01d3050e",
    "Properties": "{\"choices\":null,\"object\":\"demo\",\"database\":\"pg\",\"timestamp\":\"1581534117303\"}",
    "LastUpdated": 1581530000000,
    "LastUpdatedBy": "System"
}

Используя клей aws, я хочу реляционизировать столбец «Свойства», но поскольку тип данных является строкой, это сделать невозможно. Преобразование его в структуру может сделать это на основе чтения этого блога -

https://aws.amazon.com/blogs/big-data/simplify-querying-nested-json-with-the-aws-glue-relationalize-transform/

>>> df.show
<bound method DataFrame.show of DataFrame[Id: string, LastUpdated: bigint, LastUpdatedBy: string, Properties: string]>
>>> df.show()
+--------+-------------+-------------+--------------------+
|      Id|  LastUpdated|LastUpdatedBy|          Properties|
+--------+-------------+-------------+--------------------+
|01d3050e|1581530000000|       System|{"choices":null,"...|
+--------+-------------+-------------+--------------------+

Как можно вложить столбец "свойства" в разбейте его на столбцы «выборы», «объект», «база данных» и «отметка времени», используя реляционный преобразователь или любой UDF в pyspark.

Ответы [ 3 ]

1 голос
/ 19 февраля 2020

Используйте from_json, поскольку столбец Properties является строкой JSON.

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

schema = StructType([StructField("choices", StringType(), True),
                    StructField("object", StringType(), True),
                    StructField("database", StringType(), True),
                    StructField("timestamp", StringType(), True)],
                    )

df.withColumn("Properties", from_json(col("Properties"), schema)).show(truncate=False)

#+--------+-------------+-------------+---------------------------+
#|Id      |LastUpdated  |LastUpdatedBy|Properties                 |
#+--------+-------------+-------------+---------------------------+
#|01d3050e|1581530000000|System       |[, demo, pg, 1581534117303]|
#+--------+-------------+-------------+---------------------------+

Однако, если схема может меняться от одной строки к другой, я бы предложил вместо этого преобразовать ее в тип карты:

df.withColumn("Properties", from_json(col("Properties"), MapType(StringType(), StringType()))).show(truncate=False)

#+--------+-------------+-------------+------------------------------------------------------------------------+
#|Id      |LastUpdated  |LastUpdatedBy|Properties                                                              |
#+--------+-------------+-------------+------------------------------------------------------------------------+
#|01d3050e|1581530000000|System       |[choices ->, object -> demo, database -> pg, timestamp -> 1581534117303]|
#+--------+-------------+-------------+------------------------------------------------------------------------+

Затем вы можете получить доступ к элементам карты, используя element_at (Spark 2.4 +)

1 голос
/ 19 февраля 2020

Создание вашего фрейма данных:

from pyspark.sql import functions as F
list=[["01d3050e","{\"choices\":null,\"object\":\"demo\",\"database\":\"pg\",\"timestamp\":\"1581534117303\"}",1581530000000,"System"]]
df=spark.createDataFrame(list, ['Id','Properties','LastUpdated','LastUpdatedBy'])
df.show(truncate=False)

+--------+----------------------------------------------------------------------------+-------------+-------------+
|Id      |Properties                                                                  |LastUpdated  |LastUpdatedBy|
+--------+----------------------------------------------------------------------------+-------------+-------------+
|01d3050e|{"choices":null,"object":"demo","database":"pg","timestamp":"1581534117303"}|1581530000000|System       |
+--------+----------------------------------------------------------------------------+-------------+-------------+

Использование встроенных регулярных выражений, разбиения и element_at:

Нет необходимости использовать UDF, встроенные функции адекватны и очень оптимизированы для задач с большими данными.

df.withColumn("Properties", F.split(F.regexp_replace(F.regexp_replace((F.regexp_replace("Properties",'\{|}',"")),'\:',','),'\"|"',"").cast("string"),','))\
.withColumn("choices", F.element_at("Properties",2))\
.withColumn("object", F.element_at("Properties",4))\
.withColumn("database",F.element_at("Properties",6))\
.withColumn("timestamp",F.element_at("Properties",8).cast('long')).drop("Properties").show()


+--------+-------------+-------------+-------+------+--------+-------------+
|      Id|  LastUpdated|LastUpdatedBy|choices|object|database|    timestamp|
+--------+-------------+-------------+-------+------+--------+-------------+
|01d3050e|1581530000000|       System|   null|  demo|      pg|1581534117303|
+--------+-------------+-------------+-------+------+--------+-------------+


root
 |-- Id: string (nullable = true)
 |-- LastUpdated: long (nullable = true)
 |-- LastUpdatedBy: string (nullable = true)
 |-- choices: string (nullable = true)
 |-- object: string (nullable = true)
 |-- database: string (nullable = true)
 |-- timestamp: long (nullable = true)
0 голосов
/ 21 февраля 2020

Поскольку я использовал службу AWS Glue, в итоге я использовал класс "Unbox", чтобы распаковать строковое поле в dynamicFrame. Работал хорошо для моего варианта использования.

https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-crawler-pyspark-transforms-Unbox.html

unbox = Unbox.apply(frame = dynamic_dframe, path = "Properties", format="json")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...