关于量化:模型推理量化实现分享一详解-minmax-对称量化算法实现

 欢送关注我的公众号 [极智视界],回复001获取Google编程标准 O_o >_<  o_O O_o ~_~ o_O 大家好,我是极智视界,本文分析一下 min-max 对称量化算法实现,以 Tengine 的实现为例。   Tengine 是 OpenAILab 开源的优良端侧深度学习推理框架,其外围次要由 C 语言实现,包裹的性能代码嵌套了 C++。量化是推理减速必不可少的优化环节,成熟的推理框架个别会把量化模块剥离进去造成独立的一套工具,如 Tengine、NCNN、昇腾、寒武纪都这么做,这次要是因为量化过程和硬件非强相干,解耦开来无能更多的事。   min-max 和 kl 量化算法是硬件厂商适配推理引擎的根底和标配, 其中 kl 量化深受用户青睐,如英伟达的 TensorRT 也正是采纳了 kl 量化策略;而这里要介绍的 min-max 的特点是逻辑简略、成果良好,作为量化实现分享系列的开篇比拟适合,这里带大家一起钻研一下 Tengine 中 minx-max 量化策略的具体实现。 1、量化应用   量化次要分为激活值(动静)量化、权值&偏置(动态)量化,而权值&偏置的量化是对精度影响比拟大的,激活值的量化对整体影响较小,但也须要量化,才有可能协同达到整体称心的成果。对于个别量化来说,权值&偏置的量化会采纳逐通道 perChannel 的形式,而激活值的量化个别是逐层 perLayer 的形式。解释一下为啥会这样,对于量化来说,卷积必定是大头,对于卷积来说,若激活值量化采纳逐通道形式,这和卷积核参数共享是相悖的,所以个别激活值就用逐层量化,以符合卷积参数共享。   这里次要看一下 Tengine 量化须要的传参: Input model:传入的 fp32 tmfile 模型文件;Output model:生成的 int8 tmfile 模型文件;Calib images:传入的激活值量化校准图片;Scale file:生成的校准表文件;Agorithm:量化算法,可选 MIN-MAX、KL、ACIQ、DFQ、EQ;Dims:输出校准图的 shape,这里传三维 c h w,n 在代码中写死 n = 1;Mean:图像预处理均值;Scale:图像预处理缩放尺度;BGR2RGB:通道转换;Center crop:图像预处理,裁剪;Letter box:图像预处理,放弃横纵比的前提下对图像做 resize;YOLOv5 focus:相似 yolov5 的预处理注意力机制;Thread num:量化多线程设置;2、min-max 量化 min-max 是最简略的量化算法,次要逻辑如下: ...

December 15, 2021 · 6 min · jiezi

关于量化:2021年量化大事件

1.量化基金规模头部效应显著 2021 年以来公募量化基金集中度晋升,截至2021年三季报,规模前30%的公募量化对冲基金规模集中度晋升至81.2%,公募中证500加强基金的规模集中度晋升至83.9%。百亿量化私募扩容,最新百亿级量化私募达到25家,其中明汯投资、幻方量化、九坤投资、灵均投资等私募规模靠前。 2.公募量化的“被动”转型 量化拥抱被动成为重要趋势,2018年以来,公募量化基金对于业绩超预期股票池的配置权重出现显著回升趋势。同时局部量化基金超配抱团股。 3.量化基金的两波回撤与抱团瓦解无关 量化基金2021年2-3月份以及9月份经验了两波回撤,格调切换和抱团瓦解是重要起因。量化基金两波回撤别离与茅指数和周期股的抱团瓦解无关。 4.市场交易构造变动:公募和私募交易活跃度晋升 公募基金等机构参加交易型行情比例减少,在不同市值股票分组内,机构专用参加龙虎榜交易比例均减少,均匀减少比例为3.2%。量化私募基金龙虎榜交易活跃度显著晋升,2020年以来,量化私募月度上榜次数和均匀上榜交易金额出现回升趋势。 5.量化成交奉献尚不足以撼动被动交易位置 依据情景假如下的剖析,量化私募成交占比约占全市场20%左右的量级。 6.量化私募监管政策与时俱进 在量化私募行业蓬勃发展的当下,监管政策亦在缓缓演进,以符合行业的倒退。从证监会主席易会满的会议讲话,到中基协和中证协对于量化私募交易数据上报的发文,监管政策的演进将有利于量化私募行业将来行稳致远。 7.公募FOF产品翻新提速 2021年以来,公募FOF产品一直减少新成员,从FOF-LOF到ETF-FOF,再到QDII-DOD-LOF,各种翻新产品的涌现,不仅是适应市场需求的后果,更是监管层大胆翻新的体系。 8.ETF赛道竞争白热化 ETF作为工具型产品,逐步受到投资者的宽泛欢送,存量规模亦显著晋升,各家基金公司纷纷入场布局。热门宽基指数、行业或主题ETF是各家基金公司布局的切入点,竞争激励。

December 14, 2021 · 1 min · jiezi

关于量化:如何用WonderTrader开发商品套利策略

前言 《WonderTrader架构详解》系列文章曾经写了四篇了,对于笔者这样的懒人来说,切实是一个大工程。这次先临时不介绍架构了,本文将给大家介绍一下如何在WonderTrader上编写一个跨种类套利策略,旨在给大家演示一下WonderTrader下多标的策略的个别写法,同时也介绍一下笔者在wtpy最新版本v0.6.3中最新公布的绩效剖析模块。 WonderTrader架构文章列表: WonderTrader架构详解之一——整体架构WonderTrader架构详解之二——从数据说起WonderTrader架构详解之三——信号与执行WonderTrader架构详解之四——浅谈平台对策略的反对套利策略简介 置信每一个做量化的人,都钻研过套利策略。套利策略以其逻辑的绝对简略、收益的绝对稳固而失去少数人的青眼。同时,套利策略和趋势策略有较强的互补性,这就意味着,如果配合切当,对平滑收益曲线,升高绩效的稳定,进步夏普有极大的促进作用。 套利策略的分类就十分多了,笔者曾在《WonderTrader架构详解之四——浅谈平台对策略的反对》文中援用了丁鹏博士的《量化投资——策略与技术》对于策略的分类,外面提到的套利的模式就有很多种,例如:期现套、跨期套、跨种类套、配对交易等等。 笔者为了开发该策略,查阅材料的时候,看到一个观点,深认为然:不论套利策略分类如许简约,套利策略的实质是在不同合约之间的价差稳定中寻找交易机会。 套利策略实现策略设计 鉴于笔者的量化策略的技能树,还没点亮,所以本文中的策略示例,是从互联网上学习到的,笔者在此由衷地感激此策略的分享者。该策略根本设计如下: 合约对抉择螺纹钢主力间断合约SHFE.rb.HOT和铁矿石主力间断合约DCE.i.HOT回测数据为主力间断数据,进行了复权解决,避免主力合约换月不同步的时候引起的价差变动基于1分钟数据,对过来N分钟两组收盘价序列进行线性回归,失去一个系数beta、一个常量c和一个残差序列对残差序列进行ADF测验,如果残差序列是一个安稳序列,则进入信号计算逻辑依据失去的系数beta和常量c,以及最新的价格计算新的残差当新的残差超出一个阈值,就触发入场信号,当价差再回归到阈值以内,就触发出场信号策略实现参数设计 依据上文的介绍,策略外围的参数如下: code1:套利合约1,本例设置为SHFE.rb.HOTcode2:套利合约2,本例设置为DCE.i.HOTperiod:数据周期,本例设置为m1,即一分钟线threshold:阈值,本例设置为0.9N:统计的K线条数,本例设置为360,即一个交易日的一分钟线bar_cnt:策略读取的K线条数,大于N,次要思考预留滚动计算的空间def __init__(self, name:str, code1:str, code2:str, bar_cnt:int, period:str, N:int, threshold:float=1): BaseCtaStrategy.__init__(self, name) self.__n__ = N self.__threshold__ = threshold self.__period__ = period self.__bar_cnt__ = bar_cnt self.__code_1__ = code1 self.__code_2__ = code2协整测验 策略外围的逻辑在于协整测验,只有协整测验通过了,能力进行信号触发。 # 协整检验函数def cointegration_check(series01, series02): # 对两个序列别离进行ADF测验 urt_1 = ts.adfuller(np.array(series01), 1)[1] urt_2 = ts.adfuller(np.array(series02), 1)[1] # 同时安稳或不安稳则差分再次测验 if (urt_1 > 0.1 and urt_2 > 0.1) or (urt_1 < 0.1 and urt_2 < 0.1): urt_diff_1 = ts.adfuller(np.diff(np.array(series01)), 1)[1] urt_diff_2 = ts.adfuller(np.diff(np.array(series02), 1))[1] # 同时差分安稳进行OLS回归的残差安稳测验 if urt_diff_1 < 0.1 and urt_diff_2 < 0.1: matrix = np.vstack([series02, np.ones(len(series02))]).T beta, c = np.linalg.lstsq(matrix, series01, rcond=None)[0] resid = series01 - beta * series02 - c # 最初对残差序列再进行ADF测验 if ts.adfuller(np.array(resid), 1)[1] > 0.1: result = False else: result = True return beta, c, resid, result else: result = False return 0.0, 0.0, 0.0, result else: result = False return 0.0, 0.0, 0.0, result信号收回 当残差超出上边界,阐明残差正向扩充,依据残差均值回归的个性,这时就须要做空价差;相同的,当残差超出下边界,阐明残差反向扩充,这时就须要做多价差;当加入在高低边界范畴之内,则清掉已有的头寸,期待下一次机会。 ...

May 9, 2021 · 2 min · jiezi

关于量化:WonderTrader架构详解之三信号与执行

