Q语言——表的高级查询语言(三)

操作案例

这一节我们进行大型数据的案例演示,这里利用脚本创建一个10000000行的数据表。我们可以创建一个文本文档,并命名为trades.q,里面的脚本内容为创建一个trades表,具体如下:

mktrades:{[tickers; sz]
 dt:2015.01.01+sz?31;
 tm:sz?24:00:00.000;
 sym:sz?tickers;
 qty:10*1+sz?1000;
 px:90.0+(sz?2001)%100;
 t:([] dt; tm; sym; qty; px);
 t:`dt`tm xasc t;
 t:update px:6*px from t where sym=`goog;
 t:update px:2*px from t where sym=`ibm;
 t}
trades:mktrades[`aapl`goog`ibm; 10000000]

trades表的具体内容如下:

q)trades
dt tm sym qty px
----------------------------------------
2015.01.01 00:00:01.129 aapl 6410 99.18
2015.01.01 00:00:02.099 aapl 5200 93.82
2015.01.01 00:00:02.670 ibm 6840 196.92
2015.01.01 00:00:02.885 goog 7400 610.98
2015.01.01 00:00:03.396 aapl 8460 97.14
2015.01.01 00:00:03.834 aapl 3980 107.63
2015.01.01 00:00:04.047 ibm 2220 206.14
2015.01.01 00:00:04.096 goog 3320 575.52
2015.01.01 00:00:04.435 ibm 7390 202.38
2015.01.01 00:00:04.551 ibm 3630 216.04
2015.01.01 00:00:04.772 aapl 3990 90.15
2015.01.01 00:00:05.014 aapl 970 98.43
2015.01.01 00:00:05.190 goog 2080 628.56
2015.01.01 00:00:05.254 goog 6720 641.22
2015.01.01 00:00:05.263 goog 8990 577.38
2015.01.01 00:00:05.795 goog 8000 621.72
2015.01.01 00:00:07.135 goog 4790 658.98
2015.01.01 00:00:08.103 ibm 5150 183.56
2015.01.01 00:00:08.121 goog 4060 568.02
2015.01.01 00:00:08.237 aapl 2640 101.79
.. 

另外我们再创建一个股票信息表instr,该表记录了每支股票的基本信息。包括股票代码,公司名称,行业分类等字段。具体如下:

q)instr:([sym:`symbol$()] name:`symbol$(); industry:`symbol$())
q)`instr upsert (`ibm; `$"International Business Machines"; `$"Computer Services")
`instr
q)`instr upsert (`msft; `$"Microsoft"; `$"Software")
`instr
q)`instr upsert (`goog; `$"Google"; `$"Search")
`instr
q)`instr upsert (`aapl; `$"Apple"; `$"Electronics")
`instr
q)instr
sym | name industry
----| -------------------------------------------------
ibm | International Business Machines Computer Services
msft| Microsoft Software
goog| Google Search
aapl| Apple Electronics
q)update `instr$sym from `trades /这时我们需要将股票信息表与股票交易表的股票代码对应起来,
因此我们将股票信息表的sym字段(主键)更新为股票交易表的sym字段的外键,建立两个表之间的对应联系
`trades
q)meta trades /查询股票交易表的结构信息,有一个表instr的sym字段作为外键
c | t f a
---| ---------
dt | d s
tm | t
sym| s instr
qty| j
px | f
q)meta instr
c | t f a
--------| -----
sym | s
name | s
industry| s

1. 基本查询

首先是对一个表的有多少条数据记录的一个统计,这里有好几种方式来进行查询统计,分别如下:

q)count trades
10000000
q)exec count i from trades /这里的i为前面介绍的虚拟列
10000000
q)select count i from trades
x
--------
10000000
q)exec count i from trades where sym=`ibm
3329912
q)select count i from trades where sym=`ibm
x
-------
3329912
q)select count i by sym from trades
sym | x
----| -------
aapl| 3333508
goog| 3336580
ibm | 3329912
q)() xkey select count i by sym from trades / xkey是将主键变为普通的字段
sym x
------------
aapl 3333508
goog 3336580
ibm 3329912

现在我们想查询一下aapl这支股票在2015.01.05这一天的所有交易记录。则可以使用条件查询where语句。

q)select from trades where dt=2015.01.05, sym=`aapl
dt tm sym qty px
----------------------------------------
2015.01.05 00:00:01.680 aapl 3420 99.62
2015.01.05 00:00:02.113 aapl 5270 104.25
2015.01.05 00:00:02.151 aapl 7750 90.93
2015.01.05 00:00:02.360 aapl 2180 102.86
2015.01.05 00:00:02.461 aapl 8900 95.35
2015.01.05 00:00:03.537 aapl 7330 104.03
2015.01.05 00:00:04.406 aapl 8400 96.76
2015.01.05 00:00:05.013 aapl 7600 90.46
2015.01.05 00:00:05.015 aapl 8240 107.35
2015.01.05 00:00:06.652 aapl 2910 90.39
2015.01.05 00:00:06.686 aapl 7020 108.23
2015.01.05 00:00:07.806 aapl 7550 98.62
2015.01.05 00:00:08.832 aapl 7520 104.62
2015.01.05 00:00:09.468 aapl 3760 102.01
2015.01.05 00:00:09.877 aapl 8880 105.8
2015.01.05 00:00:11.006 aapl 7510 103.09
2015.01.05 00:00:14.255 aapl 3820 104.78
2015.01.05 00:00:15.386 aapl 2910 99.84
2015.01.05 00:00:15.814 aapl 3610 90.9
2015.01.05 00:00:15.895 aapl 7100 105.39
..

