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 }
相关推荐
wrx繁星点点8 分钟前
事务的四大特性(ACID)
java·开发语言·数据库
小小娥子37 分钟前
Redis的基础认识与在ubuntu上的安装教程
java·数据库·redis·缓存
DieSnowK39 分钟前
[Redis][集群][下]详细讲解
数据库·redis·分布式·缓存·集群·高可用·新手向
-XWB-1 小时前
【MySQL】数据目录迁移
数据库·mysql
老华带你飞1 小时前
公寓管理系统|SprinBoot+vue夕阳红公寓管理系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·spring boot·课程设计
我明天再来学Web渗透2 小时前
【hot100-java】【二叉树的层序遍历】
java·开发语言·数据库·sql·算法·排序算法
Data 3172 小时前
Hive数仓操作(十一)
大数据·数据库·数据仓库·hive·hadoop
吱吱鼠叔2 小时前
MATLAB数据文件读写:2.矩阵数据读取
数据库·matlab·矩阵
掘根2 小时前
【MySQL】Ubuntu环境下MySQL的安装与卸载
数据库·mysql·centos