前言 《WonderTrader架构详解》系列文章,上周介绍了WonderTrader的数据处理机制。当平台解决了策略的数据问题当前,就须要向策略提供稳固牢靠的信号执行机制,保障策略的信号被正确的执行,是每一个平台最根本的性能。因而,本文作为系列文章的第三篇,将次要介绍WonderTrader信号执行的解决机制。 往期文章列表: WonderTrader架构详解之一——整体架构WonderTrader架构详解之二——从数据说起1+N执行架构 WonderTrader设计了一种执行架构,该执行架构能够将一个策略组合的信号,散发到若干个执行通道执行,咱们称之为1+N执行架构。为什么会把信号执行设计成这样呢? 基于策略最简化的根本准则对于策略来说,最外围的工作是抓住赚钱的因子。然而交易的过程中,会波及到行情的接入和订单回报的解决。市面上有不少平台,抉择间接将这样的回报裸露给策略。在这类平台上,策略除了外围逻辑以外,还须要写很多反复的订单治理逻辑。有些平台甚至将底层接口的数据结构间接裸露给策略,这样做的后果是给策略引入了更多的复杂度,除了治理回报,还须要对不同的接口做数据的兼容解决。WonderTrader的一个设计指标就是是要给策略研发人员做减法,让他们将更多的精力放到策略自身,而不是如何和接口打交道。1+N执行架构,也正是基于这条根本准则。因为信号和执行彻底剥离,所以策略不须要关注任何回报,因为底层会将执行这块解决得很好,策略只须要关注本人的指标头寸和进出场的价格即可。这样就大大简化了策略的编写,只须要将信号计算的外围逻辑实现好就能够了。大规模产品治理的理论需要当咱们只有一个策略时,最简略的形式,就是把策略写成一个小型的平台,本人解决行情回报和交易回报,再针对特定的需要做一些性能扩大即可。而这种做法,容易把平台做成一个“玩具”,不具备治理大规模产品的根底,有很多量化交易框架都相似这种。对于大规模的产品治理,最根本的需要就是多个策略造成一个组合独特交易,而一个策略组合又同时在多个产品进行交易。而不同资金规模的产品,在收回交易指令的时候,所须要的执行算法也是不一样的。比方买入100手和买入1手某期货合约,显然就不能用齐全一样的形式下单。1+N执行架构很重要的一方面就是针对大规模产品治理的需要。雷同的策略组合,在不同的交易通道下,会依据产品的资金规模和危险偏好配置不同的交易数量放大倍数。而交易数量的不同,有须要针对不同的交易通道配置不同的执行算法,这样能力让各个产品都能取得更好的执行价格。策略逻辑和执行的重复衡量1+N执行架构下,须要把策略分为两类策略:重逻辑轻操作的策略和轻逻辑重操作的策略。大多数趋势类策略都属于前者,而简直所有的高频策略都属于后者。重逻辑轻操作的策略,最显著的特点就是信号呈现时必须要执行到位。这类策略,还体现出频率较低(绝对高频而言),对滑点敏感度低等特点。而这类策略就是1+N执行架构次要服务的策略,这类策略也很容易通过1+N执行架构扩大到更多的交易通道。绝对的,轻逻辑重操作的策略,最显著的特点就是信号呈现时,要看执行的状况,能力决定下一步如何应答。这类策略最常见的就是套利策略和高频策略,它们须要不停地挂单试探盘口的状况,能力做出更无利的决策。显然1+N执行架构无奈满足这类策略,所以WonderTrader专门针对这类策略提供了HFT引擎,该引擎将交易接口和交易回报间接裸露给策略,这样策略就能依据回报执行治理订单。投研人员的编码程度的考量在量化畛域中,对从业人员的成分划分,有一个很乏味的分类办法:P-Quant和Q-Quant。P-Quant的独特属性是这些从业人员根本都有软件开发的技术背景,领有绝对较好的编码能力。而Q-Quant则相同,个别没有技术背景,编码能力绝对较弱,然而在策略建模、金融工程方面绝对较强。因为两类从业人员的背景和能力树的不同,所以他们对于平台的需要也不大一样。P-Quant编码能力绝对较强,偏向于平台提供外围的根底性能即可,他们能够自行开发本人须要的各种性能,同时心愿平台可能尽可能低延时(P-Quant从事高频策略开发的比例更高);Q-quant编码能力绝对较弱,则偏向于平台提供更欠缺的基础设施和策略开发API,绝对的,他们对延时不那么敏感(当然如果平台可能做到更低延时,对他们同样无利),然而心愿平台反对的利用场景更丰盛一些(要满足不同类型的策略的回测、实盘等需要)。 鉴于以上提到的几个方面,WonderTrader最终决定针对CTA策略,基于信号和执行的剥离,采纳1+N执行架构进行实盘交易。而针对HFT策略,则抉择将交易接口间接裸露给策略(不同交易接口的差别被封装到交易模块中,策略解决的交易接口是无差别的),让策略自行治理订单,转而在数据需要上提供反对。这样就能兼顾不同类型的策略的不同的利用场景,让不同类型的策略都能在WonderTrader中能够更高效的治理起来。 如何同步信号 对于CTA引擎,既然采纳1+N执行架构,就意味着平台须要治理两套头寸,一套是策略的实践头寸,一套是交易通道的理论头寸。在实盘运行中,咱们须要对两套头寸别离进行治理。 实践头寸的治理实践头寸的治理,也分为两个层面。第一个是策略层面,即每个策略有本人的实践头寸,包含进出场的工夫、价格、方向、数量等。策略能够治理的也只能是本人的实践部位,每个策略都是相互隔离的。持有的头寸,再实时计算浮动盈亏,并核算最大潜在浮盈和最大潜在浮亏等数据。回测的时候,数据都在内存中,不须要实时保留。而实盘环境下,要思考策略重启等各种状况,所以数据须要实时保留到文件中。第二个是组合层面,即策略的指标头寸整合到一起当前,造成一个净头寸。这个净头寸实质上和策略的实践头寸是统一的,只不过是轧平过相同的头寸当前,归属于策略组合治理。策略组合也要实时计算浮动盈亏,同时依据预设的资金规模,计算资金危险指标,进行实时风控。实盘环境下,数据也要实时保留到文件中(回测引擎针对单策略,所以不存在策略组合的概念)。理论头寸的解决策略组合的头寸是最终散发到各个交易通道的根底指标头寸。每个交易通道,会依据所对应产品的资金规模和危险需要别离配置本人的放大倍数,根底为1倍,反对小数倍数,会主动做四舍五入的解决。每个执行通道确定了本人的理论指标头寸当前,跟本人以后理论持有的头寸进行比拟,将有差异的头寸,依据预设的执行算法(WonderTrader内置的是以最新价、最优价和对手价三个价格作为根底价格,再加上一个滑价跳数作为限价,基本上满足了绝大部分资金规模无限的策略需要)收回下单指令。并依据订单回报和成交回报实时更新持仓状态和订单状态。滑点如何解决正如后面所说,不同资金规模的策略,最终成交的价格是不同的,更何况不同的交易通道还是依赖雷同的信号进行交易的,并发执行的前提下,不同交易通道的订单最终也会在交易所进行竞争。这就造成一个主观的事实:雷同策略在不同产品中的实盘体现肯定是有差别的,而差别的起源就是滑点。回测的时候,咱们个别用小手数进行回测,所以个别状况下都不思考滑点。然而实盘环境下,滑点问题就不可避免。这也是咱们内置的执行算法,都是以限价单下单的根本原因。个别状况下,笔者倡议放大倍数大的交易通道,执行算法中预设的滑价能够绝对放大倍数小的交易通道设置得更大一些(最终要依据流动性判断)。这样即便产生滑点,也在能够承受的范畴之内。如果应用市价单收回下单指令,极其行情下,最终产生的滑点可能会压缩掉全副的利润。1+N执行架构的意义 后面根本介绍了1+N执行架构基本原理。从WonderTrader的角度来说,所谓的1+N其实也有两层意思:N个策略在1个组合中独特运行,而1个组合又同时在N个交易通道进行交易。所以一开始的时候笔者无意称之为N+1+N执行架构。显然,不论从哪方面来说,1+N执行架构的意义是不凡的: 策略和执行剥离,对研发人员更敌对,团队治理更高效投研人员的技术栈其实是策略coding的外围问题,咱们抉择一个平台的时候,应该尽量的缩小技术栈的引入。而个别交易接口都是异步回调的,如果强行要策略投研人员深刻理解这类纯技术技能,从团队治理的角度来说,是低效的。同时对于投研人员来说,花太多的精力到本身技能树的分支上,也不是一个聪慧的抉择,更何况技术自身也并不那么容易。所以策略和执行的剥离,对大部分投研人员,尤其是Q-Quants来说,是一种十分敌对的设计。这样的设计当然也不是WonderTrader独创的,笔者也从共事的理论需要中和其余的量化平台排汇了一些教训。净头寸执行,不存在自成交危险对于策略来说,风控是必不可少的,同样策略组合的风控也是平台的重点之一。风控中,除了资金风控以外,还有一个合规风控。绩效回撤了,能够再赚,然而如果合规风控过线了,可能会引起十分大的麻烦。其中最重要的合规风控点就是自成交。自成交次数过多,手数过大,很容易被认定为对敲操控市场,相应的处罚也是十分严格的。WonderTrader须要解决多策略在多产品经营的问题,首要思考的就是合规性的问题。如果两个策略同时收回相同的交易信号,并且散发到多个产品同时下单,自成交的危险就会被放大。所以WonderTrader通过在策略组合中轧平相同头寸,最终以净头寸的形式收回下单指令。除了人造防止自成交的问题,同时还能节俭佣金、缩小保证金的占用。策略组合外部,策略有本人的实践头寸,不受组合头寸的影响。同一个策略组合的多产品,能够保障信号的一致性笔者此前,常常会被投资人问:你们怎么保障各个产品绩效的一致性的?笔者没有太多去钻研别家是怎么解决的,然而对于WonderTrader的1+N执行架构来说,这样的问题就显得太简略了。即便两个规模一样的产品,用同一套策略交易,绩效也不可能齐全一样。因为即便同时下单,也受到交易通道速度的影响。然而不能保障绩效的齐全一致性,保障信号的齐全一致性却是没有问题的。满足大规模治理产品的需要业余投资机构对于交易平台的需要,和集体投资者还是有很大差异的。最外围的一点就是大规模的产品如何治理的问题。例如某些头部私募,发了数百只产品,假如只有一百只产品须要实际操作,而每只产品都须要交易股票、期货、期权等标的。如果不从架构上解决这样的需要,对于这些机构的IT来说,这基本上是一个劫难:在不同的服务器上部署策略运行环境,每天还要监控各台服务器在收盘前是否失常运行。甚至有些柜台要到收盘前才会启动,这就使得运维人员的查看工夫窗口只能限定在半个小时甚至更短的工夫范畴内。WonderTrader的需要就从大规模产品治理中来,一开始的设计指标也是要满足大规模产品治理的需要。而1+N执行架构正是针对这样的需要的外围机制。结束语 本文对WonderTrader的信号执行机制的介绍就到此结束了,置信通过本文,各位读者可能对WonderTrader的1+N执行架构有一个更深刻的理解,也心愿本文可能在各位读者在遇到相似问题的的时候,对大家有所启发。笔者程度无限,不免有错漏之处,还请各位朋友多多包涵斧正。下一篇,笔者将围绕平台如何兼容不同的策略,来介绍WonderTrader不同的策略引擎的底层逻辑,望各位读者届时多多捧场。 最初再安利一下WonderTrader WonderTrader旨在给各位量化从业人员提供更好的轮子,将技术相干的货色都封装在平台中,力求给策略研发带来更好的策略开发体验。 WonderTrader的github地址:https://github.com/wondertrad... WonderTrader官网地址:https://wondertrader.github.io wtpy的github地址:https://github.com/wondertrad... 市场有危险,投资需谨慎。以上陈说仅作为对于历史事件的回顾,不代表对将来的观点,同时不作为任何投资倡议。

March 29, 2021 · 1 min · jiezi

关于量化:WonderTrader新年寄语

