定义
语法
$percentile 的语法为:
{    $percentile: {       input: <expression>,       p: [ <expression1>, <expression2>, ... ],       method: <string>    } } 
命令字段
$percentile 采用以下字段:
| 字段 | 类型 | 必要性 | 说明 | 
|---|---|---|---|
| 
 | 表达式(expression) | 必需 | 
 | 
| 
 | 表达式(expression) | 必需 | 
 
 | 
| 
 | 字符串 | 必需 | 
 | 
行为
您可以在以下位置使用 $percentile:
- $group阶段用作累加器
- $setWindowFields阶段用作累加器
- $project阶段作为聚合表达式
$percentile 作为累加器,具有以下特点:
- 为阶段中的所有文档计算单独的结果。 
- 使用 t-digest 算法计算基于百分位数的近似指标。 
- 使用近似方法来扩展海量数据。 
$percentile 作为聚合表达式,具有以下特点:
- 接受数组作为输入 
- 为每个输入文档计算单独的结果 
操作类型
在 $group 阶段,$percentile 是累加器,计算窗口中所有文档的值。
在 $project 阶段,$percentile 是一个聚合表达式,用于计算每个文档的值。
在 $setWindowFields 阶段中,$percentile 像聚合表达式一样返回每个文档的结果,但该结果是通过诸如累加器之类的文档组计算得出的。
计算注意事项
在 $group 阶段中,$percentile 始终使用近似计算方法。
在 $project 阶段,即使指定了近似方法,$percentile 也可能使用离散计算方法。
在 $setWindowFields 阶段,工作负载决定了 $percentile 使用的计算方法。
即使在相同的数据集上,计算出的 $percentile 收益的百分位数也可能不同。这是因为算法计算的是近似值。
重复样本可能会导致歧义。如果存在大量重复样本,百分位值可能无法代表实际的样本分布。考虑所有样本都相同的数据集。数据集中的所有值都位于或低于任何百分位数。“第 50 百分位数”的值实际上代表 0 或 100% 的样本。
$percentile 返回 p = 0.0 的最小值。
$percentile 返回 p = 1.0 的最大值。
数组输入
如果在 $project 阶段使用 $percentile 作为聚合表达式,则可以使用数组作为输入。语法为:
{    $percentile: {       input: [ <expression1, <expression2>, .., <expressionN> ],       p: [ <expression1>, <expression2>, ... ],       method: <string>    } } 
窗口功能
通过窗口函数,您可以计算出相邻文档移动“窗口”的结果。当每份文档通过管道时,$setWindowFields 阶段:
- 重新计算当前窗口中的文档集 
- 计算集合中所有文档的值 
- 返回该文档的单个值 
您可以在 $setWindowFields 阶段使用 $percentile 来计算时间序列或其他相关数据的滚动统计数据。
在 $setWindowField 阶段使用 $percentile 时,input 值必须是字段名。如果输入数组而不是字段名称,则操作将失败。
示例
以下示例使用 testScores 集合。创建集合:
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 } ] ) 
将单个值作为累加器进行计算
创建一个计算单个百分位值的累加器:
db.testScores.aggregate( [    {       $group: {          _id: null,          test01_percentiles: {             $percentile: {                input: "$test01",                p: [ 0.95 ],                method: 'approximate'             }          },       }    } ] ) 
输出:
{ _id: null, test01_percentiles: [ 67 ] } 
_id 字段值为 null,因此 $group 选择集合中的所有文档。
percentile 累加器从 test01 字段获取输入数据。
在此示例中,百分位数数组 p 有一个值,因此 $percentile 操作符仅计算 test01 数据的一个项。第 95 个百分位数值为 67。
将多个值作为累加器进行计算
创建一个计算多个百分位数值的累加器:
db.testScores.aggregate( [    {        $group: {           _id: null,           test01_percentiles: {              $percentile: {                 input: "$test01",                 p: [ 0.5, 0.75, 0.9, 0.95 ],                 method: 'approximate'              }           },           test02_percentiles: {              $percentile: {                 input: "$test02",                 p: [ 0.5, 0.75, 0.9, 0.95 ],                 method: 'approximate'              }           },           test03_percentiles: {              $percentile: {                 input: "$test03",                 p: [ 0.5, 0.75, 0.9, 0.95 ],                 method: 'approximate'              }           },           test03_percent_alt: {              $percentile: {                 input: "$test03",                 p: [ 0.9, 0.5, 0.75, 0.95 ],                 method: 'approximate'              }           },        }     } ] ) 
输出:
{     _id: null,    test01_percentiles: [ 62, 64, 67, 67 ],    test02_percentiles: [ 81, 82, 83, 83 ],    test03_percentiles: [ 78, 79, 80, 80 ],    test03_percent_alt: [ 80, 78, 79, 80 ] } 
_id 字段值为 null,因此 $group 选择集合中的所有文档。
percentile 累加器计算三个字段的值,即 test01、test02 和 test03。
累加器会计算每个输入字段的第 50、75、90 和 95 个百分位数。
按照与 p 的元素相同的顺序返回百分位数值。test03_percentiles 和 test03_percent_alt 中的值相同,但顺序不同。每个结果数组中元素的顺序与 p 中相应的元素顺序匹配。
$percentile在$project 阶段使用
在 $project 阶段,$percentile 是一个聚合表达式,用于计算每个文档的值。
您可以在 $project 阶段使用字段名称或数组作为输入。
db.testScores.aggregate( [    {       $project: {          _id: 0,          studentId: 1,          testPercentiles: {             $percentile: {                input: [ "$test01", "$test02", "$test03" ],                p: [ 0.5, 0.95 ],                method: 'approximate'             }          }       }    } ] ) 
输出:
{ studentId: '2345', testPercentiles: [ 80, 81 ] }, { studentId: '2356', testPercentiles: [ 79, 83 ] }, { studentId: '2358', testPercentiles: [ 78, 82 ] }, { studentId: '2367', testPercentiles: [ 72, 77 ] }, { studentId: '2369', testPercentiles: [ 60, 72 ] } 
当 $percentile 是聚合表达式时,每个 studentId 都有一个结果。
$percentile在$setWindowField 阶段使用
如需根据本地数据趋势确定百分位值,请使用 $setWindowField 聚合管道阶段的 $percentile。
此示例创建了一个筛选分数的窗口:
db.testScores.aggregate( [    {       $setWindowFields: {          sortBy: { test01: 1 },          output: {             test01_95percentile: {                $percentile: {                   input: "$test01",                   p: [ 0.95 ],                   method: 'approximate'                },                window: {                   range: [ -3, 3 ]                }             }          }       }    },    {       $project: {          _id: 0,          studentId: 1,          test01_95percentile: 1       }    } ] ) 
输出:
{ studentId: '2356', test01_95percentile: [ 62 ] }, { studentId: '2369', test01_95percentile: [ 62 ] }, { studentId: '2345', test01_95percentile: [ 64 ] }, { studentId: '2367', test01_95percentile: [ 67 ] }, { studentId: '2358', test01_95percentile: [ 67 ] } 
在此示例中,每个文档的百分位数计算还包含该文档之前和之后的三个文档的数据。
了解详情
$median 操作符是 $percentile 操作符的一种特殊情况,它使用固定的值 p: [ 0.5 ]。
有关窗口函数的更多信息,请参阅:$setWindowFields。