【发布时间】:2023-03-26 03:10:01
【问题描述】:
目标:从列包含 JSON(数组)的 SQL 表中读取数据,从 JSON 中提取某些键/值到新列中,然后写入新表。原始数据格式的乐趣之一是有些数据记录是 JSON 数组,有些不是数组(只是 JSON)。因此我们可以从开始:
testcase = [(1, [{'a': 1, 'b': 2, 'c': 3}, {'a': 11, 'b': 12, 'c': 13}]),
(2, {'a': 30, 'b': 40}),
(3, {'a': 100, 'b': 200, 'd': 300})]
for x in testcase:
print(x)
(1, [{'a': 1, 'b': 2, 'c': 3}, {'a': 11, 'b': 12, 'c': 13}])
(2, {'a': 30, 'b': 40})
(3, {'a': 100, 'b': 200, 'd': 300})
注意每个元组的第一个元素是记录 ID。第一条记录是长度为 2 的数组,第二条和第三条记录不是数组。 所需的输出是(作为 DataFrame):
a b data
1 1 2 '{"c": 3}'
1 11 12 '{"c": 13}'
2 30 40 '{}'
3 100 200 '{"d": 300}'
在这里,您可以看到我已将键“a”和“b”从字典中提取到新列中,而将剩余的键/值留在原处。 id=2 的空字典是可取的行为。
首先,我将 id 和数据提取到单独的列表中。我借此机会将 dict 制成一个 dicts 列表(长度为 1),因此类型现在是一致的:
id = [x[0] for x in testcase]
data_col = [x[1] if type(x[1]) == list else [x[1]] for x in testcase]
for x in data_col:
print(x)
[{'a': 1, 'b': 2, 'c': 3}, {'a': 11, 'b': 12, 'c': 13}]
[{'a': 30, 'b': 40}]
[{'a': 100, 'b': 200, 'd': 300}]
将 id 和 data_col 提取为单独的列表感觉有点笨拙,尽管至少我们有一个不错的属性,即我们不会复制数据:
id[0] is testcase[0][0]
True
data_col[0] is testcase[0][1]
True
而且,正如我所说,我必须处理一些记录包含 dicts 数组和一些只是 dicts 的问题,所以这使它们都保持一致。
主要的细节发生在这里,我在双列表推导中执行字典推导以迭代每个字典:
popped = [(id, {key: element.pop(key, None) for key in ['a', 'b']}) \
for id, row in zip(id, data_col) for element in row]
for x in popped:
print(x)
(1, {'a': 1, 'b': 2})
(1, {'a': 11, 'b': 12})
(2, {'a': 30, 'b': 40})
(3, {'a': 100, 'b': 200})
我需要能够将每个新行与其原始 id 相关联,以上实现了这一点,正确地再现了适当的 id 值(1、1、2、3)。通过一些整理工作,我可以将所有目标行排成一行:
import pandas as pd
from psycopg2.extras import Json
id2 = [x[0] for x in popped]
cols = [x[1] for x in popped]
data = [Json(item) for sublist in data_col for item in sublist]
popped_df = pd.DataFrame(cols, index=id2)
popped_df['data'] = data
这给了我想要的 DataFrame,如上所示。但是......我所有的清单都是必要的吗?我不能做一个简单的 json_normalize 因为我不想提取所有的键,它会随着数组和非数组的组合而崩溃。
它还需要尽可能高性能,因为它将处理数百万行。出于这个原因,我实际上使用以下方法将 DataFrame 转换为列表: 列表(popped_df.itertuples()) 然后传递给 psycopg2.extras 的 execute_values() 所以我可能还没有费心构建 DataFrame 而只是构建输出列表,但在这篇文章中,我真的想问是否有一种更清洁、更快的方法可以将这些特定键从 dicts 提取到新的列和行中,对于是否record 是否为数组,并跟踪相关的记录 id。
我避开了端到端的 pandas 方法,使用 pd.read_sql() 读取数据,因为我正在读取 DataFrame.to_sql() 相对较慢。
【问题讨论】:
-
我不太确定这是否相关,但 PostgreSQL 可以很好地使用 json:postgresql.org/docs/10/functions-json.html
-
是的,Postgres 肯定有很好的 JSON 函数。但正如@jpp 所说,数据很混乱。我希望能够轻松地对当前在 JSON 中的某些字段进行索引。有时 JSON 是一个数组。因此,我希望尽可能高效地对数百万行进行批量数据工程。
标签: python arrays json dictionary