现在我们还想查询一下goog这支股票在某天某个时间段内的股票交易数据,具体操作如下:

q)select from trades where sym=`goog, dt=2015.01.05, tm within 12:00:00 13:00:00
dt tm sym qty px
----------------------------------------
2015.01.05 12:00:01.255 goog 8460 578.1
2015.01.05 12:00:01.393 goog 4960 579.12
2015.01.05 12:00:05.226 goog 6140 594.54
2015.01.05 12:00:06.974 goog 1750 613.74
2015.01.05 12:00:07.007 goog 6980 618.18
2015.01.05 12:00:08.872 goog 3690 655.08
2015.01.05 12:00:09.877 goog 4520 644.04
2015.01.05 12:00:10.696 goog 8010 604.26
2015.01.05 12:00:10.862 goog 730 552.6
2015.01.05 12:00:13.299 goog 1520 540.42
2015.01.05 12:00:13.582 goog 1700 651.72
2015.01.05 12:00:14.863 goog 3180 651.12
2015.01.05 12:00:14.940 goog 9550 550.68
2015.01.05 12:00:15.150 goog 3080 603.9
2015.01.05 12:00:15.238 goog 1350 579.54
2015.01.05 12:00:16.309 goog 8330 585.3
2015.01.05 12:00:16.475 goog 2640 618.48
2015.01.05 12:00:16.647 goog 9660 622.26
2015.01.05 12:00:16.702 goog 5950 590.34
2015.01.05 12:00:17.351 goog 8120 625.56
..

上述的within的使用,我们也可以利用变量的形式,这样我们就可以通过变量来灵活的查询一些时间段的交易数据了。

q)noon:12:00:00
q)thirteen00:13:00:00
q)select from trades where sym=`goog, tm within (noon;thirteen00)
dt tm sym qty px
----------------------------------------
2015.01.01 12:00:00.316 goog 4320 597.66
2015.01.01 12:00:00.374 goog 7370 552.66
2015.01.01 12:00:03.617 goog 1150 640.02
2015.01.01 12:00:04.193 goog 4190 622.8
2015.01.01 12:00:06.546 goog 290 655.2
2015.01.01 12:00:07.248 goog 6650 553.62
2015.01.01 12:00:08.245 goog 9780 557.34
2015.01.01 12:00:09.378 goog 6900 571.74
2015.01.01 12:00:09.515 goog 3920 554.82
2015.01.01 12:00:09.600 goog 5740 608.22
2015.01.01 12:00:09.750 goog 5530 653.16
2015.01.01 12:00:09.756 goog 2900 590.64
2015.01.01 12:00:10.203 goog 90 605.22
2015.01.01 12:00:10.517 goog 1830 591.78
2015.01.01 12:00:11.619 goog 1820 654.06
2015.01.01 12:00:12.528 goog 9290 603.96
2015.01.01 12:00:12.557 goog 4340 571.68
2015.01.01 12:00:12.665 goog 7530 571.32
2015.01.01 12:00:13.229 goog 8180 555.6
2015.01.01 12:00:13.383 goog 170 580.92
..

