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