我正在尝试检测集合中值的“趋势”。
假设我有以下内容:
{ created_at: 2014-12-01, value:1015 }
{ created_at: 2014-12-01, value:1015 }
{ created_at: 2014-12-01, value:1019 }
{ created_at: 2014-12-02, value:1018 }
{ created_at: 2014-12-02, value:1021 }
{ created_at: 2014-12-03, value:1010 }
{ created_at: 2014-12-03, value:1012 }
{ created_at: 2014-12-03, value:1011 }
{ created_at: 2014-12-04, value:1012 }
我只想要输出如下:
{ created_at: 2014-12-01, average: 1016, diff: 0}
{ created_at: 2014-12-02, average: 1019, diff: 3}
diff是两者之间平均值的差值 这两个日期。
我想出了如何计算平均值,找出集合中的最小/最大值和第一个/最后一个值,但找不到比较两个平均值的方法......
答案 0 :(得分:1)
根据您对"每分钟聚合"的评论,假设这些是实际日期。在一次传递中唯一真正的方法是使用mapReduce。这里的关键是mapReduce可以存储一个全局变量,因此" track"你的最后一个结果,以确定"差异"在每个汇总记录之间
db.collection.mapReduce(
function() {
// Round date to the minute
var key = this.created_at.valueOf()
- ( this.created_at.valueOf() % ( 1000 * 60 ) );
emit( key, { "average": this.value } );
},
function(key,values) {
values = values.map(function(i) { return i.average });
var result = {
"average": Math.floor(Array.avg(values))
};
return result;
},
{
"out": { "inline": 1 },
"scope": { "lastAvg": 0 },
"finalize": function(key,value) {
value.diff = ( lastAvg == 0 ) ? 0 : value.average - lastAvg;
lastAvg = value.average;
return value;
}
}
)
或者你可以"后处理"正如已经提到的那样,在客户端代码中执行相同的操作来计算差异,因为您使用类似的范围变量迭代游标。作为shell示例:
var lastAvg = 0;
db.collection.aggregate([
{ "$group": {
"_id": { "$subtract": [
{ "$subtract": [ "$created_date", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$created_date", new Date(0) ] },
1000 * 60
]}
]},
"average": { "$avg": "$value" }
}},
{ "$sort": { "_id": 1 } }
]).forEach(function(doc) {
doc.average = Math.floor(doc.average);
doc.diff = ( lastAvg == 0 ) ? 0 : doc.average - lastAvg;
lastAvg = doc.average;
printjson(doc);
})
在这两种情况下,我都使用日期数学原理,以便将日期对象转换为unix / epoch时间戳表示形式,作为一个数字,该数字逐个舍入到它最近的分钟。使用聚合框架,您可以使用date aggregation operators提取日期部分进行分组。
在任何一种情况下,将Date
对象重新转换为内部.mapReduce()
内部或使用.aggregate()
进行后期处理非常简单。
所以在结束时,您可以使用"全局范围" mapReduce的功能,或者您可以只从聚合处理结果游标,以计算结果中每个分组之间的差异。
答案 1 :(得分:0)
粗略轮廓:我会计算十分钟的平均值:
> var avgCursor = db.sensor_readings.aggregate([
{ "$match" : { "created_at" : { "$gt" : ten_minutes_ago, "$lte" : now } } }
{ "$group" : { "_id" : 0, "average" : { "$avg" : "$value" } } }
]}
> var avgDoc = avgCursor.toArray()[0]
> avgDoc
{ "_id" : 0, "average" : 23 }
然后我将它存储在另一个集合中:
> db.sensor_averages.insert({ "start" : ten_minutes_ago, "end" : now, "average" : avgDoc.average })
最后,回想一下计算差异所需的两个平均值,然后计算它:
> var diffCursor = db.sensor_averages.find({ "start" : { "$gte" : twenty_minutes_ago } }).sort({ "start" : -1 })
> var diffArray = diffCursor.toArray()
> var difference = diffArray[0].average - diffArray[1].average
您还可以跳过定期聚合,而是在sensor_averages
中更新运行平均值,每10分钟跳转到一个新文档。在每10分钟开始时,插入sensor_averages
文档
{
"start" : now,
"svalues" : 0,
"nvalues" : 0
}
然后在接下来十分钟的sensor_reading
文档的每次插入中,还更新sensor_averages
doc:
db.sensor_averages.update(
{ "start" : now_rounded_to_the_ten_minute_boundary },
{ "$inc" : { "svalues" : value, "nvalues" : 1 } }
)
然后,当您想要平均值之间的差异时,请调用相应的两个文档,将svalues
除以nvalues
以获得平均值,然后减去。