现在我们还需要查询aapl这支股票每天交易的最高价格,因此需要对dt进行分组,然后通过max的聚合函数来进行分组查询。

q)select maxpx: max px by dt from trades where sym=`aapl 
/(由于我们在利用脚本创建交易表的时候px使用的是均匀分布函数随机生成的,因此每天的最高价格是恒定的)
dt  | maxpx
----------| -----
2015.01.01| 110
2015.01.02| 110
2015.01.03| 110
2015.01.04| 110
2015.01.05| 110
2015.01.06| 110
2015.01.07| 110
2015.01.08| 110
2015.01.09| 110
2015.01.10| 110
2015.01.11| 110
2015.01.12| 110
2015.01.13| 110
2015.01.14| 110
2015.01.15| 110
2015.01.16| 110
2015.01.17| 110
2015.01.18| 110
2015.01.19| 110
2015.01.20| 110
..

现在我们查询一下每支股票在交易表中的最低价和最高价分别是多少,这里我们可以通过交易表的外键从股票信息表中先去获得每支股票对应的公司全称,然后进行分组,最后来进行聚合查询,找出最小值和最大值

q)select lo: min px, hi: max px by sym.name from trades
name  | lo hi
-------------------------------| -------
Apple  | 90 110
Google | 540 660
International Business Machines| 180 220

我们还可以查询一下某支股票的总的交易量和平均交易量,平均交易量是交易表中的所有交易总和除以交易记录的总数

q)select totq: sum qty, avgq: avg qty by sym from trades where sym in `ibm`goog
sym | totq avgq
----| --------------------
goog| 16703515390 5006.179
ibm | 16667961470 5005.526

对于股票交易数据,可能我们比较感兴趣的是每天的最高价,最低价,开盘价和收盘价个,因此我们可以通过每天进行分组,然后查询每天对应的四个价格。

q)select hi: max px, lo: min px, open: first px, close: last px by dt from trades where sym=`goog
dt  | hi lo open close
----------| ---------------------
2015.01.01| 660 540 610.98 568.92
2015.01.02| 660 540 565.08 615
2015.01.03| 660 540 623.7 629.82
2015.01.04| 660 540 642.72 650.58
2015.01.05| 660 540 627.54 571.68
2015.01.06| 660 540 647.76 553.68
2015.01.07| 660 540 547.74 638.76
2015.01.08| 660 540 596.04 647.52
2015.01.09| 660 540 584.7 657.78
2015.01.10| 660 540 562.86 619.44
2015.01.11| 660 540 575.04 587.22
2015.01.12| 660 540 649.2 587.52
2015.01.13| 660 540 651.12 563.1
2015.01.14| 660 540 651.18 649.56
2015.01.15| 660 540 588.6 644.16
2015.01.16| 660 540 614.4 622.5
2015.01.17| 660 540 643.2 629.46
2015.01.18| 660 540 659.82 615.9
2015.01.19| 660 540 632.1 627.96
2015.01.20| 660 540 651 590.58
..

我们也可以通过定义一个自己的函数来进行查询,这里定义了一个favg的函数,该函数的功能为一个特殊的加权平均。

q)favg:{(sum x*1+til ctx)%ctx*ctx:count x}
q)select favgpx: favg px by sym from trades /使用自定义函数进行查询操作
sym | favgpx
----| --------
aapl| 50.00125
goog| 300.0098
ibm | 100.0079

2. 内容查询

现在我们对交易表进行一些高级的内容查询与操作,这里常见的就是数学上的操作了。毕竟是金融数据,那更多的也是做统计与分析。

首先是我们对交易表trades的数据对每天的交易数据根据交易量与交易价格做一个加权平均,具体如下:

