结构化数据上用序号访问成员的问题
【摘要】
序号访问是指通过序号(索引下标)来访问有序集合的成员。序号访问除了按单个序号访问集合成员,还有按多个序号同时取多个成员的需求。除此以外,还有用倒数的序号访问成员,按固定跨度访问成员等等。如何简单快捷的实现这些序号访问需求?这里将为你全程剖析,并提供 esProc SPL 示例代码。结构化数据上用序号访问成员的问题
1. 单序号访问
我们可以按照单个序号,从数据表中获取一条记录。
【例 1】 求 2019 年上证指数第一个交易日和最后一个交易日的交易信息。部分数据如下:
Date |
Open |
Close |
Amount |
2019/12/31 |
3036.3858 |
3050.124 |
2.27E11 |
2019/12/30 |
2998.1689 |
3040.0239 |
2.67E11 |
2019/12/27 |
3006.8517 |
3005.0355 |
2.58E11 |
2019/12/26 |
2981.2485 |
3007.3546 |
1.96E11 |
2019/12/25 |
2980.4276 |
2981.8805 |
1.9E11 |
… |
… |
… |
… |
【SPL脚本】
A |
B |
|
1 |
=file("000001.csv").import@ct() |
/导入数据文件 |
2 |
=A1.select(year(Date)==2019).sort(Date) |
/选出 2019 年的记录并按日期排序 |
3 |
=A2(1)|A2.m(-1) |
/取出上证指数第一个和最后一个交易日的信息。使用了函数 A2(1) 取序表第一条记录,函数 A2.m(-1) 取序表的倒数第一条记录。 |
A3的执行结果如下:
Date |
Open |
Close |
Amount |
2019/01/02 |
2497.8805 |
2465.291 |
9.76E10 |
2019/12/31 |
3036.3858 |
3050.124 |
2.27E11 |
有时候我们需要获取指定位置成员的序号,尤其是倒着取集合成员的序号。比如 2019 年上证指数的记录按时间排序后,倒数第二个交易日的序号是多少?这时我们希望通过参数 -2 来获取真实的序号。
【例 2】 以员工表为例,统计 [California, Texas, New York, Florida] 各州的平均工资,其他地区的员工存放到新组统计。部分数据如下:
ID |
NAME |
STATE |
SALARY |
1 |
Rebecca |
California |
7000 |
2 |
Ashley |
New York |
11000 |
3 |
Rachel |
New Mexico |
9000 |
4 |
Emily |
Texas |
7000 |
5 |
Ashley |
Texas |
16000 |
… |
… |
… |
… |
【SPL脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据库 |
2 |
=A1.query("select * from EMPLOYEE") |
/查询雇员表 |
3 |
[California,Texas,New York,Florida] |
/创建地区集合 |
4 |
=A2.align@an(A3,STATE) |
/雇员表按地区对位分组,@a 选项每组返回所有匹配成员,@n 选项不匹配成员存放到新组。 |
5 |
=A4.new(if (#>A3.p(-1),"Other",STATE):STATE,~.avg(SALARY):AvgSalary) |
/统计每组的平均工资,产生新序表。使用函数 A.p(-1) 获取最后一个成员的序号,把最后一组的地区更名为 Other。 |
A5的执行结果如下:
STATE |
SALARY |
California |
7700.0 |
Texas |
7592.59 |
New York |
7677.77 |
Florida |
7145.16 |
Other |
7308.1 |
2. 多序号访问
前面介绍了如何使用单个序号访问集合成员,有时候需要根据多个序号获取多条记录。例如对于某年按月份有序的销售记录表,使用序号集合 [4,5,6] 可以访问第二季度的数据。对于一周的值班表,使用序号集合 [1,7] 可以访问周末的值班记录。
【例 3】 有一个记录日常考勤信息的表,如下图:
Per_Code |
in_out |
Date |
Time |
Type |
1110263 |
1 |
2013-10-11 |
09:17:14 |
In |
1110263 |
6 |
2013-10-11 |
11:37:00 |
Break |
1110263 |
5 |
2013-10-11 |
11:38:21 |
Return |
1110263 |
0 |
2013-10-11 |
11:43:21 |
NULL |
1110263 |
6 |
2013-10-11 |
13:21:30 |
Break |
1110263 |
5 |
2013-10-11 |
14:25:58 |
Return |
1110263 |
2 |
2013-10-11 |
18:28:55 |
Out |
每七条数据为一组,想要转换成如下结果:
Per_Code |
Date |
In |
Out |
Break |
Return |
1110263 |
2013-10-11 |
9:17:14 |
18:28:55 |
11:37:00 |
11:38:21 |
1110263 |
2013-10-11 |
9:17:14 |
18:28:55 |
13:21:30 |
14:25:58 |
【SPL脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据库 |
2 |
=A1.query("select * from DailyTime order by Per_Code,Date,Time") |
/查询数据,并按人员编号、日期和时间排序 |
3 |
=A2.group(Per_Code,Date) |
/按人员编号和日期分组 |
4 |
=create(Per_Code,Date,In,Out,Break,Return) |
/创建一个存放最后结果的空表 |
5 |
=A3.(~([1,7,2,3,1,7,5,6])) |
/对每个组,使用函数 A([1,7,2,3,1,7,5,6]) 依次取出记录,这就是有序的全天记录。 |
6 |
=A5.conj([~.Per_Code,~.Date]|~.(Time).m([1,2,3,4])|[~.Per_Code,~.Date]|~.(Time).m([5,6,7,8])) |
/将每条记录的数据全部整理到一个集合中。其中用到了函数 A.m() 访问多个成员。 |
7 |
>A4.record(A6) |
/将数据添加到 A4 创建的表中。 |
A4的执行结果如下:
Per_Code |
Date |
In |
Out |
Break |
Return |
1110263 |
2013-10-11 |
9:17:14 |
18:28:55 |
11:37:00 |
11:38:21 |
1110263 |
2013-10-11 |
9:17:14 |
18:28:55 |
13:21:30 |
14:25:58 |
类似的,我们也可以从后向前访问多个成员。
【例 5】 求上证指数 2019 年最后 10 个交易日收盘价较前日的涨幅。部分数据如下:
Date |
Open |
Close |
Amount |
2019/12/31 |
3036.3858 |
3050.124 |
2.27E11 |
2019/12/30 |
2998.1689 |
3040.0239 |
2.67E11 |
2019/12/27 |
3006.8517 |
3005.0355 |
2.58E11 |
2019/12/26 |
2981.2485 |
3007.3546 |
1.96E11 |
2019/12/25 |
2980.4276 |
2981.8805 |
1.9E11 |
… |
… |
… |
… |
【SPL脚本】
A |
B |
|
1 |
=file("000001.csv").import@ct() |
/导入数据文件 |
2 |
=A1.select(year(Date)==2019).sort(Date) |
/选出 2019 年的记录并按日期排序 |
3 |
=A2.p(to(-10,-1)) |
/使用函数 A.p() 返回最后 10 个成员的序号 |
4 |
=A3.new(A2(~).Date:Date, string(A2(~).Close/A2(~-1).Close-1, "0.000%" ):Increase) |
/循环计算每个交易日收盘价与前一个交易日的涨幅 |
A4的执行结果如下:
Date |
Increase |
2019/12/18 |
-0.178% |
2019/12/19 |
0.001% |
2019/12/20 |
-0.402% |
2019/12/23 |
-1.404% |
2019/12/24 |
0.673% |
… |
… |
3. 固定跨度访问
固定跨度访问是指按指定的起始序号和固定的跨度访问成员。例如从数据表中抽样,每 10 条记录选出一条,可以从第一条开始取,每 10 条取一次。又比如从 1 到 100 的自然数中,选出所有 3 的倍数,可以从第 3 个数字开始,每 3 个数字取 1 个。
【例 6】 求 100 以内质数。
【SPL 脚本】
A |
B |
|
1 |
=to(100) |
/创建 1 到 100 集合 |
2 |
=to(2,10) |
/创建 2 到 10 集合 |
3 |
=A2.(A1.step(~,~*2)) |
/使用了函数 A1.step(~,~*2),针对 A2 中每一个成员,求出它在 100 以内的 n 倍数 (n>1) |
4 |
=A1.to(2,)\A3.conj() |
除去 1 和所有 100 以内的合数即为 100 以内的质数,其中 A3.conj() 求出 100 以内的合数 |
A4的执行结果如下:
Member |
2 |
3 |
5 |
7 |
11 |
13 |
17 |
19 |
《SPL CookBook》中还有更多相关计算示例。