# DolphinDB TopN 系列函数教程 DolphinDB 已经有非常多的窗口计算函数,例如 m 系列的滑动窗口计算,cum 系列累计窗口计算,tm 系列的的时间窗口滑动计算。但是所有这类函数都是对窗口内的所有记录进行指标计算,难免包含很多噪音。DolphinDB 的金融领域用户反馈,通过交易量信息等对窗口内的记录进行过滤,得到的计算指标具有更高的质量,以此为基础的交易策略能带来更多的 Alpha。同时用户也反馈,通过自定义函数来计算按额外信息过滤后的指标,消耗的时间过长。为此,DolphinDB 推出了 TopN 系列内置函数,涵盖 mTopN 系列、tmTopN 系列、cumTopN 系列,通过增量计算,大幅提升性能。DolphinDB 2.00.10 及 1.30.22 版本均支持本教程中涉及的功能。 本教程将从以下几个角度介绍 TopN 系列函数: - [1. TopN 系列函数能解决的问题、计算规则及实现](#1-topn-系列函数能解决的问题计算规则及实现) - [1.1 TopN 系列函数解决的痛点问题](#11-topn-系列函数解决的痛点问题) - [1.2 TopN 系列函数的计算规则及实现](#12-topn-系列函数的计算规则及实现) - [2. mTopN 、tmTopN 系列的应用场景](#2-mtopn-tmtopn-系列的应用场景) - [2.1 mTopN 应用场景](#21-mtopn-应用场景) - [2.2 tmTopN 应用场景](#22-tmtopn-应用场景) - [3. cumTopN 系列的应用场景](#3-cumtopn-系列的应用场景) - [4. 自定义 TopN 函数](#4-自定义-topn-函数) - [4.1 自定义 TopN 的实现方法](#41-自定义-topn-的实现方法) - [4.2 内置 TopN 与自定义 TopN 的性能对比](#42-内置-topn-与自定义-topn-的性能对比) - [5. TopN 的批流一体场景](#5-topn-的批流一体场景) - [6. DECIMAL 的使用](#6-decimal-的使用) - [7. 总结](#7-总结) ## 1. TopN 系列函数能解决的问题、计算规则及实现 ### 1.1 TopN 系列函数解决的痛点问题 在分组计算中,常常不需要对分组中的所有数据做计算,有时只需要对组中 topn 个元素做计算即可。举个例子,如想要统计每天每只股票 `Volume` 值小于第一四分位数的平均 `Volume` 值,是不可以用 `percentile` 函数直接求值的,因为 sql 语句是先整体进行条件过滤再分组计算,因此无法将每个分组的 top 元素取出。 假设存在表 *tb* 是两支股票的数据表,要按照股票和日期分组后用 `percentile` 筛选,统计每天每只股票因子位于前40%的 `value` 的平均值。 ``` trade_date=sort(take(2017.01.11..2017.01.12,20)) secu_code=take(`600570`600000,20) value=1..20 tb=table(trade_date,secu_code,value) ``` 常规的做法编写自定义聚合函数来实现: ``` defg percentile_40(x){ ret = NULL y = percentile(x,40) cc = sum(x 0){ ret = sum(iif( x //定义输入输出表结构 share streamTable(1:0, `tradingTime`windCode`open`high`low`close`volume, [TIMESTAMP,STRING,DOUBLE,DOUBLE,DOUBLE,DOUBLE,INT]) as tickStream result = table(1000:0, `windCode`tradingTime`mavgTop10RatioClose, [STRING,TIMESTAMP,DOUBLE]) //定义流计算引擎 rse = createReactiveStateEngine(name="streamTopN", metrics =[, factor], dummyTable=t, outputTable=result, keyColumn="windCode") //订阅流表、回放数据 subscribeTable(tableName=`tickStream, actionName="mTopN", handler=tableInsert{rse}) replay(inputTables=t.copy().sortBy!(`tradingTime), outputTables=tickStream, timeColumn=`tradingTime) //查询流计算结果: select * from result //如若想要反复调用上述脚本,先运行以下三行脚本,清除流表订阅 unsubscribeTable(tableName=`tickStream, actionName="mTopN") dropStreamEngine(`streamTopN) undef(`tickStream, SHARED) ``` ## 6. DECIMAL 的使用 2.00.10及后续的版本支持在 TopN 系列函数中使用 DECIMAL 类型(包括 DECIMAL32、64以及128类型)。不仅排序字段 S,而且计算字段 X 和 Y 均可使用 DECIMAL 类型。如果计算字段是 DECIMAL 类型,msumTopN、tmsumTopN 和 cumsumTopN 三个函数返回 DECIMAL 类型,其它函数的结果仍然返回 DOUBLE 类型。 虽然 `var`、`varp`、`std`、`stp`、`corr`、`covar`、`beta`、`wsum` 等8个基础函数对应的 TopN 系列函数最终结果位 DOUBLE 类型,但是当计算列 X 和 Y 为 DECIMAL 类型时,计算的中间结果用 DECIMAL128 表示,这样可以避免精度丢失。当然使用 DECIMAL 做计算的中间结果,也有不足的一面。首先计算耗时会更长,其次可能会出现 overflow。当前的版本,在计算出现 overflow 时,并不会抛出异常,这需要引起特别的注意。 DECIMAL128 的有效位数是38位(包括小数点前和后的位数)。例如要对价格数据算方差,18.2345这个数据总共6位有效数据,平方之后就是12位,如果有1亿个数(8位),总的有效位数是20位,远远低于38,不会出现 overflow。但如果小数位数特别多,例如小数点后从4位增加到了15位,这样价格数据的有效位数是17,平方之后就是34位,1万个数就可能超过 DECIMAL128 的有效数字38位。碰到小数位数特别多的数据,要么转成 DOUBLE 类型处理,要么用 decimal32、decimal64,decimal128 等函数先降低数据精度。 ## 7. 总结 本教程介绍了 TopN 的计算规则,以及内置的36个 mTopN 、tmTopN、cumTopN 系列函数在离线计算和流计算等场景的应用,并针对性地介绍了自定义 TopN 的实现方法。 在未来版本中,DolphinDB 将支持更多的内置 TopN 计算函数。