我正在寻找一种使用Mongo生成一些摘要统计信息的方法。假设我有一个包含许多形式记录的集合
{"name" : "Jeroen", "gender" : "m", "age" :27.53 }
现在我想获得性别和年龄的分布。假设性别,只有值"m"
和"f"
。在我的收藏中获得男性和女性总数最有效的方法是什么?
对于年龄,是否有一种方法可以进行一些“分组”并给我一个像汇总的直方图;即年龄在区间内的记录数:[0, 2), [2, 4), [4, 6) ...
等?
答案 0 :(得分: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
}
答案 1 :(得分:2)
要计算性别,地图功能键是每条记录的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函数中设置键,向下舍入到最近的除法。因此例如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}}
);
答案 2 :(得分:1)
获得男性总数的简单方法是db.x.find({"gender": "m"}).count()
如果您只想在一个查询中同时考虑男性和女性,那么就没有简单的方法。 Map / reduce是一种可能性。或许是新的aggregation framework。您的 binning 要求
也是如此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}})
对统计数据的查询将以这种方式快速闪电,并且您几乎不会注意到上下统计数据的任何性能开销。
答案 3 :(得分:0)
根据数据量,找到男性和女性数量的最有效方法可能是 天真的查询或地图减少工作。分箱最好通过map reduce:
完成在地图阶段,您的密钥是一个bin,值为1,而在reduce阶段,您只需求值
答案 4 :(得分: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 }
请注意,存储桶和自动存储桶通常用于连续变量(数字,日期),但在这种情况下,自动存储桶工作正常。
答案 5 :(得分:0)
根据@ColinE binning的答案,可以通过
完成直方图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