SQL作为常使用的结构化数据处理语言存在如下问题
难写难调试
SQL不提倡过程,难以实现复杂计算,经常要写得很长,难写难调试
过于封闭
SQL强依赖数据库,无法计算文件等库外数据,很难跨库计算
*扩展阅读:SQL像英语是个善意的错误
结构化数据计算一直是数据处理的主流
分析处理结构化数据的三种方式
SQL作为常使用的结构化数据处理语言存在如下问题
SQL不提倡过程,难以实现复杂计算,经常要写得很长,难写难调试
SQL强依赖数据库,无法计算文件等库外数据,很难跨库计算
*扩展阅读:SQL像英语是个善意的错误
select max(continuousDays)-1 from (select count(*) continuousDays from (select sum(changeSign) over(order by tradeDate) unRiseDays from (select tradeDate, case when closePrice>lag(closePrice) over(order by tradeDate) then 0 else 1 end changeSign from stock) ) group by unRiseDays)
SQL在使用窗口函数的情况下嵌套三层完成;
前面读懂了吗?
A | |
---|---|
1 | =stock.sort(tradeDate) |
2 | =0 |
3 | =A1.max(A2=if(closePrice>closePrice[-1],A2+1,0)) |
其实这个计算很简单,按照自然思维:先按交易日排序(行1),然后比较当天收盘价比前一天高就+1,否则就清零,最后求个最大值(行3)
Python相对SQL更加灵活开放,但仍存在如下问题
pandas不是专业结构化数据计算包,在处理分组有序等复杂运算时较为繁琐
pandas等计算包只有专业程序员才能玩得转
*扩展阅读:Python 并不适合职场编程
import time import numpy as np import pandas as pd s = time.time() sales = pd.read_csv("C:\\Users\\Sean\\Desktop\\kaggle_data\\music_project_data\\sales.csv",sep='\t') sales['ORDERDATE']=pd.to_datetime(sales['ORDERDATE']) sales['y']=sales['ORDERDATE'].dt.year sales['m']=sales['ORDERDATE'].dt.month sales_g = sales[['y','m','AMOUNT']].groupby(by=['y','m'],as_index=False) amount_df = sales_g.sum().sort_values(['m','y']) yoy = np.zeros(amount_df.values.shape[0]) yoy=(amount_df['AMOUNT']-amount_df['AMOUNT'].shift(1))/amount_df['AMOUNT'].shift(1) yoy[amount_df['m'].shift(1)!=amount_df['m']]=np.nan amount_df['yoy']=yoy print(amount_df) e = time.time() print(e-s)
Python通过多步算出结果,实现起来比SQL更容易
但Python代码比较繁琐,写出来并不容易
A | |
---|---|
1 | =file("C:\\sales.csv").import@t() |
2 | =A1.groups(year(ORDERDATE):y,month(ORDERDATE):m,sum(AMOUNT):x) |
3 | =A2.sort(m) |
4 | =A3.derive(if(m==m[-1],x/x[-1]-1,null):yoy) |
SPL的分步代码更为简洁,实现更为简单
一些场景下还会使用JAVA、VBA实施结构化数据计算
JAVA和VBA都不是为结构化数据计算设计的,缺少集合化计算类库,编码过于复杂
SPL特别适合复杂过程运算
专门针对结构化数据表设计,类库丰富,远超SQL和Python
结构化数据总是批量形式
集合成员可游离在集合外存在,方便单独引用;独立运算或及其它游离成员再组合新集合运算
允许游离成员组成新集合
运算不仅与数据本身有关,还和数据所在位置有关
A | |
---|---|
1 | =人员表.group(生日).select(~.count(1)>1).conj() |
A | |
---|---|
1 | =V.len() |
2 | =V.to(A1\4+1,A1*3\4).run(value-=V(A1\2).value) |
A | B | |
---|---|---|
1 | ... | |
2 | =A1.select(height>=1.7) | =A1.select(sex=="Female") |
3 | =A2^B2 | =A1.select(height>=1.7&&sex=="Female") |
4 | =A2\B2 | =A1.select(height>=1.7&&sex!="Female") |
A | B | |
---|---|---|
1 | ... | ... |
2 | =A1.select(sex=="Male") | =A1.select(sex=="Female") |
3 | =A2.conj(B2.([A2.~,~])) | =A3.minp@a(abs(~(1).height-~(2).height)) |
A | |
---|---|
1 | ... |
2 | =A1.group((#+2)\3) |
3 | =A1.group(#%3+1) |
A | |
---|---|
... | ... |
3 | =A2.derive(price-price[-1]:gain) |
4 | =A2.derive(price[-1:1].avg():mavg) |
WITH TT AS (SELECT RANK() OVER(PARTITION BY uid ORDER BY logtime DESC) rk, T.* FROM 登录表 T) SELECT uid,(SELECT TT.logtime FROM TT where TT.uid=TTT.uid and TT.rk=1) -(SELET TT.logtim FROM TT WHERE TT.uid=TTT.uid and TT.rk=2) 间隔 FROM 登录表 TTT GROUP BY uid
聚合函数返回值不一定是单值,也可以返回一个集合
彻底的集合化后很容易针对分组子集实施返回集合的聚合运算
A | ||
---|---|---|
1 | =登录表.groups(uid;top(2,-logtime)) | 最后2个登录记录 |
2 | =A1.new(uid,#2(1).logtime-#2(2).logtime:间隔) | 计算间隔 |
WITH B AS (SELECT LAG(销售额) OVER (ORDER BY 月份) f1, LEAD(销售额) OVER (ORDER BY 月份) f2, A.* FROM 销售表 A) SELECT 月份,销售额, (NVL(f1,0)+NVL(f2,0)+销售额)/(DECODE(f1,NULLl,0,1)+DECODE(f2,NULL,0,1)+1) 移动平均 FROM B
窗口函数只有简单的跨行引用,涉及集合要用成员去拼
有序集合上可提供跨行集合引用方案
A | |
---|---|
1 | =销售表.sort(月份).derive(销售额[-1,1].avg()):移动平均) |
SELECT max(连续日数) FROM (SELECT count(*) 连续日数 FROM (SELECT SUM(涨跌标志) OVER ( ORDER BY 交易日) 不涨日数 FROM (SELECT 交易日, CASE WHEN 收盘价>LAG(收盘价) OVER( ORDER BY 交易日 THEN 0 ELSE 1 END 涨跌标志 FROM 股票 )) GROUP BY 不涨日数)
另一种和次序有关的分组,条件成立时产生新组
A | |
---|---|
1 | =股票.sort(交易日).group@i(收盘价 < 收盘价[-1]).max(~.len()) |
WITH A AS (SELECT 代码,交易日, 收盘价-LAG(收盘价) OVER (PARITITION BY 代码 ORDER BY 涨幅) FROM 股票) B AS (SELECT 代码, CASE WHEN 涨幅>0 AND LAG(涨幅) OVER (PARTITION BY 代码 ORDER BY 交易日) >0 AND LAG(涨幅,2) OVER PARTITION BY 代码 ORDER BY 交易日) >0 THEN 1 ELSE 0 END 三天连涨标志 FROM A) SELECT distinct 代码 FROM B WHERE 三天连涨标志=1
分组子集与有序计算的组合
A | |
---|---|
1 | =股票.sort(交易日).group(代码) |
2 | =A1.select((a=0,~.pselect(a=if(收盘价>收盘价[-1],a+1,0):3))>0).(代码) |
SELECT AVG(涨幅) FROM ( SELECT 交易日, 收盘价-LAG(收盘价) OVER ( ORDER BY 交易日) 涨幅 FROM 股价表 WHERE 交易日 IN (SELECT 交易日 FROM (SELECT 交易日, ROW_NUMBER() OVER(ORDER BY 收盘价 DESC) 排名 FROM 股票) WHERE 排名<=3 )
无序集合不能利用位置访问相邻成员,计算量增大
有序集合可以提供丰富的按位置访问机制
A | |
---|---|
1 | =股票.sort(交易日) |
2 | =A1.calc(A1.ptop(3,-收盘价),收盘价-收盘价[-1]).avg() |
过滤、计算列、位置引用、相邻引用、排序/排名、列上的宏运算
分组汇总/分组子集、枚举分组/序号分组、有序分组/条件分组、转置与扩展
集合交并差、归并、外键关联、同维主子关联
…
SPL与Excel配合可以增强Excel计算能力,降低计算实施难度
通过SPL的Excel插件可以在Excel中使用SPL函数进行计算,也可以在VBA中调用SPL脚本
SPL可以调用报表工具生成、输出报表
SPL提供了外存游标、并行计算等机制实现大数据文件计算
Python未提供大数据支持,需要手写代码,非常繁琐
A | B | |
---|---|---|
1 | =file(file_path).cursor@tc() | /创建大文件游标 |
2 | =A1.sortx(key) | /游标排序 |
3 | =file(out_file).export@tc(A2) | /结果流式输出到文件 |
通过SPL可以针对文本、Excel等文件使用SQL进行查询;提供不依赖于数据库的SQL查询能力
A | |
---|---|
1 | $select CLASS,min(English),max(Chinese),sum(Math) from E:/txt/students_scores.txt group by CLASS |
A | |
---|---|
1 | $select sum(S.quantity*P.Price) as totalfrom E:/txt/Sales.txt as S join E:/txt/Products.txt as P on S.productid=P.ID where S.quantity<=10 |