MongoDB子字段多列表聚合:正确计算均值与标准差
解决MongoDB聚合中数组字段统计结果重复的问题
这个问题我之前也碰到过,核心原因是当你在$project里用data.first_thing.mean这种嵌套路径时,MongoDB会把统计计算应用到原数组的每个元素上,所以结果会重复原数组的长度次。要得到你想要的结构,有两种常用的方法:
方法一:手动构造data对象(适合字段数量少的情况)
直接在$project里重新定义整个data字段,把原来的数组字段替换成包含均值和标准差的对象,这样就不会出现重复的情况:
cursor = db.testcoll.aggregate([ { '$project': { '_id': '$name', 'data': { 'first_thing': { 'mean': {'$avg': '$data.first_thing'}, 'std': {'$stdDevPop': '$data.first_thing'} }, 'second_thing': { 'mean': {'$avg': '$data.second_thing'}, 'std': {'$stdDevPop': '$data.second_thing'} } } } } ])
执行后会得到你期望的结构:
{ "_id": "some_name", "data": { "first_thing": { "mean": 2.5, "std": 1.118033988749895 }, "second_thing": { "mean": 3.5, "std": 1.118033988749895 } } }
方法二:动态处理所有子字段(适合字段数量多的情况)
如果data下有很多子字段,不想逐个手动编写,可以用$objectToArray和$arrayToObject来动态遍历处理:
cursor = db.testcoll.aggregate([ { '$project': { '_id': '$name', 'data': { '$arrayToObject': { '$map': { 'input': {'$objectToArray': '$data'}, 'as': 'item', 'in': { 'k': '$$item.k', # 保留原字段名 'v': { 'mean': {'$avg': '$$item.v'}, 'std': {'$stdDevPop': '$$item.v'} } } } } } } } ])
这个方法的逻辑是:
- 用
$objectToArray把data对象转换成键值对数组(比如[{k: "first_thing", v: [1,2,3,4]}, ...]) - 用
$map遍历这个数组,对每个字段的数组值计算均值和标准差,生成新的键值对 - 再用
$arrayToObject把处理后的数组转回对象,得到最终的data结构
为什么原来的写法会出错?
当你使用data.first_thing.mean这种路径时,MongoDB会识别到first_thing是一个数组,它会把mean和std的计算结果附加到数组的每一个元素上,所以结果会重复原数组的长度。而我们上面的方法是直接替换整个first_thing字段,把它从数组改成包含统计值的对象,自然就不会有重复的问题了。
内容的提问来源于stack exchange,提问作者MaxPowers
相关产品推荐
相关产品推荐

