8

我正在寻找一种使用 Mongo 生成一些汇总统计数据的方法。假设我有一个包含许多表单记录的集合

{"name" : "Jeroen", "gender" : "m", "age" :27.53 }

现在我想获得性别和年龄的分布。假设性别,只有值"m""f"。获取我的收藏中男性和女性总数的最有效方法是什么?

对于年龄,有没有一种方法可以进行一些“分箱”并给我一个像摘要这样的直方图;即年龄在区间内的记录数:[0, 2), [2, 4), [4, 6) ...等?

4

6 回答 6

20

我刚刚试用了 MongoDB 2.2 版(2.2.0-rc0 已发布)中将提供的新聚合框架,它应该比 map reduce 具有更高的性能,因为它不依赖于 Javascript。

输入数据:

{ "_id" : 1, "age" : 22.34, "gender" : "f" }
{ "_id" : 2, "age" : 23.9, "gender" : "f" }
{ "_id" : 3, "age" : 27.4, "gender" : "f" }
{ "_id" : 4, "age" : 26.9, "gender" : "m" }
{ "_id" : 5, "age" : 26, "gender" : "m" }

性别聚合命令:

db.collection.aggregate(
   {$project: {gender:1}},
   {$group: {
        _id: "$gender",
        count: {$sum: 1}
   }})

结果:

{"result" : 
   [
     {"_id" : "m", "count" : 2},
     {"_id" : "f", "count" : 3}
   ],
   "ok" : 1
}

要获取垃圾箱中的年龄:

db.collection.aggregate(
   {$project: {
        ageLowerBound: {$subtract:["$age", {$mod:["$age",2]}]}}
   },
   {$group: {
       _id:"$ageLowerBound", 
       count:{$sum:1}
   }
})

结果:

{"result" : 
    [
       {"_id" : 26, "count" : 3},
       {"_id" : 22, "count" : 2}
    ],
    "ok" : 1
}
于 2012-07-24T01:07:35.023 回答
2

康斯坦丁的回答是对的。MapReduce 完成了工作。这是完整的解决方案,以防其他人觉得这很有趣。

要计算性别,map 功能键是this.gender每条记录的属性。然后 reduce 函数简单地将它们相加:

// count genders
db.persons.mapReduce(
    function(){
        emit(this["gender"], {count: 1})
    }, function(key, values){
        var result = {count: 0};
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    }, {out: { inline : 1}}
);

为了进行分箱,我们将 map 函数中的键设置为向下舍入到最接近的除以 2。因此,例如 10 和 11.9999 之间的任何值都将获得相同的密钥"10-12"。然后我们再次简单地将它们相加:

db.responses.mapReduce(
    function(){
        var x = Math.floor(this["age"]/2)*2;
        var key = x + "-" + (x+2);
        emit(key, {count: 1})
    }, function(state, values){
        var result = {count: 0};
        values.forEach(function(value) {
            result.count += value.count;
        });
        return result;
    }, {out: { inline : 1}}
);
于 2012-07-23T23:24:00.203 回答
1

获得男性总数的简单方法是db.x.find({"gender": "m"}).count()

如果您只想在一个查询中同时计算男性和女性人数,那么没有简单的方法。映射/减少将是一种可能性。或者也许是新的聚合框架您的分箱要求也是如此

Mongo 不适合聚合,但它对于许多小的增量更新来说非常棒。所以用 mongo 解决这个问题的最好方法是在单独的集合中收集聚合数据。

因此,如果您使用这样的一个文档保存统计信息集合:

stats: [
  {
     "male": 23,
     "female": 17,
     "ageDistribution": {
       "0_2" : 3,
       "2_4" : 5,
       "4_6" : 7
     }
  }
]

...然后每次您从另一个集合中添加或删除一个人时,您都会在统计数据集合中向上或向下计数相应的字段。

db.stats.update({"$inc": {"male": 1, "ageDistribution.2_4": 1}})

通过这种方式,对统计信息的查询将非常快,并且您几乎不会注意到上下统计统计信息的任何性能开销。

于 2012-07-23T11:13:28.020 回答
0

根据数据量,查找男性和女性数量的最有效方法可能是幼稚查询或地图缩减工作。装箱最好通过 map reduce 完成:

在 map 阶段,您的键是一个 bin,值为 1,而在 reduce 阶段,您只需对值求和

于 2012-07-23T11:06:46.080 回答
0

借助新的 $bucket 和$bucketAuto聚合函数,使用 Mongo 3.4 变得更加容易。以下查询自动分为两组:

db.bucket.aggregate( [
   {
     $bucketAuto: {
         groupBy: "$gender",
         buckets: 2
     }
   }
] )

使用以下输入数据:

{ "_id" : 1, "age" : 22.34, "gender" : "f" }
{ "_id" : 2, "age" : 23.9, "gender" : "f" }
{ "_id" : 3, "age" : 27.4, "gender" : "f" }
{ "_id" : 4, "age" : 26.9, "gender" : "m" }
{ "_id" : 5, "age" : 26, "gender" : "m" }

它给出以下结果:

{ "_id" : { "min" : "f", "max" : "m" }, "count" : 3 }
{ "_id" : { "min" : "m", "max" : "m" }, "count" : 2 }

注意,bucket 和 auto-bucket 通常用于连续变量(数字、日期),但在这种情况下 auto-bucket 工作得很好。

于 2017-01-23T06:32:44.667 回答
0

根据@ColinE 的答案,直方图的分箱可以通过

db.persons.aggregate([
  {
  $bucket: {
    groupBy: "$j.age",
    boundaries: [0,2,4,6,8,10,12,14,16,18,20],
    default: "Other",
    output: {
      "count": { $sum: 1 }
    }
  }
],
{allowDiskUse:true})

$bucketAuto对我不起作用,因为桶似乎是按对数刻度收集的。 allowDiskUse仅当您拥有数百万个文档时才需要

于 2017-05-10T15:05:51.380 回答