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
}
}
]
)
Chúng ta có thể sử dụng đường dẫn ở trên trong phiên bản 3.4+ để thực hiện việc này. Trong đường dẫn này, chúng ta sử dụng $ addFields
giai đoạn đường ống. toán tử để thêm mảng chỉ số phần tử của "time_series" để làm tài liệu, chúng tôi cũng đảo ngược mảng chuỗi thời gian và thêm nó vào tài liệu bằng cách sử dụng $range
và $ reverseArray
toán tử
Chúng tôi đã đảo ngược mảng ở đây vì phần tử ở vị trí p
trong mảng luôn lớn hơn phần tử ở vị trí p + 1
có nghĩa là [p] - [p + 1] <0
và chúng tôi không muốn sử dụng $ nhân
tại đây. (xem đường dẫn cho phiên bản 3.2)
Tiếp theo, chúng ta $ zipped
dữ liệu chuỗi thời gian với mảng chỉ mục và áp dụng subract
biểu thức cho mảng được kết quả bằng cách sử dụng $ map
nhà điều hành.
Sau đó, chúng tôi $ slice
kết quả để loại bỏ null / None
giá trị từ mảng và đảo ngược lại kết quả.
Trong 3.2, chúng ta có thể sử dụng $ unwind
toán tử để thư giãn mảng của chúng tôi và bao gồm chỉ mục của từng phần tử trong mảng bằng cách chỉ định tài liệu làm toán hạng thay vì "đường dẫn" truyền thống có tiền tố là $ .
Tiếp theo trong quy trình, chúng ta cần $ group
tài liệu của chúng tôi và sử dụng $ push
toán tử tích lũy để trả về một mảng tài liệu con giống như sau:
{
"_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) }
]
}
Cuối cùng là $ project
sân khấu. Trong giai đoạn này, chúng ta cần sử dụng $ map
toán tử để áp dụng một chuỗi biểu thức cho từng phần tử trong mảng mới được tính trong $ group
sân khấu.
Đây là những gì đang diễn ra bên trong $ map
(xem $ map
như một vòng lặp for) in biểu thức:
Đối với mỗi tài liệu con, chúng tôi chỉ định giá trị vào một biến bằng cách sử dụng $ let
toán tử biến. Sau đó, chúng tôi trừ giá trị của nó khỏi giá trị của trường "giá trị" của phần tử tiếp theo trong mảng.
Vì phần tử tiếp theo trong mảng là phần tử ở chỉ mục hiện tại cộng với một phần tử, tất cả những gì chúng ta cần là sự trợ giúp của $ arrayElemAt
toán tử và một $ add
đơn giản
ý chỉ chỉ mục của phần tử hiện tại và 1
.
$ subtract
biểu thức trả về giá trị âm vì vậy chúng ta cần nhân giá trị với -1
bằng cách sử dụng $ nhân
nhà điều hành.
Chúng tôi cũng cần $ filter
mảng được kết quả vì nó là phần tử cuối cùng Không có
hoặc null
. Lý do là khi phần tử hiện tại là phần tử cuối cùng, $ subtract
return Không
vì chỉ số của phần tử tiếp theo bằng kích thước của mảng.
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
]
}
}
}
}
}
])
Một tùy chọn khác mà tôi nghĩ là kém hiệu quả hơn là thực hiện thao tác bản đồ / thu nhỏ trên bộ sưu tập của chúng tôi bằng cách sử dụng map_reduce
phương pháp.
>>> 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')}
Bạn cũng có thể truy xuất tất cả tài liệu và sử dụng numpy.diff
để trả về dẫn xuất như sau:
import numpy as np
for document in collection.find({}, {'time_series': 1}):
result = np.diff(document['time_series'])