Mongodb агрегации сводит встроенный список - PullRequest
0 голосов
/ 20 октября 2019

У меня есть структура документа со встроенным списком, и после того, как я использовал конвейер агрегации, я получаю следующее:

Примечание: я использую Python с pymongo для этого запроса.

pipeline = [
    {'$unwind': '$saved_alloys'},
    {
        '$project': {
            '_id': 0,
            'name': '$saved_alloys.name',
            'compositions': '$saved_alloys.compositions'
        }
    }
]

res = db['alloys'].aggregate(pipeline)

for e in res:
  print(e)

Вывод (усеченный):

{
  'name': 'alloy-1', 
  'compositions': [
    {'symbol': 'C', 'weight': 0.36},
    {'symbol': 'Mn', 'weight': 1.41}
    {'symbol': 'Si', 'weight': 1.03},
    {'symbol': 'Ni', 'weight': 1.7}
  ]
}
{
  'name': 'alloy-2',
  'compositions': [
    {'symbol': 'C', 'weight': 0.21}, 
    {'symbol': 'Mn', 'weight': 0.23}, 
    {'symbol': 'Si', 'weight': 0.86},
    {'symbol': 'Ni', 'weight': 0.67},
    {'symbol': 'Cr', 'weight': 0.12},
  ]
}
...

Я пытаюсь провести некоторый анализ данных, уменьшив размеры композиций до декартовой плоскости. Таким образом, я хотел бы поместить это в DataFrame для панд, просто выполнив pd.DataFrame(list(res)).

. Я хотел бы получить таблицу со следующей структурой:

{
  "name": "alloy-1",
  "C": 0.36,
  "Mn": 1.41,
  "Si": 1.03,
  "Ni": 1.7,
  "Cr": 0.0
},
{
  "name": "alloy-2",
  "C": 0.21,
  "Mn": 0.23,
  "Si": 0.86,
  "Ni": 0.67,
  "Cr": 0.12
}

Обратите внимание, чтосписок compositions может иметь переменный размер, поэтому для тех элементов, которых нет в списке, я бы хотел добавить их, но иметь значение weights равное нулю (как в alloy-1 для Cr).

Заранее спасибо за любую помощь.

1 Ответ

0 голосов
/ 20 октября 2019

Без сомнения, это можно оптимизировать, но в качестве простого подхода для начала создайте ряд панд для каждого возвращаемого результата и добавьте его в DataFrame;наконец, замените все «пропущенные» значения на 0.0.

from pymongo import MongoClient
import pandas as pd
import numpy as np

db = MongoClient()["mydatabase"]

db.alloys.insert_one({
    'saved_alloys': [{
        'name': 'alloy-1',
        'compositions': [
            {'symbol': 'C', 'weight': 0.36},
            {'symbol': 'Mn', 'weight': 1.41},
            {'symbol': 'Si', 'weight': 1.03},
            {'symbol': 'Ni', 'weight': 1.7}
        ]
    },
        {
            'name': 'alloy-2',
            'compositions': [
                {'symbol': 'C', 'weight': 0.21},
                {'symbol': 'Mn', 'weight': 0.23},
                {'symbol': 'Si', 'weight': 0.86},
                {'symbol': 'Ni', 'weight': 0.67},
                {'symbol': 'Cr', 'weight': 0.12},
            ]
        }]
}
)

pipeline = [
    {'$unwind': '$saved_alloys'},
    {
        '$project': {
            '_id': 0,
            'name': '$saved_alloys.name',
            'compositions': '$saved_alloys.compositions'
        }
    }
]

res = db['alloys'].aggregate(pipeline)
df = pd.DataFrame()

for alloy in res:
    ser = pd.Series()
    # Set the series name as the alloy
    ser.name = alloy['name']

    for composition in alloy['compositions']:
        # Add in each alloy to the series
        ser.at[composition['symbol']] = composition['weight']

    df = df.append(ser)

# Once we have our DataFrame, replace any missing values with 0.0
df = df.replace(np.nan, 0.0)
print(df)

Результат:

            C    Mn    Ni    Si    Cr
alloy-1  0.36  1.41  1.70  1.03  0.00
alloy-2  0.21  0.23  0.67  0.86  0.12
...