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.
Så $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'])