WonderTrader现状2020年,能够说是WonderTrader的开源元年。WonderTrader正式开源是2020年4月开始的,现在进入2021年了,WonderTrader的开源历史也不过是8个月的工夫。平台在2020年中迭代并不是特地地快,一方面笔者还有本职工作要做,另外一方面平台也没有真正做过推广,目前关注的敌人都是天然增长的,所以需求面来得也并不充沛。不过无论是什么状况,笔者既然决定开源了,肯定会逐渐的欠缺WonderTrader,给更多的用户提供便当。 WonderTrader的倒退笔者在证券行业的从业经验,能够追溯到2009年。过后笔者刚到上海,有幸进到当今业内出名的股票极速柜台的负责人H总过后的技术团队,参加股票软件的开发,从而进入到证券行业。H总当初团队中,还有不少当年的小伙伴。笔者过后次要负责客户端的开发,技术栈为MFC。起初随着笔者参加的我的项目变多,缓缓积攒了不少开发教训,负责的工作也从客户端逐步拓展到其余方向,笔者也缓缓积攒了不少技术和业务方面的常识。笔者在量化平台方面的起步,源自于在H总团队期间参加的一位校友师兄的高频交易的技术框架搭建的我的项目,笔者一开始负责的工作也绝对比较简单。起初笔者来到H总团队当前,跟另外一位校友学姐来到从业生涯的第一家私募,从此正式开始了量化平台的搭建工作。最早的量化平台通过几年的打磨当前,逐步成熟起来,笔者也积攒了更多的教训。起初又追随学姐进入到另外一家量化私募基金(该量化私募现已是头部),在这家私募也失去了很多的机会和锤炼。起初笔者来到该私募跟敌人一起守业,次要做的是小型的交易所零碎(蕴含了撮合和柜台的一体化零碎)。守业尽管最终没有胜利,然而守业的经验也让笔者在量化交易平台的技术积攒夯实了很多。最初笔者来到守业公司,又回到私募基金,又彻底重构了原来的平台,从而有了当下的WonderTrader。笔者的从业经验,也是WonderTrader的倒退经验的由来。WonderTrader最早的雏形,是针对高频策略的,简略来说就是:间接对接交易接口和行情接口,其余的都由策略来管制。起初笔者在第一家私募的时候,公司次要采纳MC作为策略研发和交易平台,交易信号也是各个策略相互独立的。为了方便管理每个策略,笔者基于原来的开发教训,开始了整个平台的搭建。平台次要针对外部策略提供反对,包含数据服务、交易对接、零碎风控等性能。起初笔者来到某头部私募时,该私募并不应用MC。所以平台这时也经验了一次重要的重构,外围的内容就是针对策略逻辑提供框架反对,也就是说仿写一套类MC的策略API。然而因为平台之前始终采纳C/S构造提供服务,所以会引入不少网络提早,因而过后的版本其实并不适用于高频交易。当笔者最初从守业公司打磨几年当前,从新回到私募行业,开始从新反思原有平台的问题。重复斟酌思量当前,笔者决定彻底重构平台,而后才有了明天的WonderTrader。所以WonderTrader有成熟稳固的数据存储计划,也有高效欠缺的风控机制,还有简略易用的策略API接口,更有高速灵便的执行模块。总结起来,WonderTrader的倒退,共经验了出具雏形、针对MC面向策略的C/S架构、策略局部迁徙到Python、放弃C/S架构彻底重构等四个阶段。 WonderTrader的指标开源当前,曾经有不少人问过笔者为什么要开源了。不论WonderTrader推广的状况如何,平台的价值始终是有的:毕竟是经验过实盘测验,并且达到肯定资金规模的平台!而且WonderTrader因为采纳C++作为底层开发语言,执行效率上绝对更有保障一些。而且WonderTrader开源是十分彻底的,除了笔者不确定市面上是否能够通过公开渠道拿到的API,其余的全副都开源进去了。当然公司外部应用的某些功能模块也不在开源之列,然而笔者也提供了可用的繁难版本。WonderTrader的开源的初衷,能够从两个方面来说:一个比拟雅致,一个比拟有情怀!比拟雅致的一方面是,笔者这些年在WonderTrader也花了不少精力,笔者自认为WonderTrader还是很有价值的。然而国内一家私募基金的规模能有100人就算不错了,这其中还有市场和产品,投研人员最多也就占一半左右。即便全副的人都应用WonderTrader,需要还是十分无限的。笔者心愿WonderTrader可能被更多的人认可和应用,然而笔者毕竟精力有限,没方法像业余的平台公司一样,投入大量的人力物力做推广,更没方法7×24小时做技术支持,所以开源就成为了一个很好的计划。开源当前,一方面WonderTrader能够有机会给更多的人应用,排汇更多的需要一直地欠缺;另一方面笔者也有机会意识更多的同路人,对笔者来说也是一个能够学习更多常识的切入点。比拟有情怀的一方面是,笔者从业这么多年,待过几家私募基金,发现每家私募的技术计划都齐全不一样,有些甚至没有技术平台。而不少做量化技术开发的,简直都有本人从零搭建造轮子的经验。很多策略研发人员,还须要每天钻研技术细节,思考各种回报的解决细节,这是笔者最难以了解的。WonderTrader始终有一个指标,就是要将策略研发人员从技术细节中解放出来!WonderTrader的架构,简直都是围绕这个指标开展的!笔者始终有一个认知:一个好的量化平台,就应该让研发专一策略,让开发专一技术。WonderTrader采纳的低耦合的模块化架构、以及信号和执行的拆散的机制都是围绕该指标来做的。概括来说,WonderTrader开源的初衷和指标就是:一、让更多的人应用WonderTrader,让更多的人多有一个不错的抉择计划;二、让策略研发和技术开发各司其职,从侧面放慢策略的迭代速度,让这个市场更无效。 WonderTrader在2021年的打算WonderTrader经验了这么多年的倒退和迭代,其实曾经绝对比较稳定了。在2021年WonderTrader的大抵打算如下: 持续欠缺平台性能持续优化平台局部功能模块的性能欠缺应用文档底层外围优化,逐渐加强平台对高频策略的适应度提供更多易用性性能组件提供更多的demo结束语最初祝福大家在新的2021年,不论市场牛熊如何,策略都能大赚!最初再来一波广告WonderTrader的github地址:https://github.com/WonderTrad...WonderTrader官网地址:https://WonderTrader.github.iowtpy的github地址:https://github.com/WonderTrad...

January 1, 2021 · 1 min · jiezi

kdb和jupyter的配置

本文主要介绍: 配置kdb和Python交互的jupyter-notebook环境:使用pyqpyq的安装与使用1. pyq的jupyter环境配置由于本文使用的是32bit免费版本的kdb+(从官网下载windows.zip),所以Python也需要是32位版本的。 环境要求: 安装必须使用windows的cmd环境Python须为3.6版本(如不是,需要创建虚拟环境,下面会介绍)1.1 Python3.6虚拟环境的配置 如果默认已经是python3.6的32位版本,则本节可以跳过。 否则,在windows下,使用conda命令创建32位的虚拟环境: set CONDA_FORCE_32BIT=1 conda create -n py36_32 python=3.6 这样会在C:\Anaconda3\envs下生成一个py36_32的虚拟环境,使用如下命令可以激活该环境: activate py36_32可以看到在激活之后,python变为32位的3.6版本:pip版本: 接下来的步骤都要在此虚拟环境下进行。 1.2 Jupyter环境的配置 接下来,严格按照Install Jupyter Kernel上的步骤: 这里前两步已经不用再做了;%VIRTUAL_ENV%就是1.1中创建的python虚拟环境的位置,即C:\Anaconda3\envs\py36_32;第4步,将从官网下载windows.zip解压到%VIRTUAL_ENV%目录下;剩下的按步骤进行。在这里配置环境变量QHOME和QBIN: QHOME: %VIRTUAL_ENV%\qQBIN: %QHOME%\w32\q.exe 以及Path中添加: C:\Anaconda3\envs\py36_32\q\w32C:\Anaconda3\envs\py36_32\q全部完成之后,执行(仍需在虚拟环境下): pip install jupyter pyq pyq-kernel再执行 %QBIN% python.q -mpyq.kernel install即安装完毕。完成之后,执行下面代码可以检验是否安装成功: >jupyter kernelspec listAvailable kernels: pyq_3 C:\Users\a\AppData\Roaming\jupyter\kernels\pyq_3 python3 c:\users\a\py36\share\jupyter\kernels\python32. PYQ的配置依然是在python3.6 32位的虚拟环境下,执行: pip install -U pyq在虚拟环境(python3.6 32bit)下启动pyq,否则会报错找不到python.dll: q python.q 之后便可以使用pyq的功能,具体使用不再介绍,移步pyq user guide。 3. Jupyter 展示先激活虚拟环境: activate py36_32启动jupyter: jupyter notebook在new下选取PyQ3即可: ...

May 28, 2019 · 1 min · jiezi

8. Q语言学习之路—表

