文章目录
$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 }