交互数据分析从 Excel 到 SPL:股票连涨分析
本文讨论股票连涨问题,为了避免歧义,这里约定:股票连涨天数包括起始的 1 天,比如连涨 5 天意味着这 5 天内股票都在上涨,实际上只有从第 2 天开始的 4 次上涨。
数据结构和样例数据:
其中 CODE 是股票代码,DT 是交易日期,CL 是收盘价。
1. 某支股票最长连涨天数
以代码是 100046 的股票为例,分别用 Excel 和 SPL 实现。
Excel:
用筛选和排序菜单,选出代码为 100046 的股票并按照日期排序
然后将选出的数据复制出来,增加一列 UP,用来记录股票的上涨天数。
D2 格写入公式:
=IF(C2>C1,D1+1,1)
然后拖动公式,效果如图。
最后计算 UP 列的最大值即可。
SPL 可以用相同的思路实现
A |
B |
|
1 |
=T("StockRecords.xlsx") |
=0 |
2 |
=A1.select(CODE==100046).sort(DT) |
|
3 |
=A2.derive(B1=if(CL>CL[-1],B1+1,1):UP) |
|
4 |
=A3.max(UP) |
SPL 是一种程序语言,但它的代码看起来和 Excel 很像,也是在格子写公式,且可以引用格子的值。
A1 格表示导入股票数据。
SPL 有很强的交互性,可以实时查看每一步的计算结果。点击 A1 格,在界面右边就可以看到 A1 格中数据。
SPL 可以一次执行所有代码,也可以单步执行每个格子里的代码。执行过的代码都可以在右边界面查看结果。
A2 格表示选出代码为 100046 的股票,并按时间排序。
A3 增加一列 UP,记录股票的上涨天数。derive()表示新增一列,CL[-1] 表示上一行的收盘价。
A4 计算 UP 最大值,得到最长上涨天数。
SPL 写法简单,用起来和 Excel 很像。用 Excel 解决问题的思维方法,在 SPL 可以很自然的用相同的方法做出来。这对于习惯用 Excel 的同学来说,很是友好,学起来很容易。
用编程语言的好处就是它的操作过程是可以记录下来的,相同流程可以复制。比如本题,当数据源更新时,SPL 只需替换下 A1 格子里的数据源即可。而 Excel 则需要在新数据上重新操作。
当然编程语言的计算能力更强是它的另一大好处。
例如本题,熟悉 SPL 编程后,还可以省略生成 UP 列,直接计算最大值。
A |
B |
|
1 |
=T("StockRecords.xlsx") |
=0 |
2 |
=A1.select(CODE==100046).sort(DT) |
|
3 |
=A2.max(B1=if(CL>CL[-1],B1+1,1)) |
A3 格里 A2.()是一个循环函数,表示对 A2 的每一行计算 if() 值,然后返回最大值。
又或者在 SPL 里还可以用分组的思想来解决本题。
A |
|
1 |
=T("StockRecords.xlsx") |
2 |
=A1.select(CODE==100046).sort(DT) |
3 |
=A2.group@i(CL<=CL[-1]) |
4 |
=A3.max(~.len()) |
A3: group()是分组函数,@i 是一种分组方式。group@i() 表示当括号里的表达式 CL<=CL[-1] 为真时,产生新的分组,即当股票下跌时就开始新的分组。这样连续上涨的日期数据就被分到了一组。结果如下图,数据被分成了多个组。双击某个组,就可以看该组的成员数据。如双击第 3 组,可以看到该组详情。
A4: 计算各组的长度,求最大值即可得到最长连涨的天数。
2. 某支股票最长连涨区间的起止日期
Excel 可以在上一题的基础上继续,在 UP 列筛选出 UP 值为 4 的数据,得到最长连涨的终止日期,终止日期 -UP-1 就可以得到连涨开始日期。然后复制出来就可以得到右图结果。
SPL 也可以用同样的思路解决。
A |
B |
|
1 |
=T("StockRecords.xlsx") |
=0 |
2 |
=A1.select(CODE==100046).sort(DT) |
|
3 |
=A2.derive(B1=if(CL>CL[-1],B1+1,1):UP) |
|
4 |
=A3.maxp@a(UP) |
|
5 |
=A4.new(DT-UP-1:start_DT,DT:end_DT) |
A4:取出 UP 最大的所有数据。maxp() 会取出 UP 值最大的整行数据,@a 则表示最大值有多个时取出所有。因此 A4 格就取出了所有 UP 值最大的数据。
A5: 新建一个表,得到连涨区间的起止日期。
Excel 的最大值计算只能返回最大值本身(如本题中的 4),如果要获得最大值所在行的其他数据,则要配合筛选菜单功能来实现,有点繁琐。
SPL 有更丰富的聚合运算,比如 max() 取最大值,pmax 取最大值的位置,maxp 取最大值所在的整行数据。需要什么直接获取。
当然用分组的思想更加方便,不用生产 UP 列
A |
|
1 |
=T("StockRecords.xlsx").select(CODE==100046).sort(DT) |
2 |
=A1.group@i(CL<CL[-1]).maxp@a(~.len()) |
3 |
=A2.new(~(1).DT:start,~.m(-1).DT:end) |
A2: 把上一题的 max,改成 maxp,取出所有连涨区间的数据
A3: 取每组的第一行和倒数第一行的 DT 值,就是对应的起止日期。
3. 找出最长连涨天数超过 5 天的股票
Excel 按照 CODE 和 DT 排序,添加 UP 列,记录连涨天数。
在 D2 格输入:
=IF(AND(A2<>A1, C2>C1),D1+1,1)
拖动公式。
再用数据透视表,分类汇总每支股票的最大 UP 值
将汇总值复制出来,筛选出最大值大于 5 的股票
SPL 也可以用同样的思路写出来:
A |
B |
|
1 |
=T("StockRecords.xlsx").sort(CODE,DT) |
0 |
2 |
=A1.derive(B1=if(CODE==CODE[-1] && CL>CL[-1],B1+1,1):UP) |
|
3 |
=A2.groups(CODE;max(UP):max_up) |
|
4 |
=A3.select(max_up>5) |
A1: 读入数据,按照 CODE 和 DT 排序。
A2: 添加辅助列 UP 记录连涨天数。SPL 的 if() 函数支持多条件判断,不用多层嵌套。
A3: 分类汇总每支股票的最大 UP 值。groups 为分类汇总函数。
A4: 筛选出最长连涨天数超过 5 天的股票。
SPL 函数的写法很直观,会用 Excel,很容易就能看懂 SPL 代码。
使用程序语言会记录每一个操作步骤下次遇到同样问题或是数据源更新,代码可重复使用,无需重新操作。而 Excel 则无法记录进行了哪些操作步骤,遇到同样的问题就要重来一遍。而且 Excel 操作中还会产生一些中间数据,时间一久,很容易乱了。
本题也可以用只分组不汇总的方法实现,无需添加 UP 列。
A |
|
1 |
=T("StockRecords.xlsx").sort(DT) |
2 |
=A1.group(CODE;~.group@i(CL<=CL[-1]).max(~.len()):max_increase_days) |
3 |
=A2.select(max_increase_days>5) |
A2:先按照 CODE 分组,然后对每个子成员再次分组,将连续上涨的数据分为一组,然后计算每支股票的最长上涨天数。~ 表示当前循环的子成员,比如这里表示当前循环的股票组。
Excel 里只有分类汇总函数,有些需要在分类(分组)子集上进行的计算,就有些麻烦,如下一题。
SPL 里除了分类汇总函数 groups(),还有只分类(分组)不汇总的函数 group()。group() 函数可以保留分组子集,以便进一步计算。
4. 找出所有连涨超过 5 天的区间
Excel 分组时必须强制聚合,不能保留分组子集,此题做起来就比较麻烦。
先筛选出连涨 5 天的股票后,需要和任务 2 类似,倒推出上涨区间的日期,然后再根据 CODE 和日期查找对应股票价格。多条件查找 Excel 写起来也比较复杂,这里就不再做了。
SPL 能保持分组子集,就很容易解决这个问题:
A |
|
1 |
=T("StockRecords.xlsx").sort(CODE,DT) |
2 |
=A1.group@i(CODE!=CODE[-1] || CL<=CL[-1]) |
3 |
=A2.select(~.len()>5).conj() |
A2 分组条件的意思是股票代码和上一条不同或价格不涨时分新组。CODE[-1] 和 CL[-1] 类似,表示上一条记录的股票代码。由于数据整体上是对 CODE 有序的,这样可以保证不同的股票不会分到同一组。
A2 只做分组,不做聚合,保留了分组子集:
A3 过滤出长度大于 5 的分组,再将这些分组子集合并起来,就得到了期望的结果。
演示数据