sql >> Database teknologi >  >> NoSQL >> MongoDB

Beregn førsteordens afledte med MongoDB-aggregationsramme

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
        }
      }
    ]
)

Vi kan bruge ovenstående pipeline i version 3.4+ til at gøre dette. I pipelinen bruger vi $addFields pipeline fase. operatør for at tilføje arrayet af "time_series"'s elements index to do-dokument, vendte vi også tidsserie-arrayet og tilføjede det til dokumentet ved hjælp af henholdsvis $range og $reverseArray operatører

Vi vendte arrayet her, fordi elementet i position p i arrayet er altid større end elementet ved position p+1 hvilket betyder at [p] - [p+1] <0 og vi ønsker ikke at bruge $multiply her.(se pipeline for version 3.2)

Dernæst $zippede vi tidsseriedataene med indeks-arrayet og anvendte en subtrahering udtryk til det resulterende array ved hjælp af $map operatør.

$slice resultatet for at kassere null/None værdi fra arrayet og reverserede resultatet.

I 3.2 kan vi bruge $unwind operatør for at slappe af vores array og inkludere indekset for hvert element i arrayet ved at angive et dokument som operand i stedet for den traditionelle "sti" med præfikset $ .

Næste i pipelinen skal vi $group vores dokumenter og brug $push akkumulatoroperatør for at returnere en række underdokumenter, der ser sådan ud:

{
    "_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) }
    ]
}

Endelig kommer $projekt scene. I denne fase skal vi bruge $map operator til at anvende en række udtryk til hvert element i det nyligt beregnede array i $group scene.

Her er, hvad der foregår inde i $map (se $map som en for loop) in udtryk:

For hvert underdokument tildeler vi værdien feltet til en variabel ved hjælp af $let variabel operatør. Vi trækker dens værdi fra værdien af ​​"værdi"-feltet i det næste element i arrayet.

Da det næste element i arrayet er elementet ved det aktuelle indeks plus en, er alt, hvad vi behøver, hjælpen fra $arrayElemAt operatør og en simpel $add tion af det aktuelle elements indeks og 1 .

$subtract udtryk returnerer en negativ værdi, så vi skal gange værdien med -1 ved hjælp af $multiply operatør.

Vi skal også $filter det resulterende array, fordi det sidste element er Ingen eller null . Årsagen er, at når det aktuelle element er det sidste element, $subtract returner Ingen fordi indekset for det næste element er lig med størrelsen af ​​arrayet.

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
            ]
          }
        }
      }
    }
  }
])

En anden mulighed, som jeg synes er mindre effektiv, er at udføre en kort-/reducer-operation på vores samling ved hjælp af map_reduce metode.

>>> 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')}

Du kan også hente hele dokumentet og bruge numpy.diff for at returnere den afledede sådan:

import numpy as np


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


  1. MeteorJS - Linker billeder (FS.collection) til deres relevante dokument i MongoDB Collection

  2. Python og MongoDB:Opretter forbindelse til NoSQL-databaser

  3. Indsæt et nyt objekt i et underdokumentarrayfelt i mongoose

  4. Mest effektive måde at gemme indlejrede kategorier (eller hierarkiske data) i Mongo?