q)select vwap: qty wavg px by dt from trades where sym=`ibm
dt | vwap
----------| --------
2015.01.01| 200.0045
2015.01.02| 199.9817
2015.01.03| 200.0193
2015.01.04| 199.9379
2015.01.05| 199.9636
2015.01.06| 200.0109
2015.01.07| 199.9824
2015.01.08| 200.0055
2015.01.09| 199.951
2015.01.10| 199.985
2015.01.11| 200.0282
2015.01.12| 200.0326
2015.01.13| 200.0374
2015.01.14| 200.0502
2015.01.15| 200.0283
2015.01.16| 200.0439
2015.01.17| 199.9993
2015.01.18| 199.9977
2015.01.19| 200.0171
2015.01.20| 199.9833
..
q)select vwap: qty wavg px by dt, 100 xbar tm from trades where sym=`ibm 
/这里100 xbar tm表示在tm字段中以100为单位步长来进行统计分组
dt tm | vwap
-----------------------| --------
2015.01.01 00:00:02.600| 196.92
2015.01.01 00:00:04.000| 206.14
2015.01.01 00:00:04.400| 202.38
2015.01.01 00:00:04.500| 216.04
2015.01.01 00:00:08.100| 183.56
2015.01.01 00:00:08.600| 210.06
2015.01.01 00:00:10.000| 209
2015.01.01 00:00:11.100| 193.6189
2015.01.01 00:00:12.000| 182.24
2015.01.01 00:00:12.400| 181.94
2015.01.01 00:00:12.700| 190.52
2015.01.01 00:00:13.900| 199.22
2015.01.01 00:00:14.000| 204.22
2015.01.01 00:00:14.400| 184.42
2015.01.01 00:00:15.400| 207.2981
2015.01.01 00:00:16.200| 188.4
2015.01.01 00:00:16.500| 206.68
2015.01.01 00:00:16.900| 200.92
2015.01.01 00:00:21.100| 207.2
2015.01.01 00:00:21.600| 207.94
..

前面我们介绍过fby,fby其实你就可以理解为我们需要查询很多字段,但是只能用其中一个字段进行分组查询,而且其他字段也涉及一些其他操作,如找出最大值,最小值或者做一些信息统计,这时我们只能用fby,用by分组查询的结果是不对的。这里我们使用sym字段进行分组,但是需要对px字段进行加权平均,同时进行比较操作。具体如下:

q)select from trades where px<2*(favg;px) fby sym
dt tm sym qty px
----------------------------------------
2015.01.01 00:00:01.129 aapl 6410 99.18
2015.01.01 00:00:02.099 aapl 5200 93.82
2015.01.01 00:00:02.670 ibm 6840 196.92
2015.01.01 00:00:03.396 aapl 8460 97.14
2015.01.01 00:00:04.096 goog 3320 575.52
2015.01.01 00:00:04.772 aapl 3990 90.15
2015.01.01 00:00:05.014 aapl 970 98.43
2015.01.01 00:00:05.263 goog 8990 577.38
2015.01.01 00:00:08.103 ibm 5150 183.56
2015.01.01 00:00:08.121 goog 4060 568.02
2015.01.01 00:00:09.888 goog 2830 540.72
2015.01.01 00:00:10.048 aapl 3830 99.15
2015.01.01 00:00:10.683 aapl 4420 92.14
2015.01.01 00:00:10.848 goog 5740 595.86
2015.01.01 00:00:10.994 aapl 6180 91.17
2015.01.01 00:00:11.126 ibm 8520 188.3
2015.01.01 00:00:12.051 ibm 6740 182.24
2015.01.01 00:00:12.494 ibm 2160 181.94
2015.01.01 00:00:12.790 ibm 5330 190.52
2015.01.01 00:00:13.978 ibm 7790 199.22
..
q)show atrades:select avgqty:avg qty, avgpx:avg px by sym, dt from trades /查询每支股票每天的数据,返回每天的平均交易量与价格
sym dt | avgqty avgpx
---------------| -----------------
aapl 2015.01.01| 4989.021 99.97649
aapl 2015.01.02| 5004.019 100.0455
aapl 2015.01.03| 5011.448 99.99385
aapl 2015.01.04| 5002.787 99.9908
aapl 2015.01.05| 5000.981 100.0157
aapl 2015.01.06| 5004.591 100.0176
aapl 2015.01.07| 4995.854 100.0034
aapl 2015.01.08| 5015.4 99.9729
aapl 2015.01.09| 5001.951 99.98638
aapl 2015.01.10| 5006.982 99.95917
aapl 2015.01.11| 5024.328 99.97282
aapl 2015.01.12| 5004.368 100.0091
aapl 2015.01.13| 5008.056 100.019
aapl 2015.01.14| 4995.221 100.0486
aapl 2015.01.15| 5016.942 99.99451
aapl 2015.01.16| 5015.834 100.0232
aapl 2015.01.17| 5001.7 99.96238
aapl 2015.01.18| 5005.672 99.99492
aapl 2015.01.19| 4998.789 100.0035
aapl 2015.01.20| 5007.235 100.0249
..
q)deltas0:{first[x] -': x} /利用前面我们介绍的each-previous(':)来返回所给数据的一个增量
q)select dt, avgpx by sym from atrades where 0< deltas0 avgpx
 /这里我们查询的是atrades表(每支股票的每天的平均交易与平均价格的数据)中的那些股票有上涨的数据(只要满足后一条数据比前一条大,不是连续的一直增长)
