Python 将嵌套MongoDB导入Pandas

Python 将嵌套MongoDB导入Pandas,python,json,mongodb,pandas,Python,Json,Mongodb,Pandas,我在MongoDB中有一个包含大量嵌套文档的集合,我希望展开并导入到Pandas。有一些嵌套的dict,但也有一个dict列表,我希望将其转换为列(有关详细信息,请参见下面的示例) 我已经有了一个函数,它可以处理小批量的文档。但解决方案(我发现它)使用json。json.loads操作的问题是,它在集合中较大的选择上出现MemoryError时失败 我尝试了许多建议使用其他json解析器的解决方案(例如ijson),但由于不同的原因,没有一个解决了我的问题。如果我想通过json继续转换,剩下的唯

我在MongoDB中有一个包含大量嵌套文档的集合,我希望展开并导入到Pandas。有一些嵌套的dict,但也有一个dict列表,我希望将其转换为列(有关详细信息,请参见下面的示例)

我已经有了一个函数,它可以处理小批量的文档。但解决方案(我发现它)使用json。
json.loads
操作的问题是,它在集合中较大的选择上出现
MemoryError
时失败

我尝试了许多建议使用其他json解析器的解决方案(例如ijson),但由于不同的原因,没有一个解决了我的问题。如果我想通过json继续转换,剩下的唯一方法就是将较大的选择分块到较小的文档组中,并迭代解析

在这一点上,我想,-这是我这里的主要问题-也许有一种更聪明的方法来做不必要的事情,而不必在MongoDB或Pandas中直接绕道json,或者以某种方式组合

这是一个简短的示例文档:

{
  '_id': ObjectId('5b40fcc4affb061b8871cbc5'),
  'eventId': 2,
  'sId' : 6833,
  'stage': {
    'value': 1,
    'Name': 'FirstStage'
  },
  'quality': [
    {
      'type': {
        'value': 2,
        'Name': 'Color'
      },
      'value': '124'
    },
    {
      'type': {
        'value': 7,
        'Name': 'Length'
      },
      'value': 'Short'
    },
    {
      'type': {
        'value': 15,
        'Name': 'Printed'
      }
    }
}
这就是成功的数据帧表示的样子(为了可读性,我跳过了列“\u id”和“sId”:

    eventId    stage.value    stage.name    q_color    q_length    q_printed
1   2          1              'FirstStage'  124        'Short'     1 
到目前为止我的代码(遇到内存问题-请参见上文):


您不需要使用json解析器转换嵌套结构。只需从记录列表中创建数据帧:

df = DataFrame(list(cursor))
然后使用pandas打开列表和字典:

import pandas
from itertools import chain
import numpy

df = pandas.DataFrame(t)
df['stage.value'] = df['stage'].apply(lambda cell: cell['value'])
df['stage.name'] = df['stage'].apply(lambda cell: cell['Name'])
df['q_']= df['quality'].apply(lambda cell: [(m['type']['Name'], m['value'] if 'value' in m.keys() else 1) for m in cell])
df['q_'] = df['q_'].apply(lambda cell: dict((k, v) for k, v in cell))
keys = set(chain(*df['q_'].apply(lambda column: column.keys())))
for key in keys:
    column_name = 'q_{}'.format(key).lower()
    df[column_name] = df['q_'].apply(lambda cell: cell[key] if key in cell.keys() else numpy.NaN) 
df.drop(['stage', 'quality', 'q_'], axis=1, inplace=True)

我使用三个步骤来解包嵌套的数据类型。首先,使用名称和值创建一个成对(元组)的平面列表。在第二步中,基于元组的字典从元组的第一个位置获取键,并从元组的第二个位置获取值。然后使用集合提取所有现有属性名。每个属性使用循环获取一个新列。在循环中,每对属性的值都映射到相应的列单元格。

谢谢,这或多或少是什么意思我一直在寻找。还有一个问题仍然存在,在上面显示的数据中看不到。有很多可能的条目不符合质量要求(大约100条)。文档总是只有0-10条可供选择的条目。如果q_不在文档中,则相关列的相关字段中应该有一个NaN。因此1)我想知道,您的解决方案是否会抛出一个关键错误?2) 我可以对每一个问题进行硬编码,这似乎需要大量的工作,而且不是很优雅。PS:由于这似乎是一个全新的角度,我将打开一个后续问题,并将您的答案标记为已接受。非常感谢。我更新了我的答案。现在它应该更适合你的需要。也许使用Pymongo查询来过滤所有密钥会更快。哦,是的,它很有效,非常感谢!我还尝试了Pymongo查询,它比您的“链魔法”慢得多
import pandas
from itertools import chain
import numpy

df = pandas.DataFrame(t)
df['stage.value'] = df['stage'].apply(lambda cell: cell['value'])
df['stage.name'] = df['stage'].apply(lambda cell: cell['Name'])
df['q_']= df['quality'].apply(lambda cell: [(m['type']['Name'], m['value'] if 'value' in m.keys() else 1) for m in cell])
df['q_'] = df['q_'].apply(lambda cell: dict((k, v) for k, v in cell))
keys = set(chain(*df['q_'].apply(lambda column: column.keys())))
for key in keys:
    column_name = 'q_{}'.format(key).lower()
    df[column_name] = df['q_'].apply(lambda cell: cell[key] if key in cell.keys() else numpy.NaN) 
df.drop(['stage', 'quality', 'q_'], axis=1, inplace=True)