概述表(Tables)和列表、字典一样,都是q语言中的第一类对象(First-class entity)Q表是列导向的Q表是从字典构建的1.表的定义1. 表作为列字典q)dc:nameiq!(DentBeeblebroxPrefect;98 42 126)q)dc[iq;]98 42 126q)dc[;2]name| Prefectiq | 126q)dc[iq; 2]126我们通过flip指令来得到一个表q)t:flip nameiq!(DentBeeblebroxPrefect;98 42 126)q)t[;iq]98 42 126q)t[2;]name| Prefectiq | 126q)t[2;iq]126所有表的类型都是98h。类似于python的pandas包,可以通过t[column]或者t.column来获取一个列。但请注意,点操作符在函数内不起作用。q)t[nameiq]Dent Beeblebrox Prefect98 42 1262. 表的显示q)dcname| Dent Beeblebrox Prefectiq | 98 42 126q)tname iq --------------Dent 98 Beeblebrox 42 Prefect 1263. 表定义语法定义语法为([] c1:L1;...;cn:Ln)其中ci是列名,Li是对应的列值。其中的冒号不是必须的。q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)q)tflip nameiq!(DentBeeblebroxPrefect;98 42 126)1b表定义中也可以直接使用变量,这样对应的列名就是变量名了q)c1:DentBeeblebroxPrefectq)c2:98 42 126q)([] c1; c2)c1 c2————–Dent 98Beeblebrox 42Prefect 126atom元素会自动拓展到列长度q)([] c1:abc; c2:42; c3:98.6)c1 c2 c3 ----------a 42 98.6b 42 98.6c 42 98.64. 表的元数据可以通过cols指令来获取表的列;可以通过meta来获取表的元数据,结果是一个键表(keyed-table)。q)meta tc | t f a----| -----name| s iq | j其中t代表了列的类型,f表示了是否存在任何外键。指令tables返回当前命名空间下的表名, \a效果相同:q)t2:([] c1:1 2 3; c2:(1 2; enlist 3; 4 5 6))q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)q)tables .tt25. 记录表在逻辑上是一个字典记录(record)的列表,count返回记录的个数。q)count t3为了获取表中的不带列名的行,可以使用value函数q)value t[1]Beeblebrox426. 转置的列字典 vs 记录的列表逻辑上,表既可以是转置的列字典(Flipped column dictionary),也可以是记录的列表(list of records),但是物理上它被储存为列字典。事实上,Q语言会自动地识别满足表形式的列字典,在没有任何请求询问下,将其转换为表形式。例如下两例:q)type (nameiq!(Dent;98); nomeiq!(Beeblebrox;42))0hq)type (nameiq!(Dent;98); nameiq!(Beeblebrox;42))98h表被储存为列形式的一个坏处是,当想要删除掉表中的一行时,花销非常大。The best way to deal with this in large tables is not to do it.与其删除一行,不如用一个表示flag的column来表示该行有没有被删除。2. 空表和模式创建一般空表的方式:q)([] name:(); iq:())推荐的做法是给每列指定类型q)([] name:symbol$(); iq:int$())一种等价的创建带类型的表的方法:q)([] name:0#; iq:0#0)q)([] name:0#; iq:0#0) ([] name:symbol$(); iq:int$())1b3. 基本的select和update操作下面以这个表的操作为例q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)1. select的语法 基本语法为:select cols from table 取全部列直接省略掉cols即可,不用加*。2. 结果显示select的结果永远是一个表。q)select from tname iq --------------Dent 98 Beeblebrox 42 Prefect 1263. 选择列q)select name from tname----------DentBeeblebroxPrefectq)select c1:name, c2:iq from t4. 基本的updateupdate的语法与select的语法基本相同,其使用冒号右边的值来取代该列。q)update iq:iq%100 from tname iq---------------Dent 0.98Beeblebrox 0.42Prefect 1.264. 主键和键表在SQL中,可以声明一个或多个列作为主键,主键的值是unique的,使得通过主键的值来提取行成为可能。1. 键表定义:A keyed table is a dictionary mapping a table of key records to a table of value records. 一个键表并不是一个表——它是一个字典,所以类型是99h。2. 简单的例子值的表(table of value):q)v:flip nameiq!(DentBeeblebroxPrefect;98 42 126)键的表(table of key):q)k:flip (enlist eid)!enlist 1001 1002 1003创建键表:q)kt:k!vq)kteid | name iq ----| --------------1001| Dent 98 1002| Beeblebrox 42 1003| Prefect 1263. 键表的定义语法下面是通过字典形式对键表定义的常规语法:q)kt:(flip (enlist eid)!enlist 1001 1002 1003)! flip nameiq!(DentBeeblebroxPrefect;98 42 126)q)kteid | name iq----| --------------1001| Dent 981002| Beeblebrox 421003| Prefect 126直接使用表定义语法将更为简单:q)kt:([eid:1001 1002 1003] name:DentBeebleBroxPrefect; iq:98 42 126)带类型的空键表的定义形式如下:q)ktempty:([eid:int$()] symbol$name:(); iq:int$())q)ktemptyeid| name iq---| -------q)ktempty:([eid:0#0] name:0#;iq:0#0)4. 获取键表的记录因为键表本质上是一个字典映射,所以可以通过键值对记录进行查找。注意键和值都对应的是字典。q)kt[(enlist eid)!enlist 1002]name| Beeblebroxiq | 42上例可以简化为:q)kt[1002]name| Beeblebroxiq | 42我们可以获取某个列的值q)kt[1002][iq]42q)kt[1002;iq]425. 取多行的记录 q)kt[1001]name| Dentiq | 98q)kt 1001name| Dentiq | 98为了取多行的数据,通过如下的方式去取是错误的q)kt[1001 1002]'length正确的方式是:q)kt[(enlist 1001;enlist 1002)]name iq-------------Dent 98Beeblebrox 42q)kt[flip enlist 1001 1002]也可以通过构建一个匿名的表来取值q)kt ([] eid:1001 1002)name iq-------------Dent 98Beeblebrox 42回想第5.2.2节指出的,可以通过命令#来提取一个子字典,对于键表同理:q)([] eid:1001 1002)#kteid | name iq----| -------------1001| Dent 981002| Beeblebrox 426. 反向查找由于键表是一个字典,所以我们通过值来反响查找键。q)kts:([eid:1001 1002 1003] name:DentBeeblebroxPrefect)q)ktseid | name—-| ———-1001| Dent1002| Beeblebrox1003| Prefect反向查找:q)kts?([] name:PrefectDent)eid—-100310017. 键表的成分可以通过key和value来提取键表成分q)key kteid—-100110021003q)value ktname iq————–Dent 98Beeblebrox 42Prefect 126函数keys和cols返回键表的键名和列名。8. 表和键表通过xkey来设置一列为键q)t:([] eid:1001 1002 1003; name:DentBeeblebroxPrefect; iq:98 42 126)q)eid xkey teid | name iq —-| ————–1001| Dent 98 1002| Beeblebrox 42 1003| Prefect 126在xkey左运算元设置为空列表时转换为常规表q)kt:([eid:1001 1002 1003] name:DentBeeblebroxPrefect; iq:98 42 126)q)() xkey kteid name iq -------------------1001 Dent 98 1002 Beeblebrox 42 1003 Prefect 126也可以使用!的又一重载用法:左运算元为一个非负的数字,代表左侧将包含在键中的列数;右运算元为表或者键表。0代表没有键。q)1!tq)0!kteid name iq -------------------1001 Dent 98 1002 Beeblebrox 42 1003 Prefect 126q)2!0!kteid name | iq---------------| ---1001 Dent | 981002 Beeblebrox| 421003 Prefect | 126上述返回的结果都是在表的拷贝上进行的,如果要对表进行in-place的操作,则需要使用call-by-nameq)eid xkey ttq)() xkey ktktq)kteid name iq ——————-1001 Dent 98 1002 Beeblebrox 42 1003 Prefect 1269. 复合的主键 q)ktc:([lname:DentBeeblebroxPrefect; fname:ArthurZaphodFord]; iq:98 42 126)q)ktclname fname | iq —————–| —Dent Arthur| 98 Beeblebrox Zaphod| 42 Prefect Ford | 126复合主键的查找可以通过一个复合的键:q)ktc[lnamefname!BeeblebroxZaphod]iq| 42对于一个简单的键,我们可以简化查找:q)ktc[DentArthur]iq| 98空的多键值表的创建:q)ktc:([lname:symbol$();fname:symbol$()] iq:int$())q)ktc:([lname:0#;fname:0#] iq:0#0)10. 提取复合键可以通过复合键的列表来提取对应的记录q)ktc (DentArthur;PrefectFord)iq---98126当然也可以通过匿名表来提取q)ktc ([] lname:DentPrefect; fname:ArthurFord)当然也可以用#来提取子字典q)K:([] lname:DentPrefect; fname:ArthurFord)q)K#ktclname fname | iq --------------| ---Dent Arthur| 98 Prefect Ford | 12611. 提取列数据在这一小节中我们使用如下两个例子:q)kts:([k:101 102 103] v1:abc; v2:1.1 2.2 3.3)q)kts_q)ktc:([k1:abc;k2:xyz] v1:abc; v2:1.1 2.2 3.3)q)ktc_提取特定列的做法:q)kts[([] k:101 103)][v1]acq)ktc[([] k1:ac;k2:xz)][v1v2]也可以使用index at depth的做法q)kts[([] k:101 103); v1]_q)ktc[([] k1:ac;k2:xz); v1v2]_5. 外键和虚拟列外键是指表中的一列,该列的数据都在另一个表中的主键中。1. 外键的定义定义:A foreign key is one or more table columns whose values are defined as an enumeration over the key column(s) of a keyed table. 2, 外键的例子以下表为例q)kt:([eid:1001 1002 1003] name:DentBeeblebroxPrefect; iq:98 42 126)以eid列为外键的表定义如下,这里用到了枚举,强制保证了该列出现的值都在另一个表的键值中q)tdetails:([] eid:kt$1003 1001 1002 1001 1002 1001; sc:126 36 92 39 98 42)在meta命令中,可以看到外键出现在f列中:q)meta tdetailsc | t f a---| ------eid| j kt sc | j 内置函数fkeys返回一个表示外键的字典q)fkeys tdetailseid| kt3. 清除外键当想清楚一个外键时,对枚举的列使用value函数q)meta update value eid from tdetailsc | t f a---| -----eid| j sc | j 4. 外键和关系设tf是一个带有键表kt外键f的表,为了获取kt中的某一列c,可以直接在select语句中直接使用点操作符f.c即可。q)select eid.name, sc from tdetailsname sc --------------Prefect 126Dent 36 Beeblebrox 92 Dent 39 Beeblebrox 98 Dent 42 6. 使用表和键表在这一节中,我们使用如下例子q)t:([] name:DentBeeblebroxPrefect; iq:98 42 126)q)kt:([eid:1001 1002 1003] name:DentBeeblebroxPrefect; iq:98 42 126)1. Append记录使用,:,带列名时可以不同考虑顺序q)t,:nameiq!(W; 26)q)t,:iqname!(200; Albert)q)tname iq--------------Dent 98Beeblebrox 42Prefect 126Albert 200不带列名时必须考虑顺序q)t,:(H; 142)_q)t,:(97;J)'typeq)t_2. First和Last记录使用函数first和lastq)first tname| Dentiq | 98q)last tname| Albertiq | 200使用#获取前后几行记录q)2#tq)-3#kt3. Findfind操作符?返回记录的索引q)t?nameiq!(Dent;98)0上式可简化为q)t?(Dent;98)0也可以查多列q)t?((Dent;98);(Prefect;126))0 2由于键表是一个字典,所以使用?相当于执行了反向查找,返回对应的键值q)kt?nameiq!(Dent;98)eid| 1001q)kt?(Dent;98)eid| 1001单个列的查找必须生成列值,或者使用匿名表的结构q)t1:([] eid:1001 1002 1003)q)t1?enlist each 1001 10020 1q)t1?([] eid:1001 1002)0 14. Union with,使用join操作符,追加记录到一个表中,但是不会执行类型检查。q)t,nameiq!(Slaartibartfast; 123)name iq --------------------Dent 98 Beeblebrox 42 Prefect 126 Slaartibartfast 123只有表有完全一样的meta结果时,两个表才能合并成一个表。q)t,([] name:1#W; iq:1#26)q)t,t当键表使用,合并两个meta结果相同的键表时,右边的键表将upsert(update and insert)左边的键表,不同的键将append,相同的键将update。q)kt,([eid:1003 1004] name:PrefectW; iq:150 26)eid | name iq ----| --------------1001| Dent 98 1002| Beeblebrox 42 1003| Prefect 1501004| W 26 5. Coalesce ^Coalesce^被用来合并两个具有相同列的键表。^的效果与,的相同,除了这种情况:两个键表相同键合并时,右边的键表若为null,则左边的保留下来。q)([k:abc] v:10 0N 30)^([k:abc] v:100 200 0N)k| v-| ---a| 100b| 200c| 30q)([k:abcx] v:10 0N 30 40)^([k:abcy]; v:100 200 0N 0N)k| v-| ---a| 100b| 200c| 30x| 40y|^的执行效率没有,的高,因为右运算元需要检查是否为null。6. 列 Join两个具有相同数量记录的表可以使用join-each(,')来合并列。q)([] c1:abc),’([] c2:100 200 300)c1 c2 ——a 100b 200c 300在键表上的列join:q)([k:1 2 3] v1:10 20 30),’([k:3 4 5] v2:1000 2000 3000)k| v1 v2 -| ——-1| 10 2| 20 3| 30 10004| 20005| 30007. 复杂列数据1. 简单的例子在如下表中,lh列中储存了一个嵌套的列表q)tp:([] d:2015.01.01 2015.01.02; lh:(67.9 82.10; 72.8 88.4))q)tp 0d | 2015.01.01lh| 67.9 82.1q)tp lh67.9 82.172.8 88.42. 复合列数据的操作复合列数据的定义:嵌套列中的元素都是简单列表。q)tm:([] wk:2015.01.01 2015.01.08; rv:(38.92 67.34; 16.99 5.14 128.23 31.69))q)tmwk rv ----------------------------------2015.01.01 38.92 67.34 2015.01.08 16.99 5.14 128.23 31.69Nested columns mean adverbs. Lots of adverbs.q)select wk, srt:desc each rv, avgr:avg each rv, hi:max each rv from tmwk srt avgr hi -------------------------------------------------2015.01.01 67.34 38.92 53.13 67.34 2015.01.08 128.23 31.69 16.99 5.14 45.5125 128.23q)select wk, drp:neg 1_'deltas each desc each rv from tmwk drp ---------------------------2015.01.01 ,28.42 2015.01.08 96.54 14.7 11.853. 复合外键复合主键的键表:q)ktc:([lname: DentBeeblebroxPrefect; fname:ArthurZaphodFord]; iq:98 42 126)有ktc外键的表:q)tdetails:([] name:ktc$(BeeblebroxZaphod;PrefectFord;BeeblebroxZaphod); sc:36 126 42)ktc的列就可以作为tdetails的虚拟列了q)select name.lname, name.iq, sc from tdetailslname iq sc ——————Beeblebrox 42 36 Prefect 126 126Beeblebrox 42 42 8. 属性Attributes are metadata that you attach to lists of special forms. They are also used on a dictionary domain or a table column to speed retrieval for some operations.属性是描述性的,当你指定表中某一列具有某种属性时,q语言会去检查确认,但并不会帮你完成这个操作。Kx says not to expect significant benefit from an attribute for fewer than a million items.1. Sorted s# 当某一列被指定为sorted的时候,线性搜索就会被替换为二分查找,查找速度更快。当属性被成功应用于列表时,它就变成了列表的一部分,并且q会检查这个列表是否满足这一属性。q)s#1 2 4 8s#1 2 4 8q)s#2 1 3 4’s-fail排序函数asc会自动在其结果上应用sorted属性,但是til不会q)asc 2 1 8 4s#1 2 4 8q)til 50 1 2 3 4q)L:s#1 2 3 4 5q)L,:6q)Ls#1 2 3 4 5 6q)L,:0q)L1 2 3 4 5 6 0对字典应用sorted属性,会在应用到其键值上,其查找算法就会被替代为二分查找。q)d:s#10 20 30 40 50!abcdeq)key ds#10 20 30 40 50q)d 10aq)d 12aq)d 15aq)d 20b2. Unique u# q)u#2 1 4 8u#2 1 4 8q)u#2 1 4 8 2’u-failUnique属性的amend操作:q)L:u#2 1 4 8q)L,:3q)Lu#2 1 4 8 3q)L,:2q)L2 1 4 8 3 2unique属性可以作用于字典的域,表的一列或者键表的键。但是不能作用于一个字典,表或者键表。3. Parted p# Parted属性 p#表明对所有出现的值,相同的值都是互相邻近的。q)p#2 2 2 1 1 4 4 4 4 3 3p#2 2 2 1 1 4 4 4 4 3 3q)p#2 2 2 1 1 4 4 4 4 3 3 2'u-fail [0] p#2 2 2 1 1 4 4 4 4 3 3 2parted属性在任何list的操作上都不会被保留下来,即使折耳根操作保留了这个属性。q)L:p#1 1 2 3 3q)Lp#1 1 2 3 3q)L,:3q)L1 1 2 3 3 34. Grouped g# 可以作用在任何list上。It causes q to create and maintain an index – essentially a hash table.q)g#1 2 3 2 3 4 3 4 5 2 3 4 5 4 3 5 6q)L:g#100?100q)L,:1 1 1 15. 去除属性 # 通过指令 # 可以去除列表上的任何属性:q)L:s#til 10q)Ls#0 1 2 3 4 5 6 7 8 9q)#L0 1 2 3 4 5 6 7 8 9Section End.

April 11, 2019 · 5 min · jiezi

7. Q语言学习之路—数据转换

类型1. 基础数据类型2. type操作符type操作符返回一个short,并且如果对象是一个atom,则返回负数;是一个list则返回正数。q)type 42-7hq)type 10 20 307hq)type 98.6-9hq)type 1.1 2.2 3.39hq)type a-11hq)type abc11hq)type “z”-10hq)type “abc"10h任何常规列表(general list)的类型都是0h。q)type (42h; 42i; 42j)0hq)type (1 2 3; 10 20 30)0hq)type ()0h任何字典(包括键表)的类型,都是99h。q)type (abc!10 20 30)99hq)type ([k:abc] v:10 20 30)99h任何表的类型都是98h。q)type ([] c1:abc; c2:10 20 30)98h3. 变量的类型q语言是动态类型语言,所以变量的类型也会随着其被赋的值而改变。全局变量会被储存在一个一般的q字典中。可以通过命令get . 来查看全局变量和对应的值,也可以通过操作符value来查看q)value .q)a:42q)value .a| 42q)f:{xx}q)value .a| 42f| {x*x}..2. Cast转换使用dyadic操作符$,右边的运算元是原始值,而左边的运算元是目标类型。有三种方式指定目标类型:数值的short类型char类型值类型的symbol名称1. Casts that Widen因为目标类型比原始类型要宽,所以在这种情况下没有信息丢失。q)7h$42i / int to long42q)6h$42 / long to int42iq)9h$42 / long to float42fq)"j"$42i42q)"i"$4242iq)"f"$4242fmore readable:q)int$4242iq)long$42i42q)float$4242f2. 不同类型之间的转换 char类型的underlying value是ASCII码,所以我们可以将char与integer互相转换。q)char$42"*"q)long$"\n"10同样,也可以将日期与integer互相转换q)date$02000.01.01q)int$2001.01.01 / millennium occurred on leap year366i3. Casts that Narrowq)long$12.34512q)short$12345678932767h将数值类型转换为boolean类型,任何0值都是0b,其余的值都是1b。q)boolean$00bq)boolean$0.00bq)boolean$1231bq)boolean$-12.3451b也可以从复杂类型中提取成分q)date$2015.01.02D10:20:30.1234567892015.01.02q)year$2015.01.022015iq)month$2015.01.022015.01mq)mm$2015.01.021iq)dd$2015.01.022iq)hh$10:20:30.12345678910iq)minute$10:20:30.12345678910:20q)uu$10:20:30.12345678920iq)second$10:20:30.12345678910:20:30q)ss$10:20:30.12345678930i4. Casting Integral Infq)int$0Wh32767iq)int$-0Wh-32767iq)long$0Wi2147483647q)long$-0Wi-21474836475. 强制类型回想在向一个简单列表中赋值的时候需要严格match列表的类型q)L:10 20 30 40q)L[1]:42h’typeq)L,:43h’type这种情况可以通过强制类型转换来实现:q)L[1]:(type L)$42hq)L,:(type L)$43h6. Cast is Atomiccast对其右运算元是atomic的:q)“i”$10 20 3010 20 30iq)float$(42j; 42i; 42j)42 42 42fcast对其左运算元也是atomic的:q)shortintlong$4242h42i42q)“ijf”$98.699i9998.6Cast对左右运算元同步的atomic:q)“ijf”$10 20 3010i2030f3. 数据和文本的转换1. 数据转换为string使用函数string可以将任何q元素转换为一个合适的文本表示。string的一些特征:转换结果是一个char的列表,不会是单个的char。转换结果不会包含任何q类型提示符或者其它装饰符。string作用在一个真正的string(list of char)上可能不会得到你想要的结果q)string 42"42"q)string 4,“4"q)string 42i"42"q)a:2.0q)string a,“2"q)f:{xx}q)string f”{xx}“string是伪atomic的,它会作用在运算元的每个atom数据上,但对每个atom元素的运算结果都返回一个listq)string 1 2 3,“1”,“2”,“3"q)string “string”,“s”,“t”,“r”,“i”,“n”,“g"q)string (1 2 3; 10 20 30),“1” ,“2” ,“3"“10” “20” “30"2. 从String构建Symbol从string去创建symbol是一个foolproof的做法。 但是也是唯一的去创建带有空格或者其它特殊符号symbol的方法。其转换使用 $ 。q)$“abc”abcqq)$“Hello World”Hello World注意带有转义符的转换:q)$“Zaphod "Z""Zaphod "Z"q)$“Zaphod \n”ZaphodNote: 左右去空格。q)string $” abc ““abc"3. 从String中解析数据使用大写的目标类型作为左运算元,string作为右运算元。q)“J”$“42"42q)“F”$“42"42fq)“F”$“42.0"42fq)“I”$“42.0"0Niq)“I”$” “0Ni日期转换:q)“D”$“12.31.2014"2014.12.31q)“D”$“12-31-2014"2014.12.31q)“D”$“12/31/2014"2014.12.31q)“D”$“12/1/2014"2014.12.31q)“D”$“2014/12/31"2014.12.31可以从字符串中解析一个函数,使用内置的函数value或者parseq)value “{xx}"{xx}q)parse “{xx}"{x*x}4. 创建带类型的空list空的list的类型是0h,但是当append一个元素之后,得到的sigleton列表就是该元素类型的简单列表。q)L:()q)type L0hq)L,:42q)type L7h如果想规定空list的类型,可以使用类似如下语法q)c1:float$()q)c1,:42'typeq)c1:98.6同样,下面的操作也能产生带类型的空listq)0#0long$()q)0#0.0float$()q)0#``symbol$()5. Enumerations1. 传统的枚举In traditional languages, an enumerated type is a way of associating a series of names with a corresponding set of integral values.2. 数据标准化q)u:gaaplmsftibmq)v:1000000?uq)k:u?vq)k2 1 1 3 3 1 0 0 0 3 0 2 2 1 2 3 1 0 1 1 2 1 2 0 2 1 1 0 1 1 3 0..上述关系满足映射:v=u*k,使用u和索引k来储存v可以大大节省储存空间,并且提高查找效率。3. Enumerating Symbols将一个symbols列表转换为对应的索引列表的操作称为q中的枚举。它以$(又一重载)作为运算符,使用unique的symbols变量作为左运算元,在变量域的symbol的列表作为右运算元。q)u$vu$msftaaplaaplibmibmaaplgggibmgmsftmsftaaplmsft..可以通过强制转换为int来恢复上述的索引结果q)ev:u$vq)int$ev2 1 1 3 3 1 0 0 0 3 0 2 2 1 2 3 1 0 1 1 2 1 2 0 2 1 1 0 1 1 3 ..枚举symbol的基本形式为: u$v 其中u是unique symbol的简单列表,v是在u中出现的atom或者对应的列表。我们称u为枚举的域(domain),投影 u$ 为在u上的枚举。 应用枚举 u$ 在向量v上可以得到索引列表k。4. 使用枚举的symbolq)sym:gaaplmsftibmq)v:1000000?symq)ev:sym$v枚举的ev可以在几乎所有的场景中代替v。q)v[3]aaplq)ev[3]u$aaplq)v[3]:ibmq)ev[3]:ibmq)v=ibm000100010010011101000010010100000000100100000001000000001100001001011..q)ev=ibm000100010010011101000010010100000000100100000001000000001100001001011..q)where v=aapl4 5 19 20 21 31 33 34 41 42 43 49 58 59 61 74 81 83 90 94 95 98 114..q)where ev=aapl4 5 19 20 21 31 33 34 41 42 43 49 58 59 61 74 81 83 90 94 95 98 114..q)v?aapl4q)ev?aapl4q)v in ibmaapl000111010010011101011110010100010110100101110001010000001111011001011..q)ev in ibmaapl000111010010011101011110010100010110100101110001010000001111011001011..尽管枚举和原始列表是item-wise的相等,但是他们并不identical(不match)q)all v=ev1bq)v~ev0b5. 枚举的类型每一个枚举都会被赋予一个新的数值类的数据类型,从20h开始。20h是留给系统的枚举域的,你自己定义的枚举类型则从21h开始,并且逐个增加。类型的正号为simple list,负号为atom的性质依旧保持。q)sym1:gaaplmsftibmq)type sym1$1000000?sym121hq)sym2:abcq)type sym2$c-22h从不同域创建的枚举是不同的,即使他们的组成都相同q)sym1:cbaq)sym2:cbaq)ev1:sym1$abacaq)ev2:sym2$abacaq)ev1=ev211111bq)ev1~ev20b6. 更新一个枚举的列表一个对u简单的改变,可以改变枚举v中对应的所有内容q)sym:gaaplmsftibmq)ev:sym$ggmsftibmaaplaaplmsftibmmsftgibmg..q)sym[0]:twitq)symtwitaaplmsftibmq)evsym$twittwitmsftibmaaplaaplmsftibmmsfttwitibmtwit..相反,则需要对v的每一处进行更新q)vggmsftibmaaplaaplmsftibmmsftgibmg…q)@[v; where v=g; :; twit]7. 动态向枚举域添加直接向枚举append一个域中没出现过的元素时会报错q)sym:gaaplmsftibmq)v:1000000?symq)ev:sym$vq)v,:twtrq)ev,:twtr'cast正确做法是这个新值必须要先添加进域中q)sym,:twtrq)ev,:twtr如果提前不知道域中的全部值,可以使用?(又一重载)来创建一个动态的域。?的语法与枚举重载符$的语法一样。unique的symbol list的名字作为左运算元,源symbol列表作为右运算元。q)sym:()q)sym$g'castq)sym?gsym$gq)sym,gq)sym?ibmaaplsym$ibmaaplq)symgibmaaplq)sym?gmsftsym$gmsftq)symgibmaaplmsft8. 恢复一个枚举可以通过value来恢复一个枚举对应的源symbol列表q)sym:gaaplmsftibmq)v:1000000?symq)ev:sym$vq)value evaaplgmsftmsftibmmsftmsftmsftmsftmsftgibmibmibm..q)v~value ev1b

