MongoDB聚合运算符:$median

文章目录

$median聚合运算符以标量值返回中位数的近似值,即第50百分位数。$median可以在$group阶段的累加器或聚合表达式使用。

语法

js 复制代码
{
   $median: {
      input: <number>,
      method: <string>
   }
}

参数字段

  • input:必须的参数字段,数值类型的字段或表达式,指定要计算中位数的值,如果值不是字符类型,在计算式会被忽略。
  • method:字符串或字符串表达式,指定中位数的计算方法,其值必须为approximate

使用

  • $median可以在下面的阶段使用
    • 可以用于$group$setWindowFields阶段的累加器
    • 可以用于$project阶段的聚合表达式
  • $median作为累加器时有下面的特征:
    • 计算阶段中所有文档的单个结果
    • 使用t-digest算法计算基于百分位数的近似指标。
    • 使用近似方法来扩展大量数据。
  • $median作为聚合表达式具有以下特点:
    • 接受数组作为输入
    • 每个输入的文档计算一个结果

类型操作

  • $group 阶段,$median 是一个累加器,用于计算窗口中所有文档的值。
  • $project 阶段,$median 是一个聚合表达式,用于计算每个文档的值。
  • $setWindowFields 阶段,$median 像聚合表达式一样返回每个文档的结果,但结果是像累加器一样对文档组进行计算的。

计算注意事项

  • $group阶段,$median始终使用近似计算方法。
  • $project阶段,即使指定了近似方法,$median仍然使用离散计算方法。
  • $setWindowFields阶段,工作负载决定$median使用的计算方法。
  • 因为算法计算的是近似值,所以即使在相同的数据集上,计算出的百分位数$median返回也可能会有所不同。
  • 重复的样本可能会导致歧义。如果存在大量重复项,百分位数可能无法代表实际的样本分布。比如在一个所有样本都相同的数据集,数据集中的所有值都处于或低于任何百分位, "第 50 个百分位"值实际上代表 0% 或 100% 的样本。

数组处理

如果在$project阶段使用$median作为聚合表达式,则可以使用数组作为输入,$median忽略非数字数组值。

语法为:

js 复制代码
{
   $median:
      {
         input: [ <expression1, <expression2>, ..., <expressionN> ],
         method: <string>
      }
}

窗口函数

通过窗口函数,可以计算出相邻文档移动 "窗口 "的结果。当文档通过管道时,$setWindowFields阶段:

  • 重新计算当前窗口中的文档集
  • 计算集合中所有文档的值
  • 返回该文档的单个值

可以在$setWindowFields阶段使用$median计算时间序列或其他相关数据的滚动统计数据。

$setWindowField阶段使用$median时,输入值必须是字段名,如果输入的是数组而不是字段名,操作将失败。

举例

使用下面的脚本创建testScores集合:

js 复制代码
db.testScores.insertMany( [
   { studentId: "2345", test01: 62, test02: 81, test03: 80 },
   { studentId: "2356", test01: 60, test02: 83, test03: 79 },
   { studentId: "2358", test01: 67, test02: 82, test03: 78 },
   { studentId: "2367", test01: 64, test02: 72, test03: 77 },
   { studentId: "2369", test01: 60, test02: 53, test03: 72 }
] )

$median作为累加器

下面的聚合,创建一个累加器用来中位数:

js 复制代码
db.testScores.aggregate( [
   {
      $group: {
         _id: null,
         test01_median: {
            $median: {
               input: "$test01",
               method: 'approximate'
            }
         }
      }
   }
] )

结果:

js 复制代码
{ _id: null, test01_median: 62 }

_id字段的值为空,所以$group选择了集合中的所有文档。

$median累加器用test01字段作为输入字段,计算出字段的中位数为62

在 p r o j e c t 阶段使用 project阶段使用 project阶段使用median

$group阶段,$median是一个累加器,从所有文档中计算单个值。在$project阶段,$median是一个聚合表达式,计算所有文档的值。

$project阶段,可以使用字段名或数组作为输入。

js 复制代码
db.testScores.aggregate( [
   {
      $project: {
         _id: 0,
         studentId: 1,
         testMedians: {
            $median: {
               input: [ "$test01", "$test02", "$test03" ],
               method: 'approximate'
            }
         }
      }
   }
] )

结果输出:

js 复制代码
{ studentId: '2345', testMedians: 80 },
{ studentId: '2356', testMedians: 79 },
{ studentId: '2358', testMedians: 78 },
{ studentId: '2367', testMedians: 72 },
{ studentId: '2369', testMedians: 60 }

$median是一个聚合表达式时,每个studentId都有一个结果。

$setWindowField阶段使用$median

根据本地数据趋势确定百分位值,需要在$setWindowField聚合管道阶段使用$median。下面的例子创建一个窗口来过滤分数:

js 复制代码
db.testScores.aggregate( [
   {
      $setWindowFields: {
         sortBy: { test01: 1 },
         output: {
            test01_median: {
               $median: {
                  input: "$test01",
                  method: 'approximate'
               },
               window: {
                  range: [ -3, 3 ]
               }
            }
         }
      }
   },
   {
      $project: {
         _id: 0,
         studentId: 1,
         test01_median: 1
      }
   }
] )

执行的结果:

js 复制代码
{ studentId: '2356', test01_median: 60 },
{ studentId: '2369', test01_median: 60 },
{ studentId: '2345', test01_median: 60 },
{ studentId: '2367', test01_median: 64 },
{ studentId: '2358', test01_median: 64 }

下面的聚合操作使用$median运算符来判断qty是否小于250

js 复制代码
db.inventory.aggregate(
   [
     {
       $project:
          {
            item: 1,
            qty: 1,
            qtyLt250: { $median: [ "$qty", 250 ] },
            _id: 0
          }
     }
   ]
)

操作返回下面的结果:

json 复制代码
{ "item" : "abc1", "qty" : 300, "qtyLt250" : false }
{ "item" : "abc2", "qty" : 200, "qtyLt250" : true }
{ "item" : "xyz1", "qty" : 250, "qtyLt250" : false }
{ "item" : "VWZ1", "qty" : 300, "qtyLt250" : false }
{ "item" : "VWZ2", "qty" : 180, "qtyLt250" : true }
相关推荐
异世界贤狼转生码农1 小时前
MongoDB Windows 系统实战手册:从配置到数据处理入门
数据库·mongodb
QuZhengRong2 小时前
【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
android·数据库·excel
码农阿豪2 小时前
Windows从零到一安装KingbaseES数据库及使用ksql工具连接全指南
数据库·windows
时序数据说7 小时前
时序数据库市场前景分析
大数据·数据库·物联网·开源·时序数据库
听雪楼主.11 小时前
Oracle Undo Tablespace 使用率暴涨案例分析
数据库·oracle·架构
我科绝伦(Huanhuan Zhou)11 小时前
KINGBASE集群日常维护管理命令总结
数据库·database
妖灵翎幺11 小时前
Java应届生求职八股(2)---Mysql篇
数据库·mysql
HMBBLOVEPDX11 小时前
MySQL的事务日志:
数据库·mysql
weixin_4196583113 小时前
MySQL数据库备份与恢复
数据库·mysql
专注API从业者15 小时前
基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储
大数据·前端·数据库·数据挖掘·flink