【问题标题】:Multiple Fields Where Keys In Document Vary Average Aggregation文档中的键不同的多个字段平均聚合
【发布时间】:2017-11-26 19:00:57
【问题描述】:

我得到的数据集如下:

{
    "_id" : ObjectId("592d4f43d69b643ac0cb9148"),
    "timestamp" : ISODate("2017-03-01T16:58:00.000Z"),
    "Technique-Meteo_Direction moyenne du vent_Mean value wind direction[]" : 0.0,
   "Technique-Meteo_Précipitations_Precipitation status[]" : 0.0,
    "Technique-Meteo_Direction du vent_Wind direction[]" : 0.0
}

/* 5 */
{
    "_id" : ObjectId("592d4f43d69b643ac0cb9149"),
    "timestamp" : ISODate("2017-03-01T17:09:00.000Z"),
    "Technique-Meteo_Direction moyenne du vent_Mean value wind direction[]" : 0.0,
    "Technique-Meteo_Précipitations_Precipitation status[]" : 0.0,
   "Technique-Meteo_Direction du vent_Wind direction[]" : 0.0
}

/* 6 */
{
    "_id" : ObjectId("592d3a6cd69b643ac0cae395"),
    "timestamp" : ISODate("2017-01-30T09:31:00.000Z"),
    "Technique-Electrique_Prises de Courant_Power1[W]" : 14.0,
    "Technique-Electrique_Eclairage_Power2[W]" : 360.0,
    "Technique-Electrique_Electroménager_Power3[W]" : 0.0,
    "Technique-Electrique_VMC Aldes_Power4[W]" : 14.0,
    "Technique-Electrique_VMC Unelvent_Power5[W]" : 8.0
}

/* 7 */
{
    "_id" : ObjectId("592d3a6cd69b643ac0cae396"),
    "timestamp" : ISODate("2017-01-30T09:32:00.000Z"),
    "Technique-Electrique_Prises de Courant_Power1[W]" : 15.0,
    "Technique-Electrique_Eclairage_Power2[W]" : 365.0,
    "Technique-Electrique_Electroménager_Power3[W]" : 0.0,
    "Technique-Electrique_VMC Aldes_Power4[W]" : 14.0,
    "Technique-Electrique_VMC Unelvent_Power5[W]" : 8.0
}

有一个“_id”、一个“时间戳”和多个传感器字段。传感器数量不一致。通过一个界面,我选择了许多想要包含在查询中的传感器。此选项存储在一个列表中,其中每个项目都是传感器的名称。

例子:

self.chosenSensors = ["Technique-Electrique_VMC Aldes_Power4[W]", "Technique-Electrique_VMC Unelvent_Power5[W]"]

我想计算每个所选传感器的平均值。我已经这样做了,但我对每个传感器进行了查询。

在下面的示例中,我将向您展示这一点。 (不要考虑日期聚合,这是下一步)

page2.currentColl].aggregate([{"$match":{chosenSensor:{"$exists": True}}}, {"$group":{"_id":{"year":{"$year":"$timestamp"}, "month":{"$month":"$timestamp"}}, "average":{"$avg": chosenSensorAverage}}}])

结果(每个平均值都在一个新文档中):

RDC-ChambreEnfants_CO2_GAS_CONCENTRATION[ppm]
{'_id': {'year': 2017, 'month': 4}, 'average': 1475.3685814315352}
{'_id': {'year': 2017, 'month': 3}, 'average': 1374.3771154414906}
RDC-ChambreEnfants_Humidité_HUMIDITY[%]
{'_id': {'year': 2017, 'month': 4}, 'average': 37.55591753379364}
{'_id': {'year': 2017, 'month': 3}, 'average': 37.459350662153724}

我想得到的是以下内容:

{
    "Avg_Technique-Meteo_Direction moyenne du vent_Mean value wind direction[]" : 0.0,
    "Avg_Technique-Meteo_Précipitations_Precipitation status[]" : 0.0,
    "Avg_Technique-Meteo_Direction du vent_Wind direction[]" : 0.0
    "Avg_Technique-Electrique_Prises de Courant_Power1[W]" : 14.5,
    "Avg_Technique-Electrique_Eclairage_Power2[W]" : 362.5,
    "Avg_Technique-Electrique_Electroménager_Power3[W]" : 0.0,
    "Avg_Technique-Electrique_VMC Aldes_Power4[W]" : 14.0,
    "Avg_Technique-Electrique_VMC Unelvent_Power5[W]" : 8.0
}

我得到了一个提示(尼尔·伦恩):

您可以使语句更长并获得“计数”和 每个使用 $ifNull 来确定何时增加的“总和”。然后你 将 $divide “之后” $group 管道阶段以获得最终结果 “平均”。

如前所述,“键名”对我来说似乎是更大的问题,并且会 通过将它们移动到元素内的“值”可能会更好地处理 一个数组

我的第一个问题是我不知道如何在查询中使用我的传感器列表。解决这个问题后,可能还会出现其他问题。

