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
}
}
]
)
上記のバージョン3.4以降のパイプラインを使用してこれを行うことができます。パイプラインでは、 $ addFields
パイプラインステージ。演算子を使用して「time_series」の要素インデックスの配列を追加してドキュメントを作成します。また、時系列配列を逆にして、それぞれ $ range
および $ reverseArray
演算子
p
の位置にある要素のため、ここで配列を逆にしました 配列内のは、常に位置 p + 1
の要素よりも大きくなります つまり、 [p]-[p + 1] <0
また、 $ multiply
は使用しません。
ここに(バージョン3.2のパイプラインを参照)
次に、 $ zipped
インデックス配列を使用し、減算を適用した時系列データコード>
$ map
を使用した結果の配列への式 オペレーター。
次に、 $ sense
null / None
を破棄する結果 配列からの値と結果を元に戻しました。
3.2では、 $ unwind
を使用できます。
巻き戻す演算子 $ で始まる従来の「パス」の代わりに、オペランドとしてドキュメントを指定することにより、配列に各要素のインデックスを含めます。 。
次のパイプラインでは、 $ group
>
ドキュメントを使用して、 $ push
を使用します
次のようなサブドキュメントの配列を返すアキュムレータ演算子:
{
"_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) }
]
}
最後に、 $ project
が登場します。
ステージ。この段階では、 $mapを使用する必要があります。コード>
$ group
で新しく計算された配列の各要素に一連の式を適用する演算子 ステージ。
これが$map
内で起こっていることです ( $ map
を参照してください forループとして) in 式:
サブドキュメントごとに、値を割り当てます。 $ let
を使用して変数にフィールドを設定します
変数演算子。次に、配列内の次の要素の「value」フィールドの値からその値を減算します。
配列の次の要素は現在のインデックスに1を加えた要素であるため、必要なのは $ arrayElemAt
演算子と単純な $ add
現在の要素のインデックスと1
の位置 。
$ extract
式は負の値を返すため、値に -1
を掛ける必要があります $ multiply
を使用する
オペレーター。
また、 $ filter
も必要です。
最後の要素がNone
であるため、結果の配列 またはnull
。その理由は、現在の要素が最後の要素である場合、 $ extract
None
を返します 次の要素のインデックスが配列のサイズに等しいためです。
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
]
}
}
}
}
}
])
効率が悪いと思うもう1つのオプションは、 map_reduce
メソッド。
>>> 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')}
すべてのドキュメントを取得して、 numpy.diff
を使用することもできます このように導関数を返すには:
import numpy as np
for document in collection.find({}, {'time_series': 1}):
result = np.diff(document['time_series'])