sql >> Database >  >> NoSQL >> MongoDB

Bereken eerste-orde-derivaat met MongoDB-aggregatieraamwerk

db.collection.aggregate(
    [
      {
        "$addFields": {
          "indexes": {
            "$range": [
              0,
              {
                "$size": "$time_series"
              }
            ]
          },
          "reversedSeries": {
            "$reverseArray": "$time_series"
          }
        }
      },
      {
        "$project": {
          "derivatives": {
            "$reverseArray": {
              "$slice": [
                {
                  "$map": {
                    "input": {
                      "$zip": {
                        "inputs": [
                          "$reversedSeries",
                          "$indexes"
                        ]
                      }
                    },
                    "in": {
                      "$subtract": [
                        {
                          "$arrayElemAt": [
                            "$$this",
                            0
                          ]
                        },
                        {
                          "$arrayElemAt": [
                            "$reversedSeries",
                            {
                              "$add": [
                                {
                                  "$arrayElemAt": [
                                    "$$this",
                                    1
                                  ]
                                },
                                1
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  }
                },
                {
                  "$subtract": [
                    {
                      "$size": "$time_series"
                    },
                    1
                  ]
                }
              ]
            }
          },
          "time_series": 1
        }
      }
    ]
)

We kunnen hiervoor de bovenstaande pijplijn in versie 3.4+ gebruiken. In de pijplijn gebruiken we de $addFields pijplijn stadium. operator om de array van de "time_series"'s elements index to do document toe te voegen, we hebben ook de time series array omgedraaid en aan het document toegevoegd met respectievelijk de $range en $reverseArray operators

We hebben de array hier omgedraaid omdat het element op positie p in de array is altijd groter dan het element op positie p+1 wat betekent dat [p] - [p+1] <0 en we willen de $multiply niet gebruiken hier.(zie pijplijn voor versie 3.2)

Vervolgens hebben we $gezipt de tijdreeksgegevens met de indexenarray en toegepast een aftrekken expressie naar de resulterende array met behulp van de $map telefoniste.

We $slice het resultaat om de null/None . weg te gooien waarde uit de array en keerde het resultaat terug.

In 3.2 kunnen we de $unwind gebruiken operator om af te wikkelen onze array en neem de index van elk element in de array op door een document op te geven als operand in plaats van het traditionele "pad" voorafgegaan door $ .

Als volgende in de pijplijn moeten we $group onze documenten en gebruik de $push accumulator-operator om een ​​reeks subdocumenten te retourneren die er als volgt uitzien:

{
    "_id" : ObjectId("57c11ddbe860bd0b5df6bc64"),
    "time_series" : [
        { "value" : 10, "index" : NumberLong(0) },
        { "value" : 20, "index" : NumberLong(1) },
        { "value" : 40, "index" : NumberLong(2) },
        { "value" : 70, "index" : NumberLong(3) },
        { "value" : 110, "index" : NumberLong(4) }
    ]
}

Eindelijk komt de $project fase. In deze fase moeten we de $map operator om een ​​reeks uitdrukkingen toe te passen op elk element in de nieuw berekende array in de $group podium.

Dit is wat er gebeurt in de $map (zie $map als een for-lus) in uitdrukking:

Voor elk subdocument kennen we de waarde . toe veld naar een variabele met behulp van de $let variabele operator. Vervolgens trekken we de waarde af van de waarde van het veld "waarde" van het volgende element in de array.

Aangezien het volgende element in de array het element met de huidige index plus één is, hebben we alleen de hulp nodig van de $arrayElemAt operator en een simpele $add ie van de index van het huidige element en 1 .

De $subtract expressie retourneert een negatieve waarde, dus we moeten de waarde vermenigvuldigen met -1 met behulp van de $multiply telefoniste.

We moeten ook $filter de resulterende array omdat dit het laatste element is Geen of null . De reden is dat wanneer het huidige element het laatste element is, $aftrekken return Geen omdat de index van het volgende element gelijk is aan de grootte van de array.

db.collection.aggregate([
  {
    "$unwind": {
      "path": "$time_series",
      "includeArrayIndex": "index"
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "time_series": {
        "$push": {
          "value": "$time_series",
          "index": "$index"
        }
      }
    }
  },
  {
    "$project": {
      "time_series": {
        "$filter": {
          "input": {
            "$map": {
              "input": "$time_series",
              "as": "el",
              "in": {
                "$multiply": [
                  {
                    "$subtract": [
                      "$$el.value",
                      {
                        "$let": {
                          "vars": {
                            "nextElement": {
                              "$arrayElemAt": [
                                "$time_series",
                                {
                                  "$add": [
                                    "$$el.index",
                                    1
                                  ]
                                }
                              ]
                            }
                          },
                          "in": "$$nextElement.value"
                        }
                      }
                    ]
                  },
                  -1
                ]
              }
            }
          },
          "as": "item",
          "cond": {
            "$gte": [
              "$$item",
              0
            ]
          }
        }
      }
    }
  }
])

Een andere optie die volgens mij minder efficiënt is, is het uitvoeren van een kaart/verkleiningsbewerking op onze collectie met behulp van de map_reduce methode.

>>> import pymongo
>>> from bson.code import Code
>>> client = pymongo.MongoClient()
>>> db = client.test
>>> collection = db.collection
>>> mapper = Code("""
...               function() {
...                 var derivatives = [];
...                 for (var index=1; index<this.time_series.length; index++) {
...                   derivatives.push(this.time_series[index] - this.time_series[index-1]);
...                 }
...                 emit(this._id, derivatives);
...               }
...               """)
>>> reducer = Code("""
...                function(key, value) {}
...                """)
>>> for res in collection.map_reduce(mapper, reducer, out={'inline': 1})['results']:
...     print(res)  # or do something with the document.
... 
{'value': [10.0, 20.0, 30.0, 40.0], '_id': ObjectId('57c11ddbe860bd0b5df6bc64')}

U kunt ook het hele document ophalen en de numpy.diff . gebruiken om de afgeleide als volgt terug te geven:

import numpy as np


for document in collection.find({}, {'time_series': 1}):
    result = np.diff(document['time_series']) 


  1. Versnel MongoDB-aggregatie

  2. Hoe mongodb/mongoid-scripts te benchmarken, voor het vergelijken van twee verschillende querytechnieken

  3. Verbinding maken met MySQL zonder rootwachtwoord op Terminal

  4. MongoDB $exp