Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MongoDB和x27的移动平均线;s的聚合框架?_Mongodb_Aggregation Framework_Moving Average - Fatal编程技术网

MongoDB和x27的移动平均线;s的聚合框架?

MongoDB和x27的移动平均线;s的聚合框架?,mongodb,aggregation-framework,moving-average,Mongodb,Aggregation Framework,Moving Average,如果您有50年的温度天气数据(例如每天),您将如何计算该时间段内以3个月为间隔的移动平均值?您可以通过一个查询来实现这一点,还是必须进行多个查询 Example Data 01/01/2014 = 40 degrees 12/31/2013 = 38 degrees 12/30/2013 = 29 degrees 12/29/2013 = 31 degrees 12/28/2013 = 34 degrees 12/27/2013 = 36 degrees 12/26/2013 = 38 deg

如果您有50年的温度天气数据(例如每天),您将如何计算该时间段内以3个月为间隔的移动平均值?您可以通过一个查询来实现这一点,还是必须进行多个查询

Example Data

01/01/2014 = 40 degrees
12/31/2013 = 38 degrees
12/30/2013 = 29 degrees
12/29/2013 = 31 degrees
12/28/2013 = 34 degrees
12/27/2013 = 36 degrees
12/26/2013 = 38 degrees
.....

我不相信聚合框架可以在当前版本(2.6)中对多个日期执行此操作,或者,至少,如果没有一些严肃的练习,就无法执行此操作。原因是聚合管道一次只处理一个文档,因此有必要为每天创建一个文档,其中包含前3个月的相关信息。这将是一个计算平均值的
$group
阶段,这意味着前一阶段将产生大约90份每天记录的副本,带有一些可用于
$group
的区分键


因此,我不认为有一种方法可以在单个聚合中一次对多个日期执行此操作。如果有人找到了一种方法,我很高兴自己错了,并且不得不编辑/删除这个答案,即使它是如此复杂,不实用。一个PostgreSQL分区类型函数将在这里完成这项工作;也许有一天会增加这个功能。

我想我可能会对我自己的问题有一个答案。地图还原就可以了。首先使用emit将每个文档映射到它应该平均的邻居,然后使用reduce来平均每个数组。。。新的平均值数组应该是移动平均值,因为它的id是你关心的新的日期间隔

我想我需要更好地理解地图缩小

:)

例如。。。如果我们想在内存中执行此操作(稍后我们可以创建集合)

要点


这看起来对吗?

在MongoDB中,我倾向于这样做,即在文档中为每天的值保留过去90天的运行总和,例如

{"day": 1, "tempMax": 40, "tempMaxSum90": 2232}
{"day": 2, "tempMax": 38, "tempMaxSum90": 2230}
{"day": 3, "tempMax": 36, "tempMaxSum90": 2231}
{"day": 4, "tempMax": 37, "tempMaxSum90": 2233}
{sym: "A", d: ISODate("2018-01-01"), val: 10}
{sym: "A", d: ISODate("2018-01-02"), val: 30}
每当需要将新数据点添加到集合中时,不必读取和求和90个值,您可以通过两个简单的查询(一个加法和一个减法)高效地计算下一个和,如下所示(psuedo代码):

每天的90天移动平均值就是90天总和除以90

如果您还想提供不同时间尺度的移动平均值(例如,1周、30天、90天、1年),您可以简单地为每个文档维护一个总和数组,而不是单个总和,每个时间尺度需要一个总和


这种方法需要额外的存储空间和额外的处理来插入新数据,然而,在大多数时间序列图表场景中,新数据收集速度相对较慢,检索速度较快,这是合适的。

agg框架现在内置了
$map
$reduce
$range
,因此数组处理更加直接。下面是一个在一组数据上计算移动平均值的示例,您希望通过某个谓词进行过滤。基本设置是每个文档都包含可过滤标准和一个值,例如

{"day": 1, "tempMax": 40, "tempMaxSum90": 2232}
{"day": 2, "tempMax": 38, "tempMaxSum90": 2230}
{"day": 3, "tempMax": 36, "tempMaxSum90": 2231}
{"day": 4, "tempMax": 37, "tempMaxSum90": 2233}
{sym: "A", d: ISODate("2018-01-01"), val: 10}
{sym: "A", d: ISODate("2018-01-02"), val: 30}
这是:

// This controls the number of observations in the moving average:
days = 4;

c=db.foo.aggregate([

// Filter down to what you want.  This can be anything or nothing at all.
{$match: {"sym": "S1"}}

// Ensure dates are going earliest to latest:
,{$sort: {d:1}}

// Turn docs into a single doc with a big vector of observations, e.g.
//     {sym: "A", d: d1, val: 10}
//     {sym: "A", d: d2, val: 11}
//     {sym: "A", d: d3, val: 13}
// becomes
//     {_id: "A", prx: [ {v:10,d:d1}, {v:11,d:d2},  {v:13,d:d3} ] }
//
// This will set us up to take advantage of array processing functions!
,{$group: {_id: "$sym", prx: {$push: {v:"$val",d:"$date"}} }}

// Nice additional info.  Note use of dot notation on array to get
// just scalar date at elem 0, not the object {v:val,d:date}:
,{$addFields: {numDays: days, startDate: {$arrayElemAt: [ "$prx.d", 0 ]}} }

// The Juice!  Assume we have a variable "days" which is the desired number
// of days of moving average.
// The complex expression below does this in python pseudocode:
//
// for z in range(0, size of value vector - # of days in moving avg):
//    seg = vector[n:n+days]
//    values = seg.v
//    dates = seg.d
//    for v in seg:
//        tot += v
//    avg = tot/len(seg)
// 
// Note that it is possible to overrun the segment at the end of the "walk"
// along the vector, i.e. not enough date-values.  So we only run the
// vector to (len(vector) - (days-1).
// Also, for extra info, we also add the number of days *actually* used in the
// calculation AND the as-of date which is the tail date of the segment!
//
// Again we take advantage of dot notation to turn the vector of
// object {v:val, d:date} into two vectors of simple scalars [v1,v2,...]
// and [d1,d2,...] with $prx.v and $prx.d
//
,{$addFields: {"prx": {$map: {
    input: {$range:[0,{$subtract:[{$size:"$prx"}, (days-1)]}]} ,
    as: "z",
    in: {
       avg: {$avg: {$slice: [ "$prx.v", "$$z", days ] } },
       d: {$arrayElemAt: [ "$prx.d", {$add: ["$$z", (days-1)] } ]}
        }
        }}
    }}

            ]);