April 8, 2019 · 2 min · jiezi

6. Q语言学习之路—函数

接下来几章将进入Q语言的核心内容——函数、表、数据转换、查询和I/O等等, excited :)1. 函数说明在q中,函数是可以修改全局变量的,所以q并不是一个纯正的函数式语言。1. 函数定义使用花括号{和},函数的输入输出类型不用指定,甚至函数名都可以不用指定。如下是一个完整的函数定义:q) {[x] xx}调用函数时,参数用中括号包围起来,参数通过分号;分隔q){[x] xx}[3]9函数可以赋值给一个变量:q)f:{[x] xx}q)f[3]92. 函数标识和术语 函数的正式定义为{[p1;…;pn] e1; …; em},其中可选的p1;…;pn是正式的参数,e1; …; em是执行的表达式,虽然是从左向右写的,但是仍然是从右向左执行的。函数的参数个数被称为函数的valence, 最常见的函数是monadic(valence 1)和dyadic(valence 2)。一个niladic函数是指一个没有输入的函数,表示如下:f:{[] … }例子如下:q){[] 42} / pure function returns constant 4242q){[] aa} / impure function: references global a{[] aa}函数最大的参数个数(valence)为8,超过8个参数将会报错。如果参数较多的话,将参数打包成list或者dictionary输入函数。函数的输出值为函数最后一个表达式的结果:q){[x] xx}q){[x;y] a:xx; b:yy; r:a+b; r}Keep it short. 很多q语言中的函数都是紧凑并且模块化的,很多函数都是仅仅一行3. 函数应用函数的调用是严格的,意味着在参数替换之前,表达式就已经被执行了:q)f:{[x] xx}q)f[0N!3+1]416当提供的参数多于函数定义的参数时,会报’rank错误。q) {[x] xx}[3;4]‘rank4. 没有返回值的函数函数体最后只有一个分号;,返回::。q)fvoid:{[x] a set x;}q)fvoid 42q)a42注意在q中,分号;是分隔符而并不是终止符。5. 参数的并列写法类似于列表索引和字典取值,函数的参数也可以是并列写法:q){[x] 2*x} 4284q)f:{[x] x*x}q)f 5256. 函数名的应用当函数被赋值给一个全局变量时,可以通过symbol形式的函数名来调用:q)f:{x*x}q)f[5]25q)f[5]25q)f 525q).my.name.space.f:{2*x}q).my.name.space.f[5]107. 隐含的参数当一个函数没有参数被明确定义时,三个隐含的参数x, y, z会被自动定义,下面的函数定义是等价的:{[x] xx}{xx}{[x;y] x+y}{x+y}三个参数x, y, z的调用是按照先后顺序的,意味着x总是第一个被调用,y第二个,z第三个。下面这个函数只有提供三个参数值时才会返回值q)g:{x+z} / likely meant x+y; requires 3 args in callq)g[1;2] / still waiting for 3rd arg – i.e., a projection{x+z}[1;2]q)g[1;2;3] / 2nd arg is required but ignored48. 匿名函数和lambda表达式一个没有函数名称的函数叫做匿名函数,匿名函数的常见的两个用处:定义在函数内的匿名函数f{[…] …; {…}[…]; …}函数容器q)powers:({1}; {x}; {xx}; {xxx})…q)selected:2q)powers[selected]{xx}9. 恒等函数::恒等函数::返回它的输入作为输出;裸的恒等函数不能使用并列的形式,它必须使用中括号调用。q)::[42]42q)::[abc]abcq):: 42 / error’q)(::) 424210. 函数是数据类似于python中的函数是对象一样,在q语言中函数都是数据,可以作为输入和输出。q)apply:{x y}q)sq:{xx}q)apply[sq; 5]252. 通过名字调用常规的函数使用传值调用(call-by-value),意味着参数在被传递时是按值传递的,在这个过程中,原始值被拷贝了一份,以保证之前的原始数据不会被修改。但这样做的一个问题是,当输入参数的size非常大时,拷贝是被禁止的。这个时候就有一个新的方法:call-by-name,在这种情况下变量的名字被传递而不是变量的值。Call-by-name没有特别的语法。一个例子就是内置函数get,传递全局变量的名字,返回对应的值q)a:42q)get a42另一个例子是函数set,是给全局变量赋值q)a set 43aq)a433. 局部和全局变量1. 定义局部和全局变量在函数体内使用:定义的变量被称为局部变量。函数体内最大的局部变量个数为24个。q)f:{a:42; a+x}q语言不遵循词法作用域规则,意味着在函数体内的函数并没有获得上层函数体内变量的权限,例如下例中, helper函数并没有获取局部变量a的值的权限q)f:{[p1] a:42; helper:{[p2] a*p2}; helper p1}然而,你必须对该局部函数声明一个额外的参数来传递局部变量q)f:{[p1] a:42; helper:{[a; p2] a*p2}[a;]; helper p1}q)f 5210在所有函数定义以外的变量被称为全局变量。2. 在函数内对全局变量赋值当函数体内没有同名的局部变量时,可以使用双冒号::来对全局变量进行赋值;q)b:6q)f:{b::7; xb}q)f[6]42q)b7而当函数体内有同名的局部变量时,双冒号符操作的是局部变量,并不是全局变量q)b:6q)f:{b:42; b::x; b}q)f[98]98q)b6相比::,更推荐使用set来进行全局变量修改, 这样就不会有局部变量名冲突了。q)a:42q)f:{a:98.6; a set x}q)f 43aq)a434. 投影投影是指只指定函数的一部分参数,其结果会是其余参数的函数。1. 函数投影一个函数投影的例子为:q)add:{x+y}q)add[42;]{x+y}[42;]q)add[42;][3]45q)add3:{x+y+z}q)add3[2][3][4]9上式可以理解为add3作用于参数2,返回一个函数;该函数作用于参数3,返回一个函数;最后作用于4,返回结果9。上式等价于add3[2;3;4]。No second look. 被赋值的函数投影变量不会随着原函数的改变而改变q)f:{x-y}q)g:f[42;]q)g{x-y}[42;]q)g[6]36q)f:{x+y}q)g{x-y}[42;]q)g[6]362. 运算符投影 当使用运算符中缀形式的时候,一个q的运算符可以通过固定其左运算元来进行投影,这时需要括号。q)(7*) 642由于任何操作符都是一个函数,当然可以使用其前缀形式进行投影:q)-[;42] 9856上面两个公式中的空格符都不是必须的:q)(7*)642q)-[;42]98563. 多维投影当函数有多个参数的时候(valence &gt; 2),函数可以有多个投影,例如下例q){x+y+z}[1;;3]{x+y+z}[1;;3]q){x+y+z}[1;;3] 265. Atomic函数一个Atomic函数是指该函数直接作用于一个q数据结构中的atom数据。1. Monadic Atomic函数和map回忆一下,monadic函数是指只作用于一个参数的函数,如下是一个monadic atomic函数的例子,该函数作用于一个字典q)neg 10-10q)neg 10 20 30-10 -20 -30q)neg (10 20 30; 40 50)-10 -20 -30-40 -50q)neg abc!10 20 30a| -10b| -20c| -30q)neg abc!(10 20; 30 40 50; 60)a| -10 -20b| -30 -40 -50c| -602. Dyadic atomic函数和zipDyadic函数是指作用于两个参数的函数。若将dyadic函数的非atomic部分固定下来(可以看成一个函数的投影),那么dyadic函数就变为monadic函数。如下例中的dyadic操作符?。q)10 20 30?100q)10 20 30?10 20 30 40 500 1 2 3 3q)(enlist 10)?100q)10 20?100q)10 20 30 40 50?100在算术中,比较和关系运算符都是atomic的,在这种应用下会有四种情况:atom和atomatom和listlist和atomlist和list在最后一种情况下,两个list元素的长度必须相等q)1+1011q)1+10 20 3011 21 31q)1 2 3+1011 12 13q)1 2 3+10 20 3011 22 33这个功能相当于传统语言中的zip3. 构造atomic函数简单想一想就可以明白,由atomic函数构成的函数仍然是atomic的,因此构造一个atomic函数的方法就是去包含内置的atomic函数。构造Monadic atomic函数:q)f:{(x*x)+(2*x)-1}q)f 0-1q)f til 10-1 2 7 14 23 34 47 62 79 98构造Dyadic atomic函数:q)pyth:{sqrt (x*x)+y*y}q)pyth[1; 1]1.414214q)pyth[1; 1 2 3]1.414214 2.236068 3.162278q)pyth[1 2 3; 1 2 3]1.414214 2.828427 4.2426416. 副词副词是高阶的函数,用以改变函数在列表上的应用方式。这个术语来自于将q操作符当做动词。Proficiency in the use of adverbs is one skill that separates q pretenders from q contenders. :)1. Monadic each合并函数例如count只会作用在嵌套列表的最高层级:q)count 10 20 303q)count (10 20 30; 40 50)2如果我们想知道嵌套列表中每个元素的长度,这个时候副词each就派上用场了,它使得monadic函数能够作用于列表的每个元素而不是整个列表,each有两种使用方法:中缀形式:each紧跟在函数的后面q) count each (10 20 30; 40 50)3 2前缀形式q) each[count] (10 20 30; 40 50)3 2对于层数较深的嵌套矩阵,可能需要对each进行迭代q)(count each) each ((1 2 3; 3 4); (100 200; 300 400 500))3 22 3q)each[each[count]] ((1 2 3; 3 4); (100 200; 300 400 500))3 22 3一些例子:q)reverse "live""evil"q)reverse ("life"; "the"; "universe"; "and"; "everything")q)reverse each ("life"; "the"; "universe"; "and"; "everything")当想要将一个长度为n的向量转换为一个大小为n*1的矩阵时,可以使用enlist each来实现,但flip enlist在大列表上执行更快。q)enlist each 1001 1002 1004 10031001100210041003q)flip enlist 1001 1002 1004 100310011002100410032. each-both'副词each-both符号'作用在一个dyadic函数上,使得函数能够成对地作用在对应的列表元素上,符号'读作"zip"。q)("abc"; "uv"),'("de"; "xyz")"abcde""uvxyz"q)1,'10 20 301 101 201 30q)1 2 3,'101 102 103 10q)2#'("abcde"; "fgh"; "ijklm")"ab""fg""ij"当熟练的时候,可以使用each-both的前缀形式:q),'[("abc"; "uv"); ("de"; "xyz")]"abcde""uvxyz"一个table的例子:q)t1:([] c1:1 2 3)q)t2:([] c2:abc)q)t1,’t2c1 c2—–1 a2 b3 c3. each-left :each-left操作符作用于一个dyadic函数,使第一个参数下的每一项都应用于第二个参数:(“abc”; “de”; enlist “f”) ,: “>““abc>““de>““f>“4. each-right /:each-right 作用于一个dyadic函数,使第一项作用于第二个参数的每一项:q)"</”,/:(“abc”;“de”;enlist “f”)"</abc”"</de”"</f"5. Cross Product叉积(Cross Product)成对地作用于左侧的每一项和右侧的每一项。 如果我们对join执行each-right和each-left操作,再内置函数raze对得到的嵌套矩阵夷平,就可以得到我们想要的结果q)1 2 3,/::10 201 10 1 202 10 2 203 10 3 20q)raze 1 2 3,/::10 201 101 202 102 203 103 20上述操作还是较为复杂,我们可以通过内置函数cross来得到上述结果q)1 2 3 cross 10 201 101 202 102 203 103 20可以注意到,若我们组合each-left和each-right时,我们便可以得到上述结果的转置q)raze 1 2 3,:/:10 201 102 103 101 202 203 206. Over /Over操作符/是一个提供递归机制的高阶函数。它最简单的形式是修改一个dyadic函数,使其在一个list上累积函数作用的结果q)0 +/ 1 2 3 4 5 6 7 8 9 1055注意: 在运算符和/之间不能存在空格,因为/可以被用作注释。运算符左运算元为累积计算的初始值,右运算元为待累积计算的列表。但我们也可以省略左运算元(即初始值),这个时候需要我们对表达式做一些变换,被作用函数和Over运算符/用括号包住,这时右边列表的第一个元素就是初始值。q)(+/) 1 2 3 4 5 6 7 8 9 1055上面的括号是必需项,被修改的函数实际上是一个monadic函数。一些有用的over形式:q)(/) 1 2 3 4 5 6 7 8 9 10 / product3628800q)(|/) 7 8 4 3 10 2 1 9 5 6 / maximum10q)(&/) 7 8 4 3 10 2 1 9 5 6 / minimum1使用,/可以高效地移除列表的顶层嵌套,其对应的内置函数为raze。q)(,/)((1 2 3; 4 5); (100 200; 300 400 500))1 2 34 5100 200300 400 500q)raze ((1 2 3; 4 5); (100 200; 300 400 500))1 2 34 5100 200300 400 5007. Iteration/的另一种用法是作为循环代码的等价形式。在这个版本下,左运算元表示了循环的次数,右运算元为初始值。例如,计算Fibonacci数列:q)fib:{x, sum -2#x}q)10 fib/ 1 11 1 2 3 5 8 13 21 34 55 89 144q)fib/[10;1 1]1 1 2 3 5 8 13 21 34 55 89 144另一个版本的/控制了循环的进行直至收敛,或者检测到一个环的存在。下面以牛顿法为例介绍这种循环的使用,我们使用牛顿法来寻找函数{-2+xx}的根:q)f:{-2+xx}q)secant:{[f;x;e] (f[x+e]-f x-e)%2e}q){x-f[x]%secant[f; x; 1e-6]}/[1.5]1.414214q语言会判断当前的输出值与之前的输出值之间的大小,如果两个值之间相差在一定的tolerance以内,则认为算法收敛并且迭代完成;否则继续执行循环任务。此外,q语言通过每次比较运算结果和初始值是否match()来判断当前程序是否存在环(loop),如果存在环,程序则终止:q)newtcycle:{[xn] xn-((xnxnxn)+(-2xn)+2)%-2+3xnxn}q)newtcycle/[0.0]1f如果运算的结果与初始值相等,但是类型不同(not match),程序则不会停止;例如上例中如果提供初始值0,则程序会一直运行下去。运算符/的最后一种重载用法,等价于使用while循环,它提供了一个判断条件,若每次的运算结果满足条件,则继续执行;否则,终止计算q)fib:{x,sum -2#x}q)fib/[{1000>last x}; 1 1]1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 15978. Scan \Scan操作符\同样是一个高阶函数,其作用与操作符/一样,与其不同的是,Scan\会返回中间的计算结果。可以把Scan\当做是Over/的"running"版本。q)0+\1 2 3 4 5 6 7 8 9 101 3 6 10 15 21 28 36 45 55q)()1 2 3 4 5 6 7 8 9 10q)(|)7 8 4 3 10 2 1 9 5 67 8 8 8 10 10 10 10 10 10q)(&)7 8 4 3 10 2 1 9 5 67 7 4 3 3 2 1 1 1 1q)100 f\1 2 3 4 5 6 7 8 9 10q)(f)1 2 3 4 5 6 7 8 9 10所有over操作符/的用法都适用于Scan操作符\,使用Scan操作符的好处是可以看到函数中间过程的运行结果。q)fib:{x, sum -2#x}q)fib[{1000>last x}; 1 1]9. each-previous ‘:each-previous操作符对列表的每项和其前一项执行dyadic函数操作。当执行Dyadic操作时,当前项是Dyadic函数的左运算元,前一项是Dyadic函数的右运算元。由于列表中第一项没有前项,所以我们必须在运算符:‘左运算元的位置提供初始值,如下例q)100 -’: 100 99 101 102 1010 -1 2 1 -1与其它副词一样,each-previous :‘也有一个monadic函数的形式。但在这种形式下,列表中的第一个元素不会被当做初始的前项,相反,它直接返回该项。q)(-’:)100 99 101 102 101100 -1 2 1 -1q)deltas 100 99 101 102 101100 -1 2 1 -1q)(%’:)100 99 101 102 101100 0.98999999999999999 1.0202020202020201 1.0099009900990099 0.99019607843137258q)ratios 100 99 101 102 101100 0.98999999999999999 1.0202020202020201 1.0099009900990099 0.99019607843137258保留第一项的动机是,可以通过保留的第一项来恢复整个listq)sums deltas 100 99 101 102 101100 99 101 102 101q)deltas sums 100 99 101 102 101100 99 101 102 101当我们需要返回的结果中都是变化值时,可以通过如下方法得到q)deltas0:{first[x] -’: x}q)deltas0 100 99 101 102 1010 -1 2 1 -1一个使用each-previous的非常有用的工具是使用判断连续项是否是match的。实际中,我们经常会关注两个连续项不同的情况,这种情况下使用内置函数differq)(’:) 1 1 1 2 2 3 4 5 5 5 6 6011010001101bq)not (’:) 1 1 1 2 2 3 4 5 5 5 6 6100101110010bq)differ 1 1 1 2 2 3 4 5 5 5 6 6100101110010b可以对differ的结果使用where和cut来分隔列表q)L:1 1 1 2 2 3 4 5 5 5 6 6q)where differ L0 3 5 6 7 10q)(where differ L) cut L1 1 12 2,3,45 5 56 6下面我们来做一些q的练习q)runs:(where differ L) cut L / store runsq)ct:count each runs / store count of each runq)runs where ct=max ct / find the runs of maximum length1 1 15 5 5用一行代码来实现上面的代码q) runs where ct=max ct:count each runs:(where differ L) cut L同样,我们可以上述技术来找到上升和下降子序列q)L:9 8 7 11 10 12 13q)(where -0W>’:L) cut L9 8 711 10,12,13q)(where 0W<’:L) cut L,9,87 1110 12 138. 一般应用Thorough understanding of the general application is another test that separates the q pretenders from the contenders.1. 动词 @q语言的基础操作包括:从list中通过索引取值,在字典中通过键取值或执行一个monadic函数。高阶函数@是q语言中基础操作的真正形式,它将一个monadic映射(可能是索引取值,字典取值或者monadic函数)作用于一个元素之上。与所有的内置函数一样,它同样有中缀和前缀的表示形式q)10 20 30 40@120q)L:10 20 30 40q)L@120q)@[L; 1]20q)count@L4q)@[count; L]4q){x*x}@L100 300 900 1600q)d:abc!10 20 30q)d@a10q)@[d;b]20当@与niladic函数应用时,可以使用空元素::来代表空值q)f:{6*7}q)f[]42q)@[f; ::]42q)f@(::)42q)f@43422. 动词.q语言中,多元映射包括了:深度索引一个列表,从一个字典中取一个被嵌套的值和执行一个带有多个参数的函数等。高阶函数.是q语言中多元应用的真正形式。它将多元映射投影到多个参数上,并且可以被写为中缀和前缀形式。.的右侧必须是一个listq)L:(10 20 30; 40 50)q)L[1][0]40q)L[1; 0]40q)L . 1 040q)d:abc!(10 20 30; 40 50; enlist 60)q)d[b][0]40q)d[b; 0]40q)d . (b; 0)40q)g:{x+y}q)g[1; 2]3q)g . 1 23可以配合monadic函数使用.,其效果如下q)f:{x*x}q)f@525q)f . enlist 525q)f . enlist 1 2 31 4 9为了表示一个隐藏的索引,可以使用::来代替q)m:(1 2 3;4 5 6)q)m[0;]1 2 3q)m . (0; ::)1 2 3q)m . (::; 1)2 5对于一个niladic函数的.执行形式,需要使用::生成一个list。q)f:{6*7}q)f . enlist (::)42q)f . enlist 4242All data structures in q are composed from lists and dictionaries.一些很好的练习:q)L:10 20 30q)L . enlist 1_q)m:(10 20 30; 100 200 300)q)m . 0 1_q)ds:(abc!10 20 30; xy!100 200)q)ds . (0; b)_q)mix:(10 20 30; abc!(1; 2; (300 400)))q)mix . (1; c; 1)_q)dc:c1c2!(1 2 3; abc)q)dc . (c2; 1)_q)t:([]c1:1 2 3;c2:abc)q)t . (1; c2)_答案分别是20 20 20 400 b b3. 应用Monadic函数的@回忆@的一般操作:q)L:10 20 30 40 50q)@[L;1]20q)@[L;0 1]10 20现在除了取值外,我们同时应用一个函数:q)@[L;1;neg]10 -20 30 40 50q)@[L;0 2;neg]-10 20 -30 40 50注意到上述结果与正常在列表子集上的运算不同,正常只会返回在子集上运算的结果q)neg L@0 1-10 -20而这个提升的版本会返回修改后的整个列表。Monadic函数使用@的一般应用 的语法是@[L;I;f]其中L是列表,I为索引的容器。这个形式可以泛化到任何可以被视为映射的数据结构,例如给定一个字典和一个键值列表q)d:abc!10 20 30q)ks:acq)@[d; ks; neg]a| -10b| 20c| -30上述操作都是在输入数据结构的拷贝上完成的。我们也可以通过pass-by-name的方法来进行in-place修改q)L:10 20 30 40q)@[L; 0; neg]-10 20 30 40q)L10 20 30 40q)@[L; 0 ; neg]Lq)L-10 20 30 404. 应用Dyadic函数的@当Dyadic函数使用@时,需要提供一个额外的运算元,显然运算元要与子集的大小匹配。除了一种额外的情况,当运算元是atom时,会被自动拓展到与子集相同大小。q)L:10 20 30 40q)@[L; 0 1; +; 100 200]110 220 30 40q)@[L; 0 1; +; 100]110 120 30 40q)d:abc!10 20 30q)@[d; ab; +; 100 200]a| 110b| 220c| 30q)@[d; ab; +; 100]a| 110b| 120c| 30Dyadic函数使用@的一般应用 的语法是@[L;I;g;v]其中L和I与上小节定义一样,可以是任意能够被视为映射的数据结构;g是一个Dyadic函数;v是一个atom或者与I匹配的列表。列表赋值: 一个非常有用的Dyadic函数应用是使用赋值符:在子集上赋值q)L:10 20 30 40q)@[L; 0 2; :; 42 43]42 20 43 40与Monadic函数一样,in-place操作可以通过pass-by-name形式q)L:10 20 30 40q)@[L; 0 2; :; 42 43]Lq)L42 20 43 405. 应用Monadic函数的.总结一下,@是作用在数据结构的顶层,而.则是深度索引。重新回顾一下.的前缀用法q)m:(10 20 30; 100 200 300)q).[m; 0 1]20q)d:abc!(10 20 30; 40 50; enlist 60)q).[d; (a; 1)]20应用monadic函数的.形式:q).[m; 0 1; neg]10 -20 30100 200 300q).[d; (a; 1); neg]a| 10 -20 30b| 40 50c| ,60同样,若想in-place修改,则使用pass-by-name形式。可以使用::来代替隐藏的索引q).[m; (0; ::); neg]-10 -20 -30100 200 300q)d:abc!(100 200 300; 400 500; enlist 600)q).[d; (a; ::); neg]a| -100 -200 -300b| 400 500c| ,600q).[d; (::; 0); neg]a| -100 200 300b| -400 500c| ,-600应用monadic函数的.一般形式为:.[L; I; f]7. 应用Dyadic函数的.一般形式为.[L;I;g;v]其中g是Dyadic函数,v是atom或与I相对应的运算元q)m:(10 20 30; 100 200 300)q).[m; 0 1; +; 1]10 21 30100 200 300q).[m; (::; 1); +; 1 2]10 21 30100 202 300q)m10 20 30100 200 300q).[m; (::; 1); +; 1]mq)m10 21 30100 200 300q).[m; (::; 1); :; 42]mq)m10 42 30100 42 300q)d:abc!(100 200 300; 400 500; enlist 600)q).[d; (a; 1); +; 1]q).[d; (a; ::); +; 1]q).[d; (::; 0); +; 1]q).[d; (::; 0); :; 42] ...

