使用Pyspark从数组读取JSON项目?

时间:2019-05-13 15:19:11

标签: json pyspark databricks azure-databricks

我在从Cosmos DB中读取数据块中的项目时遇到了一些问题,似乎将JSON作为字符串值读取,并且将数据从其中读取到列中时遇到了一些问题。

我有一列名为ProductRanges的行,其中具有以下值:

[0, 6, 9] 6
[2, 6, 9] 2
6 diminished
2 major

在Cosmos DB中,JSON文档有效,当导入数据时,数据框中的数据类型是字符串,而不是我期望的JSON对象/结构。

我希望能够计算“名称”出现的次数,并通过它们迭代获得最小值,最大值和值项,因为我们可以拥有的范围数可以大于3。我虽然在stackoverflow和其他地方发表过一些文章,但仍然停留在格式上。我尝试使用爆炸并读取基于列值的模式,但是它确实说了“在有效文档中”,认为可能是由于Pyspark在开始和结束时都需要{},但甚至在来自cosmos db的SQL查询仍然以字符串的数据类型结束。

任何指针将不胜感激

2 个答案:

答案 0 :(得分:1)

我看到您从Azure CosmosDB检索了JSON文档并将其转换为PySpark DataFrame,但是嵌套的JSON文档或数组无法按预期方式转换为DataFrame列中的JSON对象,因为没有定义JSON类型在pyspark.sql.types模块中,如下所示。

enter image description here

我在尝试解决文档PySpark: Convert JSON String Column to Array of Object (StructType) in Data Frame时找到了适合您当前情况的文档,甚至与您想要的一样。

上面的文档显示了如何使用ArrayTypeStructTypeStructField和其他基本PySpark数据类型将列中的JSON字符串转换为组合数据类型,可以在通过定义列模式和UDF来实现PySpark。

这是示例代码的摘要。希望对您有所帮助。

source = [{"attr_1": 1, "attr_2": "[{\"a\":1,\"b\":1},{\"a\":2,\"b\":2}]"}, {"attr_1": 2, "attr_2": "[{\"a\":3,\"b\":3},{\"a\":4,\"b\":4}]"}]

JSON通过sqlContext读入数据帧。输出为:

+------+--------------------+

|attr_1|              attr_2|

+------+--------------------+

|     1|[{"a":1,"b":1},{"...|

|     2|[{"a":3,"b":3},{"...|

+------+--------------------+


root
  |-- attr_1: long (nullable = true)
  |-- attr_2: string (nullable = true)

然后,通过定义列模式和UDF来转换attr_2列。

# Function to convert JSON array string to a list
import json

def parse_json(array_str):
    json_obj = json.loads(array_str)
    for item in json_obj:
        yield (item["a"], item["b"])

# Define the schema
from pyspark.sql.types import ArrayType, IntegerType, StructType, StructField

json_schema = ArrayType(StructType([StructField('a', IntegerType(
), nullable=False), StructField('b', IntegerType(), nullable=False)]))

# Define udf
from pyspark.sql.functions import udf

udf_parse_json = udf(lambda str: parse_json(str), json_schema)

# Generate a new data frame with the expected schema

df_new = df.select(df.attr_1, udf_parse_json(df.attr_2).alias("attr_2"))
df_new.show()
df_new.printSchema()

输出如下:

+------+--------------+

|attr_1|        attr_2|

+------+--------------+

|     1|[[1,1], [2,2]]|

|     2|[[3,3], [4,4]]|

+------+--------------+


root
  |-- attr_1: long (nullable = true)
  |-- attr_2: array (nullable = true)
  |    |-- element: struct (containsNull = true)
  |    |    |-- a: integer (nullable = false)
  |    |    |-- b: integer (nullable = false)

答案 1 :(得分:0)

从给定的json数据中,您可以使用printSchema查看数据框的架构并使用它 考虑以下示例:

{"Id":11,"data":[{"package":"com.browser1","activetime":60000},{"package":"com.browser6","activetime":1205000},{"package":"com.browser7","activetime":1205000}]}
{"Id":12,"data":[{"package":"com.browser1","activetime":60000},{"package":"com.browser6","activetime":1205000}]} 
......

appActiveTime.printSchema()
root
 |-- data: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- activetime: long (nullable = true)
 |    |    |-- package: string (nullable = true)

由于具有数组,因此需要展开数据并选择如下的struct字段

import org.apache.spark.sql.functions._
appActiveTime.withColumn("data", explode($"data"))
       .select("data.*")
       .show(false)

输出如下:

+----------+------------+
|activetime|     package|
+----------+------------+
|     60000|com.browser1|
|   1205000|com.browser6|
|   1205000|com.browser7|
|     60000|com.browser1|
|   1205000|com.browser6|
+----------+------------+

希望这会有所帮助。