使用 pandas.json_normalize 到 "unfold" 字典列表的字典
Using pandas.json_normalize to "unfold" a dictionary of a list of dictionaries
我是 Python 的新手(以及一般的编码),所以我会尽力解释我正在努力应对的挑战。
我正在处理一个从数据库导出为 CSV 文件的大型数据集。但是,此 CSV 导出中有一列包含字典的嵌套列表(据我所知)。我在网上广泛寻找解决方案,包括在 Whosebug 上,但还没有完全找到完整的解决方案。我想我从概念上理解了我想要完成的事情,但不清楚要使用的最佳方法或数据准备过程。
这是一个数据示例(缩减为我感兴趣的两列):
{
"app_ID": {
"0": 1abe23574,
"1": 4gbn21096
},
"locations": {
"0": "[ {"loc_id" : "abc1", "lat" : "12.3456", "long" : "101.9876"
},
{"loc_id" : "abc2", "lat" : "45.7890", "long" : "102.6543"}
]",
"1": "[ ]",
]"
}
}
基本上每个 app_ID 可以有多个位置绑定到一个 ID,或者它可以是空的,如上所示。我尝试使用我在网上找到的一些指南,这些指南使用 Panda 的 json_normalize() 函数来“展开”或将字典列表放入 Panda 数据框中它们自己的行中。
我想以这样的方式结束:
loc_id lat long app_ID
abc1 12.3456 101.9876 1abe23574
abc1 45.7890 102.6543 1abe23574
等...
我正在学习如何使用 json_normalize 的不同功能,例如“record_path”和“meta”,但还无法使用它。
我尝试使用以下方法将 json 文件加载到 Jupyter Notebook 中:
with open('location_json.json', 'r') as f:
data = json.loads(f.read())
df = pd.json_normalize(data, record_path = ['locations'])
但它只创建一个 1 行和多列长的数据框,我希望从最内层的字典生成多行,这些行与 app_ID 和 loc_ID 字段。
尝试解决方案:
我能够接近我想要使用的数据帧格式:
with open('location_json.json', 'r') as f:
data = json.loads(f.read())
df = pd.json_normalize(data['locations']['0'])
但这将需要通过列表进行某种迭代以创建数据框,然后我将失去与 app_ID 字段的连接。 (尽我所能理解 json_normalize 函数的工作原理)。
我尝试使用 json_normalize 是否正确,还是应该重新开始并尝试不同的路线?任何建议或指导将不胜感激。
我不能说建议您使用 convtools
库是一件好事,因为您是初学者,因为这个库几乎就像另一个 Python 而不是 Python。它有助于动态定义数据转换(在幕后生成 Python 代码)。
但是无论如何,如果我理解输入数据的话,下面是代码:
import json
from convtools import conversion as c
data = {
"app_ID": {"0": "1abe23574", "1": "4gbn21096"},
"locations": {
"0": """[ {"loc_id" : "abc1", "lat" : "12.3456", "long" : "101.9876" },
{"loc_id" : "abc2", "lat" : "45.7890", "long" : "102.6543"} ]""",
"1": "[ ]",
},
}
# define it once and use multiple times
converter = (
c.join(
# converts "app_ID" data to iterable of dicts
(
c.item("app_ID")
.call_method("items")
.iter({"id": c.item(0), "app_id": c.item(1)})
),
# converts "locations" data to iterable of dicts,
# where each id like "0" is zipped to each location.
# the result is iterable of dicts like {"id": "0", "loc": {"loc_id": ... }}
(
c.item("locations")
.call_method("items")
.iter(
c.zip(id=c.repeat(c.item(0)), loc=c.item(1).pipe(json.loads))
)
.flatten()
),
# join on "id"
c.LEFT.item("id") == c.RIGHT.item("id"),
how="full",
)
# process results, where 0 index is LEFT item, 1 index is the RIGHT one
.iter(
{
"loc_id": c.item(1, "loc", "loc_id", default=None),
"lat": c.item(1, "loc", "lat", default=None),
"long": c.item(1, "loc", "long", default=None),
"app_id": c.item(0, "app_id"),
}
)
.as_type(list)
.gen_converter()
)
result = converter(data)
assert result == [
{'loc_id': 'abc1', 'lat': '12.3456', 'long': '101.9876', 'app_id': '1abe23574'},
{'loc_id': 'abc2', 'lat': '45.7890', 'long': '102.6543', 'app_id': '1abe23574'},
{'loc_id': None, 'lat': None, 'long': None, 'app_id': '4gbn21096'}
]
我是 Python 的新手(以及一般的编码),所以我会尽力解释我正在努力应对的挑战。
我正在处理一个从数据库导出为 CSV 文件的大型数据集。但是,此 CSV 导出中有一列包含字典的嵌套列表(据我所知)。我在网上广泛寻找解决方案,包括在 Whosebug 上,但还没有完全找到完整的解决方案。我想我从概念上理解了我想要完成的事情,但不清楚要使用的最佳方法或数据准备过程。
这是一个数据示例(缩减为我感兴趣的两列):
{
"app_ID": {
"0": 1abe23574,
"1": 4gbn21096
},
"locations": {
"0": "[ {"loc_id" : "abc1", "lat" : "12.3456", "long" : "101.9876"
},
{"loc_id" : "abc2", "lat" : "45.7890", "long" : "102.6543"}
]",
"1": "[ ]",
]"
}
}
基本上每个 app_ID 可以有多个位置绑定到一个 ID,或者它可以是空的,如上所示。我尝试使用我在网上找到的一些指南,这些指南使用 Panda 的 json_normalize() 函数来“展开”或将字典列表放入 Panda 数据框中它们自己的行中。
我想以这样的方式结束:
loc_id lat long app_ID
abc1 12.3456 101.9876 1abe23574
abc1 45.7890 102.6543 1abe23574
等...
我正在学习如何使用 json_normalize 的不同功能,例如“record_path”和“meta”,但还无法使用它。
我尝试使用以下方法将 json 文件加载到 Jupyter Notebook 中:
with open('location_json.json', 'r') as f:
data = json.loads(f.read())
df = pd.json_normalize(data, record_path = ['locations'])
但它只创建一个 1 行和多列长的数据框,我希望从最内层的字典生成多行,这些行与 app_ID 和 loc_ID 字段。
尝试解决方案:
我能够接近我想要使用的数据帧格式:
with open('location_json.json', 'r') as f:
data = json.loads(f.read())
df = pd.json_normalize(data['locations']['0'])
但这将需要通过列表进行某种迭代以创建数据框,然后我将失去与 app_ID 字段的连接。 (尽我所能理解 json_normalize 函数的工作原理)。
我尝试使用 json_normalize 是否正确,还是应该重新开始并尝试不同的路线?任何建议或指导将不胜感激。
我不能说建议您使用 convtools
库是一件好事,因为您是初学者,因为这个库几乎就像另一个 Python 而不是 Python。它有助于动态定义数据转换(在幕后生成 Python 代码)。
但是无论如何,如果我理解输入数据的话,下面是代码:
import json
from convtools import conversion as c
data = {
"app_ID": {"0": "1abe23574", "1": "4gbn21096"},
"locations": {
"0": """[ {"loc_id" : "abc1", "lat" : "12.3456", "long" : "101.9876" },
{"loc_id" : "abc2", "lat" : "45.7890", "long" : "102.6543"} ]""",
"1": "[ ]",
},
}
# define it once and use multiple times
converter = (
c.join(
# converts "app_ID" data to iterable of dicts
(
c.item("app_ID")
.call_method("items")
.iter({"id": c.item(0), "app_id": c.item(1)})
),
# converts "locations" data to iterable of dicts,
# where each id like "0" is zipped to each location.
# the result is iterable of dicts like {"id": "0", "loc": {"loc_id": ... }}
(
c.item("locations")
.call_method("items")
.iter(
c.zip(id=c.repeat(c.item(0)), loc=c.item(1).pipe(json.loads))
)
.flatten()
),
# join on "id"
c.LEFT.item("id") == c.RIGHT.item("id"),
how="full",
)
# process results, where 0 index is LEFT item, 1 index is the RIGHT one
.iter(
{
"loc_id": c.item(1, "loc", "loc_id", default=None),
"lat": c.item(1, "loc", "lat", default=None),
"long": c.item(1, "loc", "long", default=None),
"app_id": c.item(0, "app_id"),
}
)
.as_type(list)
.gen_converter()
)
result = converter(data)
assert result == [
{'loc_id': 'abc1', 'lat': '12.3456', 'long': '101.9876', 'app_id': '1abe23574'},
{'loc_id': 'abc2', 'lat': '45.7890', 'long': '102.6543', 'app_id': '1abe23574'},
{'loc_id': None, 'lat': None, 'long': None, 'app_id': '4gbn21096'}
]