我正在尝试将mongoDB文档转换为扁平的熊猫数据框结构。
我的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 |
我走错了路吗?
解决此问题的最便捷方法是什么?
答案 0 :(得分: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)
答案 1 :(得分:0)
我设法以最丑陋的方式解决了它。
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 |
我无法想象这是一个好的解决方案,所以请随时为我提供更好的解决方案。
答案 2 :(得分:0)
我意识到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 |