image
q)select 2#dt, 2#avgpx by sym from atrades where 0
image
q)dntrades~`sym xasc `sym xgroup trades
1b
q)select 2#dt, 2#tm, 2#qty, 2#px by sym from trades
image
q)select sym, cnt: count each dt, avgpx: avg each px from dntrades
sym cnt avgpx
---------------------
aapl 3333508 100.0015
goog 3336580 600.0047
ibm 3329912 200.0113
q)select sym, favgpx: favg each px from dntrades
sym favgpx
-------------
aapl 50.00125
goog 300.0098
ibm 100.0079
q)select sym, vwap:qty wavg' px from dntrades
sym vwap
-------------
aapl 100.0014
goog 600.0047
ibm 200.0067
q)select max px-mins px by sym from trades 
/对于股票数据,我们很多时候也会关注股票的最高价和最低价,下面就是我们查询一直股票在交易记录中的最高价与最低价的一个差值
sym | px
----| ---
aapl| 20
goog| 120
ibm | 40
q)select min px-maxs px by sym from trades /与上面一样,
sym | px
----| ----
aapl| -20
goog| -120
ibm | -40

3. 数据透视表

在Excel中,数据透视表其实就是把表的一个版面布置进行改变,以便按照不同方式分析数据,也可以重新安排行号、列标和页字段。其实就是表现有表一个重新布局操作(练习这个之前我们可以先去看看Excel的数据透视表的具体逻辑和操作,可以参考这个:https://baijiahao.baidu.com/s?id=1612743724635387723&wfr=spider&for=pc)。比如我们在Q中有下面一表t(包含了周几week,姓名name,销售量sales):

q)show t:([]week:1 2 3 2 3; name:`zhangsan`lisi`zhangsan`wangwu`lisi;  sales:100 200 300 400 500)
week name sales
-------------------
1 zhangsan 100
2 lisi 200
3 zhangsan 300
2 wangwu 400
3 lisi 500

但是我们好像看不出这张表的整体数据情况一下,我们想一眼就能看出每个人对应的一个每天的一个具体销量,比如如下的形式:

week| zhangsan lisi wangwu
----| --------------------
1 | 100
2 | 200 400
3 | 300 500

这样我们就能够一下看出这张表的数据结构信息,如zhangsan在周一(1)和周三(3)的销售量分别是100和300,在周二(2)没有销售数据。跟Excel一样,直观的展现出来。这种操作在Q中也叫数据透视表。具体操作如下:

q) N:exec distinct name from t /首先我们需要确定name字段去重后有哪些人 
q)N 
`zhangsan`lisi`wangwu   /这是去重后的信息
q)exec name!sales by week from t /然后我们需要把每个人和他每天的一个销量对应起来,并且人和销量转换成字典对应的形式,并用天来分组
1| (,`zhangsan)!,100
2| `lisi`wangwu!200 400
3| `zhangsan`lisi!300 500
q)exec N#name!sales by week:week from t 
/这个时候我们用去重后的人的信息作为表的新的字段,然后name!sales去匹配新的字段并更新,没有匹配上的直接用空值表示,则得到如下信息
week| zhangsan lisi wangwu
----| --------------------
1 | 100
2 | 200 400
3 | 300 500

当然上述操作我们也可以使用前面介绍的函数式查询,就是利用如下形式来操作:

?[t;c;b;a] / select 与 exec查询操作方式
![t;c;b;a] / update 与 delete查询操作方式
N:exec distinct name from t 可以转换为:
N:?[t; (); (); (distinct; `name)]