【问题讨论】:

    标签: mongodb python-3.x aggregation-framework pymongo


    【解决方案1】:

    概念大纲

    我在非常简短的评论中基本上要说的是 而不是 为每个传感器“键”名称发出单独的聚合查询,您可以将其放入 ONE ,只要您正确计算“平均值”即可。

    当然,您的数据中的问题是并非所有文档中都存在“键”。因此,要获得正确的“平均值”,我们不能只使用$avg,因为它会计算“所有”文档,无论密钥是否存在。

    因此,我们改为分解“数学”,并首先为每个键的总计 Count 和总计 Sum 执行 $group。这使用$ifNull 来测试字段是否存在,还使用$cond 来替换要返回的值。

    .aggregate([
      { "$match": {
        "$or": [
          { "Technique-Electrique_VMC Aldes_Power4[W]": { "$exists": True } },
          { "Technique-Electrique_VMC Unelvent_Power5[W]": { "$exists": True } }
        ]
      }}
      { "$group":{
        "_id":{
          "year":{ "$year":"$timestamp" },
          "month":{ "$month":"$timestamp" }
        },
        "Technique-Electrique_VMC Aldes_Power4[W]-Sum": { 
          "$sum": { 
            "$ifNull": [ "$Technique-Electrique_VMC Aldes_Power4[W]", 0 ]
          }
        },
        "Technique-Electrique_VMC Aldes_Power4[W]-Count": { 
          "$sum": { 
            "$cond": [
              { "$ifNull": [ "$Technique-Electrique_VMC Aldes_Power4[W]", false ] },
              1,
              0
            ]
          }
        },
        "Technique-Electrique_VMC Unelvent_Power5[W]-Sum": {
          "$sum": { 
            "$ifNull": [ "$Technique-Electrique_VMC Unelvent_Power5[W]", 0 ]
          }
        },
        "Technique-Electrique_VMC Unelvent_Power5[W]-Count": {
          "$sum": {
            "$cond": [ 
              { "$ifNull": [ "$Technique-Electrique_VMC Unelvent_Power5[W]", false ] },
              1,
              0
            ]
          }
        }
      }},
      { "$project": {
        "Technique-Electrique_VMC Aldes_Power4[W]-Avg": {
          "$divide": [
            "$Technique-Electrique_VMC Aldes_Power4[W]-Sum",
            "$Technique-Electrique_VMC Aldes_Power4[W]-Count"
          ]
        },
        "Technique-Electrique_VMC Unelvent_Power5[W]-Avg": {
          "$divide": [
            "Technique-Electrique_VMC Unelvent_Power5[W]-Sum",
            "Technique-Electrique_VMC Unelvent_Power5[W]-Count"
          ]
        }
      }}
    ])
    

    $cond 运算符是一个“三元”运算符,这意味着第一个“if”条件为true,“then”返回第二个参数,“else”返回第三个参数。

    所以"Count" 中的三元组的意义在于解决:

    • 如果该字段存在则返回 1 表示计数
    • 否则不存在时返回0

    $group 完成后,为了获得Average,我们在单独的$project 阶段中​​为每个键生成的两个数字上使用$divide

    最终结果是您提供的每个键的“平均值”,这仅考虑了为实际存在该字段的文档添加值和计数。

    因此,将所有键放在一个聚合语句中将为您节省大量处理时间和资源。


    管道的动态生成

    因此,要在 python 中“动态”执行此操作,请从列表开始:

    sensors = ["Technique-Electrique_VMC Aldes_Power4[W]", "Technique-Electrique_VMC Unelvent_Power5[W]"]
    
    match = { '$match': { '$or': map(lambda x: { x: { '$exists': True } },sensors) } }
    
    group = { '$group': { 
      '_id': {
        'year': { '$year': '$timestamp' },
        'month': { '$month':'$timestamp' }
      }
    }}
    
    project = { '$project': {  } }
    
    for k in sensors:
      group['$group'][k + '-Sum'] = {
        '$sum': { '$ifNull': [ '$' + k, 0 ] }
      }
      group['$group'][k + '-Count'] = {
        '$sum': { '$cond': [ { '$ifNull': [ '$' + k, False ] }, 1, 0 ]  }
      }
      project['$project'][k + '-Avg'] = {
        '$divide': [ '$' + k + '-Sum', '$' + k + '-Count' ]
      }
    
    pipeline = [match,group,project]
    

    对于给定的“传感器”列表,这与上面的完整列表相同。

    【讨论】:

    • 你可能会用这个答案回答我未来的很多问题。但是,我目前的问题是我的传感器列表可以更改。这就是为什么我把他们的名字放在一个列表中。但我想用这个列表来做你所做的。例如,“$Technique-Electrique_VMC Aldes_Power4[W]”将被 mySensorsList[0] 替换,这在查询中当然是不可能的。我发现了一些 $in 和 $unwind 的用法,但我什至不知道它们是否是好的运算符,我不知道如何使用它。
    • @Clément 与所有 MongoDB 查询一样,聚合管道只是由您正在使用的语言的本机数据结构组成。所以这些只是Dicts。您可以在代码中构建字典,因此您不需要“硬编码”键。我只是向您展示如何为这里的逻辑构建管道的最终键名。现在明白了?这是现在要做的事情。对于“未来”,我认为您还可以做一些优化。
    • @Clément 由于您似乎不明白,我已经为您编写了 Python 代码,以便从给定的传感器列表生成管道。扔出尽可能多的传感器名称。这就是动态执行它并将所有键扔到一个聚合语句中的全部意义。
    • 好的,我刚刚看到您的动态生成编辑。实际上我被卡住了,因为我不知道我们可以直接在查询中包含 python 循环。我肯定缺乏这方面的知识和逻辑。感谢您的帮助。
    • 我刚刚注意到我的结果有问题。每个 '-Count' 采用最后计算的值;也就是说传感器的最后一个元素的计数。这真的很奇怪,因为它不是几乎相似的求和行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-06
    • 2021-04-13
    相关资源
    最近更新 更多