Python 使用PySpark解析具有大量唯一键(不是对象列表)的JSON对象

Python 使用PySpark解析具有大量唯一键(不是对象列表)的JSON对象,python,json,apache-spark,pyspark,Python,Json,Apache Spark,Pyspark,我目前正在处理JSON文件中的以下源数据: { "unique_key_1": { "some_value_1": 1, "some_value_2": 2 }, "unique_key_2": { "some_value_1": 2, "some_value_2": 3 } "unique_key_3": { "some_value_1": 2, "some_

我目前正在处理JSON文件中的以下源数据:

{
    "unique_key_1": {
        "some_value_1": 1,
        "some_value_2": 2
    },
    "unique_key_2": {
        "some_value_1": 2,
        "some_value_2": 3
    }
    "unique_key_3": {
        "some_value_1": 2,
        "some_value_2": 1
    }
    ...
}
注意,源数据在一个大字典中是有效的,它有许多唯一的键。它不是一个字典列表。我有很多类似这样的大型JSON文件,我想使用PySpark将其解析为以下数据帧结构:

key          | some_value_1 | some_value_2
-------------------------------------------
unique_key_1 |            1 |            2
unique_key_2 |            2 |            3
unique_key_3 |            2 |            1
如果我处理的是小文件,我可以使用类似以下代码简单地解析此文件:

[{**{"key": k}, **v} for (k, v) in source_dict.items()] 
然后,我将在此列表上创建一个Spark数据框,并继续执行我需要执行的其余操作

我的问题是,我不太明白如何将这样的大型JSON对象解析为数据帧。当我使用
SPARK.read.json(“source_dict.json”)
时,我得到一个数据帧,其中每个唯一的
值(可以预测)作为列读入。请注意,真正的数据文件可能有超过10秒的数千个密钥


我对Spark这个世界还相当陌生,我似乎找不到一个方法来完成这个任务。这似乎是一个支点或类似的东西会有所帮助。是否有人有任何解决方案或指向可能解决方案的指针?谢谢,我很感激

将键放入单独列的最简单方法是在将数据读入Spark之前重新构造json。如果JSON的结构如下所示,您将获得所需的结果:

[
    {"key":"unique_key_1",
        "some_value_1": 1,
        "some_value_2": 2
    },
    {"key":"unique_key_2",
        "some_value_1": 2,
        "some_value_2": 3
    },
    {"key":"unique_key_3",
        "some_value_1": 2,
        "some_value_2": 1
    }
]
如果您无法控制json,则可以将
from_json
列函数与
explode
一起使用。首先,只需将json作为单行单列文本读取,然后解析它

然后首先使用
from_json
解析文本:

json\u schema=MapType(StringType(),StringType())
df.withColumn(“json”,来自_json(col('text'),json_schema))#扩展到键值列

然后,将新创建对象的关键点分解为单独的行:

.select(explode(col('json'))) # make a row for each key in the json
最后,您可以对解包值并将其选择到单独的列中执行相同的操作。下面是一个小演示,可以将所有内容组合在一起:

from pyspark.sql.types import *
from pyspark.sql.functions import *

text_schema = StructType([StructField('text', StringType(), True)])
json_schema = MapType(StringType(), StringType())

data = """{
    "unique_key_1": {
        "some_value_1": 1,
        "some_value_2": 2
    },
    "unique_key_2": {
        "some_value_1": 2,
        "some_value_2": 3
    },
    "unique_key_3": {
        "some_value_1": 2,
        "some_value_2": 1
    }
}
"""

df = (spark.createDataFrame([(data,)], schema=text_schema) # read dataframe
  .withColumn("json", from_json(col('text'), json_schema)) # expand into key-value column 
  .select(explode(col('json'))) # make a row for each key in the json
  .withColumn("value", from_json(col('value'), json_schema)) # now interpret the value for each key as json also
  .withColumn("some_value_1", col("value.some_value_1")) # unpack the object into separate rows
  .withColumn("some_value_2", col("value.some_value_2"))
  .drop('value')
     )

display(df)

使用flatmap,您可以编写一个函数来进行转换

def f(row):
l = []
d = row.asDict()
for k in d.keys():
    l.append(Row(k, d[k][0], d[k][1]))
return Row(*l)

rdd = df.rdd.flatMap(f)
spark.createDataFrame(rdd).show()


+------------+---+---+
|          _1| _2| _3|
+------------+---+---+
|unique_key_1|  1|  2|
|unique_key_2|  2|  3|
|unique_key_3|  2|  1|
+------------+---+---+

如需更多信息,请参阅此

使用RDD更改JSON结构,然后使用
.toDF()
将其转换为DF感谢您的响应!有关于如何更改JSON结构的提示吗?我很感激:谢谢你把这些放在一起!我确实控制了源JSON,所以我已经开始朝这个方向前进。但是,使用
StringType
本身是一个非常有趣的解决方案。我还没有测试你的解决方案,但看起来很有希望!谢谢这是有道理的,而且看起来很简单!如上所述,我控制了源数据,并将源数据更改为更兼容的格式。但是,我希望你的回答对其他人有所帮助!