exec N#name!sales by week:week from t 可以转换为:
?[t;(); (1#week)!1#week; (#;N;(!;name;`sales))]

当然了,我们也可以将上面的函数式查询写成一个函数形式,后面对于同样的表,我们只需要传递参数即可:

q) dopivot:{[t; kn; pn; vn]
 P:?[t; (); (); (distinct; pn)];
 ?[t;(); (1#kn)!1#kn; (#;`P;(!;pn;vn))]}
q)dopivot[t; `week; `name; `sales] /通过传递参数的形式来生成数据透视表
week| zhangsan lisi wangwu
----| --------------------
1 | 100
2 | 200 400
3 | 300 500

同样我们现在有下面一张简单的表tn,他的p字段是一个复杂的字段,包含symbol数据和int数据:

q)tn:([] k:1 2 3 2 3; p:(`a1;2;`a1;3;2); v:100 200 300 400 500)
q)tn
k p v
---------
1 `a1 100
2 2 200
3 `a1 300
2 3 400
3 2 500

这时我们也可以做数据透视表,但是使用之前的形式就不可以了:

q)dopivot[t; `k; `p; `v] 
'type  
/我们使用上面的形式就会报错,因为数据类型不匹配,
这个时候我们就需要对tn表的一个p字段进行处理一下,都转换成symbol形式,
2我们可以转换为`X_2的形式,3为`X_3,具体转换方式如下,我们也可以写成函数的形式:
q)mkNames:{
 x:(::),x;
 x:1_x:@[x; where not 10h=type each x; string];
 `$@[x; where not any x[;0] within/: ("AZ";"az"); "X_",]}

mkNames函数中,我们使用的是隐式传递参数的形式,这里的参数就是x,第一行的目的是将传进来的数据转换成列表的形式(至于为什么要把::放在最前面我也没看懂为啥,等我后面弄懂了修改这里吧,如果有懂了的小伙伴可以分享给我一下),第二行的目的是将x列表中不是字符串的形式转换为字符转,第三行的意思是将x(此时为字符串式的列表)中那些不包含字母的字符串转换为“X_原数据”的形式。

接下来我们就可以写可以处理复杂字段的函数式生成数据透视表的函数了,具体如下:

q)dopivot:{[t; kn; pn; vn]
 t:![t; (); 0b; (1#pn)!enlist (`mkNames; pn)];
 P:?[t; (); (); (distinct; pn)];
 ?[t;(); (1#kn)!1#kn; (#;`P;(!;pn;vn))]}

该函数的后面两行与前面我们写的dopivot的形式一样,只是在前面多加了一行t:![t; (); 0b; (1#pn)!enlist (`mkNames; pn)];这一行的目的就是讲pn字段转换为a1 X_2 X_3这种形式。

q)dopivot1[tn; `k; `p; `v] /最后就可以利用该函数生成新的数据透视表了
k| a1 X_2 X_3
-| -----------
1| 100
2| 200 400
3| 300 500

下面我们又有一张新表tr,同样可以利用dopivot函数生成数据透视表:

q)tr:([]k:1 2 3 2 3 1; p:`a1`a2`a1`a3`a2`a1; v:100 200 300 400 500 1000)
q)tr
k p v
---------
1 a1 100
2 a2 200
3 a1 300
2 a3 400
3 a2 500
1 a1 1000
q)dopivot1[tr;`k;`p;`v]
k| a1 a2 a3
-| -----------
1| 100
2| 200 400
3| 300 500

但是通过对比,我们发现生成的数据透视表少了一条数据,由于k为1时,对应的a1有两条v的数据,但是只能在数据透视表中放一个数据(不能放复杂的列表),因此只选择了放第一次匹配上的数据100,然后就将1000这条数据进行了省略。但是我们可以选择将这两条数据加起来处理,具体处理方式如下:

q)dopivot2:{[t; agg; kn; pn; vn]
 t:![t; (); 0b; (1#pn)!enlist (`mkNames; pn)];
 t:?[t; (); (kn,pn)!kn,pn; (1#vn)!enlist (agg;vn)];
 P:?[t; (); (); (distinct; pn)];
 ?[t; (); (1#kn)!1#kn; (#;`P;(!;pn;vn))]}

在函数dopivot2中,我们又对传进来的表t进行了处理,t:?[t; (); (kn,pn)!kn,pn; (1#vn)!enlist (agg;vn)];是将pn字段中相同的记录中对应的vn数据加起来。

q)dopivot2[tr;sum;`k;`p;`v] /最后我们生产的数据透视表如下,可以看书对a1数据的加处理:
k| a1 a2 a3
-| ------------
1| 1100
2| 200 400
3| 300 500

你可能感兴趣的:(Q语言——表的高级查询语言(三))