Python 使用PySpark解析具有大量唯一键(不是对象列表)的JSON对象
我目前正在处理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_
{
"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
本身是一个非常有趣的解决方案。我还没有测试你的解决方案,但看起来很有希望!谢谢这是有道理的,而且看起来很简单!如上所述,我控制了源数据,并将源数据更改为更兼容的格式。但是,我希望你的回答对其他人有所帮助!