这可能会产生以下输出:

{
    "_id" : "S1",
    "prx" : [
        {
            "avg" : 11.738793632512115,
            "d" : ISODate("2018-09-05T16:10:30.259Z")
        },
        {
            "avg" : 12.420766702631376,
            "d" : ISODate("2018-09-06T16:10:30.259Z")
        },
        ...

    ],
    "numDays" : 4,
    "startDate" : ISODate("2018-09-02T16:10:30.259Z")
}

被接受的答案对我很有帮助,但我花了一段时间才理解它是如何工作的,所以我想我应该解释一下我帮助别人的方法。特别是在你的背景下,我认为我的回答会有所帮助

理想情况下,这适用于较小的数据集

首先按天对数据进行分组,然后将数组中的所有天追加到每天:

{
  "$sort": {
    "Date": -1
  }
},
{
  "$group": {
    "_id": {
      "Day": "$Date",
      "Temperature": "$Temperature"
    },
    "Previous Values": {
      "$push": {
        "Date": "$Date",
        "Temperature": "$Temperature"
      }
    }
  }
这将为您留下一条如下所示的记录(将正确订购):

既然每天都有附加的所有天数,我们需要从先前的值数组中删除比this _id.day字段最近的项目,因为移动平均值是向后看的:

{
  "$project": {
    "_id": 0,
    "Date": "$_id.Date",
    "Temperature": "$_id.Temperature",
    "Previous Values": 1
  }
},
{
  "$project": {
    "_id": 0,
    "Date": 1,
    "Temperature": 1,
    "Previous Values": {
      "$filter": {
        "input": "$Previous Values",
        "as": "pv",
        "cond": {
          "$lte": ["$$pv.Date", "$Date"]
        }
      }
    }
  }
},
“上一个值”数组中的每个项仅包含小于或等于每个记录日期的日期:

{"Day": "2017-02-01", 
"Temperature": 40, 
"Previous Values": [
    {"Day": "2017-01-31", "Temperature": 33},
    {"Day": "2017-01-30", "Temperature": 36},
    {"Day": "2017-01-29", "Temperature": 33},
    {"Day": "2017-01-28", "Temperature": 32},
    ...
    ]}
现在我们可以选择我们的平均窗口大小,因为数据是按天计算的,我们将在一周内获取数组的前7条记录;每月30人;或3个月,90天:

{
  "$project": {
    "_id": 0,
    "Date": 1,
    "Temperature": 1,
    "Previous Values": {
      "$slice": ["$Previous Values", 0, 90]
    }
  }
},
为了平均先前的温度,我们展开先前的值数组,然后按日期字段分组。展开操作执行以下操作:

{"Day": "2017-02-01", 
"Temperature": 40, 
"Previous Values": {
        "Day": "2017-01-31", 
        "Temperature": 33}
},

{"Day": "2017-02-01", 
"Temperature": 40, 
"Previous Values": {
        "Day": "2017-01-30", 
        "Temperature": 36}
},

{"Day": "2017-02-01", 
"Temperature": 40, 
"Previous Values": {
        "Day": "2017-01-29", 
        "Temperature": 33}
},
...
请注意,Day字段是相同的,但是我们现在有了一个来自previous Values数组的每个先前日期的文档现在我们可以重新分组,然后对之前的值进行平均。温度以获得移动平均值:

{"$group": {
    "_id": {
      "Day": "$Date",
      "Temperature": "$Temperature"
    },
    "3 Month Moving Average": {
      "$avg": "$Previous Values.Temperature"
    }
  }
}

就这样!我知道将每条记录连接到每条记录并不理想,但这在较小的数据集上效果很好

您的确切意思是什么?是否希望某些值重叠?如果是的话,是哪些?白天?或者只是一个滚动平均值。聚合框架实际上无法将一个文档与另一个文档进行比较,因此这听起来更像mapReduce。@neil lunn我想计算一个滚动平均值。。所以对于3个月的时间间隔,我想取一天,用过去3个月的数据平均这一天,然后在接下来的50年中,对50年中的每一天进行平均。因此,我认为某些值会与平均值重叠。如果使用mapReduce,您将如何做到这一点?如果使用聚合框架,您将无法做到这一点。我想你是对的,我必须比较不同的文件。谢谢@neil lunn,看起来聚合框架无法做到这一点,你是对的。。让我知道你是否知道如何使用mongodb的MapReduce来实现这一点,所以我想创建这个移动或滚动平均数据数组-这里有更多关于移动平均的信息,所以你要么查询这些数据片段,用某种语言(ruby、python、node)计算移动平均数,要么为每个间隔运行聚合查询。。。这真的是最好的解决方案吗?这不是感觉不对吗?有没有更好的方法可以用map reduce来实现这一点,我想不出来?我还没有想过map reduce。一般来说,我尽量避免使用