April 3, 2019 · 6 min · jiezi

5. Q语言学习之路—字典

字典基础1. 定义字典是key-values pairs, 但是在q中,字典是按照list来存储的。字典的创建使用操作符!,读作bang :) 所有的字典类型都是99h。q)10 20 30!1.1 2.2 3.310| 1.120| 2.230| 3.3q)abc!100 200 300a| 100b| 200c| 300可以使用操作符key, value, count来分别获取字典的键、值和个数。尽管q语言不强制键的唯一性(historical mistake),但是却对每个输入值都提供唯一的输出,只有第一次出现的key会被看到。当你知道字典的键是unique的时候,可以使用命令u#将字典转换为哈希表,这样与原来的线性搜索相比,会提高查找的速度。q)(u#abc)!10 20 30注意:与传统语言不一样,不同顺序的字典在q中是不等的。q)(abc!10 20 30)~acb!10 30 200b2. 空和单例字典 空字典:()!()带类型的空字典:symbol$()!float$()单例字典,必须要使用enlist形式先生成一个list,否则会报错q)(enlist x)!enlist 42x| 423. 查找使用中括号或者并列的形式:q)d:abc!10 20 30q)d[a]10q)d b20当查找值不在字典的键表中时,返回值列表中初始值类型的Null值:q)d[x]0N4. 逆向查找 使用? 与列表类似,?返回值在字典中对应的键。q)d:abca!10 20 30 10q)d?10a当查找的值不在字典的值列表中时,返回键列表中初始值类型的Null值:q)d:abca!10 20 30 10q)d?405. 字典和列表 可以通过字典来表示一个稀疏列表:q)d1:0 100 500000!10 20 30q)d2:0 99 1000000!100 200 300q)d1+d20 | 110100 | 20500000 | 3099 | 2001000000| 3006. 不唯一的键和值 如之前所说,当键不唯一时,会返回第一个出现时的值:q)ddup:abac!10 20 30 20q)ddup[a]10逆向查找同理:q)ddup?30aq)ddup?20b7. Non-simple 键 和 值键和值是嵌套的列表:q)d:(ab; cde; enlist f)!10 20 30q)d f30q)d?20cdeq)d:abc!(10 20; 30 40 50; enlist 60)q)d b30 40 50q)d?30 40 50bq)d?enlist 60注意,对单个元素的键或值要单独对其生成一个列表(enlist),否则会有如下的问题:q)dwhackey:(1 2; 3 4 5; 6; 7 8)!10 20 30 40 / atom 6 is whackq)dwhackey 1 210q)dwhackey 60Nq)dwhackval:10 20 30 40!(1 2; 3 4 5; 6; 7 8) / atom 6 is whackq)dwhackval?3 4 520q)dwhackval?60N会导致查找失败,返回Null值。2. 字典操作1. Amend 和 Upsert update:q)d:abc!10 20 30q)d[b]:42insert:q)d:abc!10 20 30q)d[x]:42在q语言中,update/insert操作被称为upsert操作。2. 提取子字典使用提取操作符#,左运算元为子字典的键,右运算元为原始字典。 另外,当原始字典有重复键时,只会提取第一次出现的键:q)ddup:abac!10 20 30 20q)ac#ddupa| 10c| 203. 删除 使用操作符_, 用法与#类似,但需要注意_前后要加空格。q)d:abc!10 20 30q)ac _ db| 20q)(enlist b) _ d删除全部键值对会得到一个带类型的空字典:q)d:abc!10 20 30q)abc _ dq)-3!abc _ d"(symbol$())!long$()“操作符cut与_在字典上的效果相同。另外一个很少用的用法是,字典在_的左边,_右运算元为一个单一的键,表示字典删除这个键:q)d _ ba| 10c| 304. 字典上的基础运算一些常见的基础运算如下例所示,非常容易理解q)d:abc!10 20 30q)neg da| -10b| -20c| -30字典的加法: 相同键值进行加法操作,不同键值保留下来q)d1:abc!1 2 3q)d2:bcd!20 30 40q)d1+d2a| 1b| 22c| 33d| 405. join,使用,进行合并字典的操作,由于q语言是right to left特性的,所以合并的两个字典如果有相同的键值,那么右边的会保留下来。q)d1:abc!10 20 30q)d2:cd!300 400q)d1,d2a| 10b| 20c| 300d| 400因此,字典合并时,前后顺序很重要。6. Coalesce ^ 这种合并方式类似于,合并, 但与其不同的是,当^右边右边项值为Null时不覆盖左边项,见下例:q)d1:abc!10 0N 30q)d2:bcd!200 0N 400q)d1^d2a| 10b| 200c| 30d| 400q)d1,d2a| 10b| 200c|d| 4007. 算术和相等运算符 对于相等性比较,由于null代表缺失值,所以所有的null值会被认为是相等的。q)(abc!10 20 30)=bcd!20 300 400a| 0b| 1c| 0d| 0q)(abc!0N 20 30)=bcd!20 300 0Na| 1b| 1c| 0d| 1q)(abc!10 20 30)<bcd!20 300 400a| 0b| 0c| 1d| 13. 列字典列字典(Column Dictionary)是表(Table)的基础。1. 定义和术语一个一般的列字典具有如下的形式:c1...cn!(v1;...;vn)其中ci是symbol类型,vi是具有相同长度的列表。通常vi均为简单列表。2. 简单例子q)travelers:nameiq!(DentBeeblebroxPrefect;42 98 126)q)travelersname| Dent Beeblebrox Prefectiq | 42 98 126索引:q)travelers[name; 1]Beeblebroxq)travelers[iq; 2]126单列的字典:q)dc1:(enlist c)!enlist 10 20 30q)dc1c| 10 20 304. 列字典的翻转q)dc:c1c2!(abc; 10 20 30)q)dcc1| a b cc2| 10 20 30q)t:flip dcq)tc1 c2-----a 10b 20c 30索引:q)dc[c1; 0]aq)dc[c1; 1]bq)dc[c1; 2]cq)t[0; c1]aq)t[1; c1]bq)t[2; c1]`cUnlike the case of transposing rectangular lists, transposing a column dictionary does not physically re-arrange data.(翻转并不会改变数据的存储)

March 30, 2019 · 2 min · jiezi