将嵌套的 mongoDB 文档转换为平面 pandas DataFrame(对象数组中的对象数组)
Posted
技术标签:
【中文标题】将嵌套的 mongoDB 文档转换为平面 pandas DataFrame(对象数组中的对象数组)【英文标题】:Convert nested mongoDB document into flat pandas DataFrame (Array of objects within array of objects) 【发布时间】:2019-09-30 07:06:43 【问题描述】:我正在尝试将 mongoDB 文档转换为平面 pandas 数据框结构。
我的 mongoDB 集合结构示例:
data = collection.find_one('ID':300)
print(data)
'_id': "ObjectId('5cd932299f6b7d4c9b95af6c')",
'ID': 300,
'updated': 23424,
'data': [
'meta': 8,
'data': [
'value1': 1, 'value2': 2,
'value1': 3, 'value2': 4
]
,
'meta': 9,
'data': [
'value1': 5, 'value2': 6
]
]
当我把它放入熊猫数据框时,我得到了
df = pd.DataFrame(data)
print(df)
| _id | ID | updated | data
|
|--------------------------|-----|---------|------------------------ ---------------------------|
| 5cd936779f6b7d4c9b95af6d | 300 | 23424 | 'meta': 8, 'data': ['value1': 1, 'value2': 2... |
| 5cd936779f6b7d4c9b95af6d | 300 | 23424 | 'meta': 9, 'data': ['value1': 5, 'value2': 6] |
当我使用 pd.concat 遍历数据框时,我得到了
df.rename(columns='data':'data1', inplace=True)
df2 = pd.concat([df, pd.DataFrame(list(df['data1']))], axis=1).drop('data1', 1)
df3 = pd.concat([df2, pd.DataFrame(list(df2['data']))], axis=1).drop('data', 1)
print(df3)
| _id | ID | updated | meta | 0 | 1 |
|--------------------------|-----|---------|------|----------------------------|----------------------------|
| 5cd936779f6b7d4c9b95af6d | 300 | 23424 | 8 | 'value1': 1, 'value2': 2 | 'value1': 3, 'value2': 4 |
| 5cd936779f6b7d4c9b95af6d | 300 | 23424 | 9 | 'value1': 5, 'value2': 6 | None |
最低层数组的最低层对象始终具有相同的名称。
所以我想要:
| ID | updated | meta | value1 | value2 |
|-----|---------|------|--------|--------|
| 300 | 23424 | 8 | 1 | 2 |
| 300 | 23424 | 8 | 3 | 4 |
| 300 | 23424 | 9 | 5 | 6 |
我是不是走错了路?
解决这个问题最方便的方法是什么?
【问题讨论】:
【参考方案1】:@sinB - 您可以通过删除 for 循环来进一步改进这一点(在处理包含许多文档的数据库时会导致问题)。无论如何您都不需要循环,因为可以使用单个命令将结果转换为 pandas 数据帧。
而不是这个:
#add each doc as a new row in dataframe
for doc in collection.aggregate(pipeline):
df = df.append(doc,ignore_index=True)
你可以用这个
query_result = collection.aggregate(pipeline)
query_result = list(query_result)
df = pd.io.json.json_normalize(query_result)
【讨论】:
【参考方案2】:我意识到 mongoDB 可以完成所有繁重的工作。
工作代码:
import pandas as pd
from pymongo import MongoClient
mongoClient = MongoClient('localhost', 27017)
db = mongoClient.DB_NAME
collection = db.COLLECTION_NAME
pipeline = [
'$match':'ID':300,
"$unwind":'path': '$data', 'preserveNullAndEmptyArrays': True,
"$unwind":'path': '$data.data', 'preserveNullAndEmptyArrays': True,
'$project':
'ID':'$ID',
'updated':"$updated",
'meta':"$data.meta",
'value1':"$data.data.value1",
'value2':"$data.data.value2"
]
#Make empty dataframe
df = pd.DataFrame()
#add each doc as a new row in dataframe
for doc in collection.aggregate(pipeline):
df = df.append(doc,ignore_index=True)
print(df)
输出:
| ID | updated | meta | value1 | value2 |
|-----|---------|------|--------|--------|
| 300 | 23424 | 8 | 1 | 2 |
| 300 | 23424 | 8 | 3 | 4 |
| 300 | 23424 | 9 | 5 | 6 |
【讨论】:
感谢@sinB .... df 也可以直接创建为 df = pd.DataFrame(collection.aggregate(pipeline))【参考方案3】:好吧,我设法以最可怕的方式解决了它。
def flatten(data):
a =
def make_dict(data):
for i in list(data):
if isinstance(data[i], list):
for j in data[i]:
make_dict(j)
else:
a.update(i:[])
return data
def add_to_dict(data):
for i in list(data):
if isinstance(data[i], list):
for j in data[i]:
add_to_dict(j)
else:
a[i].append(data[i])
max = 0
for i in a:
if len(a[i]) > max:
max = len(a[i])
for i in a:
if len(a[i]) < max:
a[i].append(a[i][-1])
make_dict(data)
add_to_dict(data)
return a
pd.DataFrame(flatten(data))
输出:
| ID | updated | meta | value1 | value2 |
|-----|---------|------|--------|--------|
| 300 | 23424 | 8 | 1 | 2 |
| 300 | 23424 | 8 | 3 | 4 |
| 300 | 23424 | 9 | 5 | 6 |
我无法想象这是一个好的解决方案,所以请随时帮助我找到更好的解决方案。
【讨论】:
以上是关于将嵌套的 mongoDB 文档转换为平面 pandas DataFrame(对象数组中的对象数组)的主要内容,如果未能解决你的问题,请参考以下文章
将查询构建器条件转换为 MongoDB 操作,包括嵌套的子文档数组