Python 和 SPL 对比 5——有序运算
人们天然对序运算感兴趣,比上期、同期比等都是序运算,本文对比 Python 和 SPL 的有序运算。
排序
排序是最常见的运算了,如:
按日期对代码为 000062 的股票排序。
股票数据如下:
Python
import pandas as pd stock_file="D:/data/STOCKS.csv" stock_data=pd.read_csv(stock_file,dtype={'STOCKID':'object'}) stock_62=stock_data.query('STOCKID=="000062"').copy() stock_62["DATE"]=pd.to_datetime(stock_62["DATE"]) stock_62.sort_values(by="DATE",inplace=True) print(stock_62) |
导入 pandas 股票数据路径 读股票数据 选出股票 转换日期格式 按日期排序
|
Pandas提供了 sort_values 函数来对某列排序,操作起来还是很方便的。
SPL
A |
B |
|
1 |
D:\data\STOCKS.csv |
/股票数据路径 |
2 |
=file(A1).import@tc(#1:string,#2,#3) |
/读股票数据 |
3 |
=A2.select(STOCKID=="000062") |
/选出股票 |
4 |
=A3.run(DATE=date(DATE,"yyyy/MM/dd")) |
/转换日期格式 |
5 |
=A3.sort(DATE) |
/排序 |
SPL用 sort(…) 函数来排序。
我们偶尔也需要对多列进行不同方向的排序,如:
将所有股票数据按股票代码升序,股价降序排序。
Python
#续用 stock_data stock_sort2key=stock_data.sort_values(by=["STOCKID","CLOSING"],ascending=[1,0]) print(stock_sort2key) |
多列不同方向排序
|
sort_values()中的 by 参数支持多列,ascending 参数支持不同方向排序,还是比较方便的。
SPL
A |
B |
|
… |
/A2是股票信息 |
|
7 |
=A2.sort(STOCKID,-CLOSING) |
/读股票数据 |
SPL仍用 sort(…) 函数完成多键,不同方向排序,同样方便。
排序后的位置 / 索引
排序后的位置信息也很重要,有时也会用到这个信息,如:
计算 000062 股票股价最高的三天的涨幅。
Python
#续用 stock_62 sort_pos=(-stock_62["CLOSING"]).argsort() max3pos=sort_pos.iloc[:3] stock_62s=stock_62.shift(1) max3CL=stock_62["CLOSING"].iloc[max3pos] max3CLs=stock_62s["CLOSING"].iloc[max3pos] max3_rate=max3CL/max3CLs-1 print(max3_rate) |
按 CLOSING 倒序排序后返回位置 取前 3 个位置 股票信息下移一行 CLOSING最高的 3 天价格 CLOSING最高的 3 天的前一天价格 CLOSING最高的 3 天股票涨幅
|
Python的 argsort(…) 可以返回排序后的位置信息。由于 Python 中没有循环函数,也不可以在循环过程中利用位置信息来计算,只能绕一下,先找到股价最高的 3 天的股价,再找到股价最高 3 天的前一天的股价,两者计算后得到涨幅,有点麻烦。
SPL
A |
B |
|
… |
/A3是 000062 股票信息 |
|
9 |
=A3.psort@z(CLOSING) |
/股价排序位置 |
10 |
=A9.m(:3) |
/取前 3 个位置 |
11 |
=A3.calc(A10,if(#==1,null,CLOSING/CLOSING[-1]-1)) |
/按位置算涨幅 |
SPL中的 psort(…) 函数返回从小到大的位置信息,@z 选项则是逆序。calc(…) 函数是定位计算,利用成员的位置和相对位置进行计算。CLOSING[-1] 是当前成员的前一个成员。
这里计算股价最高 3 天的位置信息也可以用 ptop(…) 函数,这样省去了大排序,计算效率更高。如:A9 就不需要了,A10=A3.ptop(-3,CLOSING),A11 正常算。
延伸一下,当需要利用多列不同方向位置信息时,Python 和 SPL 又都是怎么处理的呢?
计算按股票代码升序、股价降序排序后的位置 / 索引。
Python
#续用 stock_data import numpy as np sort2key_pos=np.lexsort((-stock_data["CLOSING"],stock_data["STOCKID"])) print(sort2key_pos) |
导入 numpy 多列不同方向排序位置 / 索引
|
Python又是一个新函数 lexsort(…),而且还不是 Pandas 库里的,是 Numpy 库里的。正常的逻辑是先按 STOCKID 升序,再按 CLOSING 降序的,通常是从左往右重要度依次降低,但是 Python 中必须把先排序的 STOCKID 放在最后,然后是 CLOSING;如果是三个或者更多列排序,重要度要从右往左排,有点反人性了。而且这个函数没有逆序的参数,只能用负号“-”来表示逆序,但是 Python 又不支持字符串用负号,就会报错。这个问题如果是按 STOKID 降序,CLOSING 升序来排序,lexsort() 函数就用不了了,还得另想办法,感兴趣的同学可以动手试试看 Python 会有什么反应,代码是这样的:
np.lexsort((stock_data["CLOSING"],-stock_data["STOCKID"]))。
SPL
A |
B |
|
… |
/A2是股票信息 |
|
13 |
=A2.psort(STOCKID,-CLOSING) |
/多列不同方向排序位置 / 索引 |
SPL就轻松太多了,和 sort 的函数完全一样,psort() 自然返回排序后的位置。而且 SPL 支持字符串取负号(-STOCKID 就是逆序排序)。
相邻记录引用
数据分析中,经常引用某种次序下的相邻记录来计算,如:
计算代码为 000062 的股票每天的涨跌幅。
Python
#续用上例中的 stock_62 stock_62s=stock_62["CLOSING"].shift(1) stock_62["RATE"]=stock_62["CLOSING"]/stock_62s-1 print(stock_62) |
前一天收盘价 计算涨跌幅
|
Python的 shift(n) 函数是移动 n 行数据,参数 n 可以是正数也可以是负数,如 1 是向下移动一行,-1 是向上移动一行,这样就可以利用移动后的数据来和当前行计算了。
SPL
A |
B |
|
… |
/A3是 000062 股票信息 |
|
15 |
=A3.derive(if(#==1,null,CLOSING/CLOSING[-1]-1):RATE) |
/增加涨跌幅 |
SPL中的循环函数,CLOSING[-1] 是在当前记录之前 1 个单位记录(即前一条记录)的 CLOSING 值,CLOSING[-1]=~[-1].CLOSING,这和上例中的 CLOSING[-1] 是一个道理。
SPL利用相对位置完成这样的运算,不像 Python 那样移动所有行,语法简洁性和运算效率都好。
移动平均也是常见的运算,如:
计算 000062 股票连续 5 个交易日的移动平均值。
Python
#续用的 stock_62 stock_62["MAVG"]=stock_62["CLOSING"].rolling(5,min_periods=1).mean() print(stock_62) |
计算移动平均 |
函数 rolling() 可以轻松计算移动平均,它算完以后是个对象,后续需要接聚合函数,如接 mean 就是移动平均,接 sum 就是移动求和,“对象”这个东西不太好理解。而且算前一行和相邻的 5 行平均值属于同一类运算,但 Python 确是两个不搭边的函数,shift 和 rolling。
SPL
A |
B |
|
… |
/A3是 000062 股票信息 |
|
17 |
=A3.derive(CLOSING[-4:0].avg():DAY5) |
/移动平均 |
SPL中还是用 CLOSING[…]形式来完成,只不过 [-4:0] 表示从之前第 4 行到当前行的全部记录。CLOSING[-4:0]=~[-4:0].(CLOSING)。这些相邻记录引用的形式是统一的,非常容易理解和记忆。这与序列的 ~[…]用法也一致,序列 ~[…]的用法在《Python 和 SPL 对比 3——循环函数》中的‘相邻引用’一节有过介绍。
小结
Python关于序运算的函数还是比较丰富的,但有点简单粗暴,如没有相对位置,只能将全体上移或下移,或者干脆用个对象来代替;位置计算的函数比较薄弱,有些需要自己硬编码或者“绕路”,编写程序和运行效率都会大打折扣。在函数的设计上也比较杂乱,不容易记忆。
SPL在序运算方面非常专业,拥有丰富的序运算函数,相对位置与绝对位置都可以在循环时轻松拿到,而且函数都是精心设计的,如 (sort,psort), (select,pselect), (max,pmax,maxp), (top,ptop)等等,函数意义类似,形式也类似,用起来就像乐高搭积木一样,知道几个原子函数(基本的积木形状)后很容易写出各种复杂代码(房子,飞机等复杂形状)。
英文版