商城网站开发合同沧州网站推广优化
商城网站开发合同,沧州网站推广优化,wordpress 改中文字体,企业网站优化关键词做数据分析和人工智能运算前常常需要大量的数据准备工作#xff0c;也就是把各种数据源以及各种规格的数据整理成统一的格式。因为情况非常复杂多样#xff0c;很难有某种可视化工具来完成此项工作#xff0c;常常需要编程才能实现。
业界有很多免费的脚本语言都适合进行数…做数据分析和人工智能运算前常常需要大量的数据准备工作也就是把各种数据源以及各种规格的数据整理成统一的格式。因为情况非常复杂多样很难有某种可视化工具来完成此项工作常常需要编程才能实现。
业界有很多免费的脚本语言都适合进行数据准备工作其中Python Pandas具有多种数据源接口和丰富的计算函数受到众多用户的喜爱esProc SPL作为一门较新的数据计算语言在语法灵活性和计算能力方面也很有特色下面对两者进行多方面的比较。本文重点比较数据的解析、清洗、计算、输出等日常任务不涉及人工智能等后续应用或高性能计算等特殊场景。
语言特征
编程范式
Python是通用开发语言支持多范式编程包括完整的面向对象和面向函数但因为大量Python用户不是专业的应用程序员很少用到这两种现代复杂的编程范式最常用的反而是古老简单的面向过程编程范式。
SPL专用于结构化数据计算也支持常见的三种范式。SPL对面向对象的概念进行了大幅简化有对象的概念可以用点号访问属性并进行多步骤计算但没有继承重载这些内容。SPL对函数式编程也进行了简化其Lambda表达式甚至比SQL更加简单易用适合非专业应用程序员。
语言整体性
Pandas不是Python的原生类库而是基于numpy开发的第三方类库numpy本身也是第三方类库,没有参与Python的统一设计也无法获得Python的底层支持导致语言的整体性不佳基础数据类型尤其是结构化数据对象DataFrame的专业性不强影响编码效率和计算效率。
SPL是原生类库可以自底向上设计统一的语法、函数、参数、接口以及基础数据类型尤其是结构化数据对象序表语言的整体性更好。
运行模式
Python是用C开发的解释型语言SPL是用Java开发的解释型语言两者都可以自动推断数据类型并据此提供了灵活方便的语法。解释型语言的性能一般不如编译型但SPL内置大量时间复杂度更低的基础运算结构化计算的性能经常能超过编译型语言。Pandas由于语言整体性较差其性能不如Python原生类库。
IDE
Python和SPL都有图形化的IDE包括完整的调试功能便利的结构化数据对象观察功能直观的代码块/作用域缩进功能。Python采用空格/tab缩进SPL采用类Excel的表格式缩进。
学习难度
Pandas资料丰富入门的学习难度较低。但如果要深入开发就必须学习完整的面向对象编程和函数式编程难度陡然提高。
SPL刻意简化了对象的概念和函数式编程的接口无论入门学习还是深入开发难度都不高。但涉及到高性能计算时需要学习较多特有的算法难度也会提高。
代码量
Pandas库函数丰富实现简单的数据准备任务时只需单独使用自己库函数代码量较低。但如果想实现较复杂的数据准备任务就要大量使用Python原生类库和第三方类库由于Pandas的语言整体性不佳难度会陡然增加代码量也水涨船高。
SPL库函数丰富语言整体性好无论简单任务还是复杂任务代码量都不多。
数据源
数据源种类
Pandas支持多种数据源包括
文本数据文件包括TAB分隔的txt、逗号分隔的csv也可自定义其它分隔符。固定宽度文件fwf各类关系型数据库ExcelJsonXMLRestful、WebServicehtml抓取sasspssstata列存格式Parquet列存格式ORCGoogle BigQuery科学数据HDF数据框feather剪贴板里的结构化数据私有格式pickle。
SPL支持的数据源也很多包括
文本数据文件包括TAB分隔的txt、逗号分隔的csv也可自定义其它分隔符固定宽度文件fwf各类关系型数据库ExcelJsonXMLRestful、WebServicehtml抓取HBaseHDFSHiveSparkElasticsearchMongoDBKafkaR2dbcFTPCassandraDynamoDBinfluxDBRedisSAP剪贴板里的结构化数据私有格式btx、ctx。
读写数据库
用SQL查询数据库用csv文件更新数据库。Pandas
conn create_engine(mysqlpymysql://root:passwordlocalhost:3306/testdb)
df_read pd.read_sql_query(select * from product, conn)
data pd.read_csv(d:/Orders.csv)
data.to_sql(testdf, conn, indexFalse)
conn.dispose()简单读写数据库时Pandas代码足够优雅。
SPL
A 1 connect(com.mysql.jdbc.Driver,jdbc:mysql://localhost:3306/testdb?userrootpasswordpassword) 2 A1.query(select * from product ) 3 T(d:/Orders.csv) 4 A1.update(A3, testdf; ORDERID) 5 A1.close()
SPL代码也很简单整体逻辑与Pandas类似。区别在于,SPL可以把数据源信息写在配置文件里代码里只要简单引用数据源名具体来说A1可以写成connect(“myDB”)
读写文本文件
规则文本读取csv文件简单计算后写入新csv。Pandas
data pd.read_csv(d:/Orders.csv)
data[OrderDate]pd.to_datetime(data[OrderDate])
resultdata.groupby(data[OrderDate].dt.year).agg({Amount:[len,np.sum]})
result.to_csv(d:/resultP.csv)Pandas代码很简洁但仍有不足之处一是不能自动解析日期时间类型二是计算代码里大中小括号都有既有表达式又有字符串有明显的可优化之处语言整体性不佳。
SPL实现相同的功能
A 1 T(d:/Orders.csv) 2 A1.groups(year(OrderDate);count(1),sum(Amount)) 3 file(d:/resulS.csv).exportt(A2)
SPL代码也很简洁且可自动解析日期时间类型可以只用一种括号可以只用表达式语言整体性极佳。
不规则的文本每三行对应一条记录其中第二行含三个字段集合的成员也是集合将该文件整理成规范的结构化数据对象。Pandas:
data pd.read_csv(d:/threeLines.txt,headerNone)
pos_seq[i//3 for i in range(len(data))]
def runSplit(x):f123x.iloc[1,0].split(\t)f[x.iloc[0,0],f123[0],f123[1],f123[2],x.iloc[2,0]]return pd.DataFrame([f], columns[OrderID,Client,SellerId,Amount,OrderDate])
dfdata.groupby(pos_seq).apply(runSplit)
df.reset_index(dropTrue, inplaceTrue) #drop the Second Index上述解析过程大体分三步先将文本读为单字段的DataFrame再进行有序分组即每三行分一组最后循环每一组将组内数据拼成单记录的DataFrame循环结束时合并各条记录形成新的DataFrame。 遇到不规则的文本时Pandas代码明显变复杂了体现在以下几处。制造形如[0,0,0,1,1,1,2,2,2…]的分组依据时需要用较复杂的for循环语句先定义循环计数i再用i整除并取商。用apply循环各组数据时需要定义一个处理组内数据的函数这个函数超出了一句因此不能用Lambda表达式来简化定义过程连Java等编译型语言都没有这种限制。取DataFrame data的成员时只能用函数iloc或loc而取list f123的成员时可以直接用下标两者都是集合但用法大相径庭只因为DataFrame不是原生类库语言整体性较差无法像原生类库那样享受简洁的语法规则。DataFrame本身有索引apply拼合多个DataFrame时会加上第二层索引需要手工去掉一层。
SPL
A 1 file(D:\\split.csv).importsi() 2 A1.group((#-1)\3) 3 A2.new(~(1):OrderID, (line~(2).split(\t))(1):Client,line(2):SellerId,line(3):Amount,~(3):OrderDate )
SPL的解析逻辑和Pandas一样但代码简单多了。制造分组依据时不用复杂的for循环语句而是用更简单的group(…)循环函数且无需定义循环计数#就是默认的循环计数~是默认的循环变量。用new循环各组数据时也要定义一个处理函数但SPL支持强大且简洁的Lambda表达式可以把多句代码直接写在new里不必像Python那样手工定义完整的函数结构。从SPL的任何集合类型包括序表取成员时都可以直接用下标语法简洁一致。new函数最后也要拼合多条记录但不会生成无用的新索引。SPL代码更简洁底层原因是原生类库的语言整体性更强。
多层数据
简单查询Json文件的上层为销售员下层为订单查询出符合条件的所有订单。Pandas:
JsonStropen(D:/data.json,r).read()
JsonObjjson.loads(JsonStr)
dfpd.json_normalize(JsonObj,[Orders])
df[OrderDate]pd.to_datetime(df[OrderDate])
resultdf.query(Amount1000 and Amount2000 and contains(business))Pandas代码比较简单。要注意的是dict、list等Python基本数据支持泛型且与Json的object、array类型天然对应适合表示多层Json但不适合表达二维数据。相反DataFrame适合表达二维数据但同一列的数据类型不可变不是真正的泛型无法表达一般的多层Json。DataFrame不擅长表达多层Json需要用json_normalize函数将多层Json转为二维DataFrame才能进行后续计算这说明Pandas的语言整体性不够好。
SPL
A 1 file(d:/EO.json).read() 2 json(A1) 3 A2.conj(Orders) 4 A3.select(Amount1000 Amount2000 likec(Client,\*business*\))
序表不仅支持二维数据也支持多层数据。序表支持真正的泛型与Json的object、array类型天然对应适合表示多层数据。多层数据是二维数据的一般形式序表同样擅长表达二维数据不需要额外的标准化动作直接就能计算。
访问层次节点对Json分组汇总分组字段既有上层字段也有下层字段。Pandas
JsonStropen(D:/data.json,r).read()
JsonObjjson.loads(JsonStr)
dfjson_normalize(JsonObj,record_path[Orders],meta[Name,Gender,Dept])
resultdf.groupby([Dept,Client]).agg({Amount:[count,sum]}).reset_index()
result.columns [Dept,Clt,cnt,sum]Pandas DataFrame无法表达多层Json也就不支持按树形的层次关系直观地访问数据只能用normalize把多层数据转为二维数据再访问扁平的二维数据。
SPL
A 1 json(file(d:/data.json).read()) 2 A1.groups(Dept,Orders.Client:Clt; count(Orders.OrderID):cnt, sum(Orders.Amount):sum)
SPL序表可以表达多层Json支持多层数据的计算比Pandas简洁优雅。多层数据计算的特征之一是提供方便的语法用来表达树形的层级关系比如上面代码中的点号Orders.Client可以自由引用任意节点的数据。当层级较多结构复杂时这种引用方式可以明显提升表达效率。
同理可知Pandas和SPL虽然都可以计算XML但DataFrame不支持多层XML必须转为二维结构表达能力不强SPL序表可以表达并计算多层XML代码更加优雅。
与Json的normalize函数不同Pandas没有为XML提供方便的标准化函数官方推荐用XML计算语言把多层XML计算为二维XML常用的XML计算语言有XSLT和XPath。为了计算XML还得学习第三方语言学习成本过高这里就不举例了。
SPL整体性极佳可以用与Json类似的代码解析XML与Json相同的代码计算XML学习成本很低。比如对多层XML进行分组汇总
A 1 file(d:\\xml\\emp_orders.xml).read() 2 xml(A1,xml/row) 3 A2.groups(Dept,Orders.Client:Clt; count(Orders.OrderID):cnt, sum(Orders.Amount):sum)
除了文件Pandas和SPL也可以解析来自RESTful/WebService的多层数据区别在于Pandas的语言整体性不佳没有提供内置的RESTful/WebService接口必须引入第三方类库。其中一种写法
import requests
resprequests.get(urlhttp://127.0.0.1:6868/api/emp_orders)
JsonOBJresp.json()SPL整体性较好原生支持多层数据和RESTful/WebService
json(httpfile(http://127.0.0.1:6868/api/emp_orders).read())结构化数据对象
生成
Pandas的结构化数据对象是DataFrame不仅可以由数据源生成也可以直接构造下面是常见的构造方法
#用List构造2个字段4条记录行号索引是默认的0-3列名是默认的0-1
dfpd.DataFrame([[1,apple],[2,orange],[3,banana],[4,watermelon]])
#用Array构造
pd.DataFrame(numpy.array([[1,apple],[2,orange],[3,banana],[4,watermelon]]))
#用Dict构造列名是指定的one、two
pd.DataFrame({one:[1,2,3,4],two:[apple,orange,banana,watermelon]})DataFrame由多个Series列或字段对象组成下级是原子数据类型或对象指针。Pandas没有真正的记录对象这在某些场景下会带来方便但也提高了理解难度编码时缺乏直观感。使用Pandas时经常用到Python的原生类库和第三类库numpy里的数据对象包括Set数学集合、List可重复集合、Tuple不可变的可重复集合、Dict键值对集合、Array数组等这些数据对象都是集合容易与Series和DataFrame发生混淆互相转化困难对初学者造成了不少困扰。除了外部类库的集合Series与自家的集合也容易发生混淆比如分组后的集合DataFrameGroupBy。这些都说明Pandas的语言整体性不强缺乏来自底层的支持。
SPL的结构化数据对象是序表同样可以构造生成
//先构造出结构再用序列填入数据行号是0-3列名是指定的one、two
Tcreate(one,two).record([1,apple,2,orange,3,banana,4,watermelon])
//先准备序列形式的数据含列名再构造生成
[one,two,1,apple,2,orange,3,banana,4,watermelon].record(2)
//用序表T0的结构作为新序表的结构再填入数据
T0.create(one,two).record([1,apple,2,orange,3,banana,4,watermelon])序表由多个Record记录对象组成下级是原子数据类型或对象指针。序表有真正的记录对象大多数场景下易于理解编码直观。Record与单记录序表虽然本质不同但业务意义相似容易混淆为了减少混淆SPL经过精心设计使两者的外部用法保持一致通常不必特意区分。SPL只有两种集合序列类似List和序表前者是后者的基础后者是有结构的前者序表分组后的集合是序列两者关系清楚泾渭分明转化容易学习和编码的成本都很低。可以看出来SPL可以从底层提供语法支持整体性较好。 访问数据
Pandas DataFrame自带行号从0开始、字段号列号、字段名列名可以直接通过下标或字段名方便地访问记录
#取行号列表index相当于行号字段名
list(df.index)
#取第1条记录
df.iloc[1]
#区间取第1-3条记录左闭右开
df.iloc[1:4]
#步进偶数位置
df.iloc[1::2]
#倒数第2条从1开始
df.iloc[-2]
#用记录序号和字段序号取值
df1.iloc[1,0]
#用记录序号和字段名取值
df.loc[1,two]SPL序表自带行号从1开始、字段号、字段名可以通过下标和字段名方便地访问记录这方面SPL和Pandas区别不大用法都很方便
//取行号列表#是行号的字段名
T.(#)
//取第2条记录可简写为T(2)
T.m(2)
//区间取第2-4条记录左闭右闭
T.m(2:4)
//步进偶数位置
T.step(2,2)
//倒数第二条从1开始
T.m(-2)
//用记录序号和字段序号取值
T.m(2).#1
//用记录序号和字段名取值
T.m(2).two行号下标的本质是高性能地址索引除了行号Pandas和SPL还提供了其他种类的索引以及对应的查询函数包括唯一值的哈希索引有序值的二分查找索引。性能不是本文重点且两者功能类似这里就不多说了。
维护数据
修改指定位置的记录。Pandas:
df.loc[4,[NAME,SALARY]][aaa,1000]Pandas没有直接提供修改函数而是用Series对象取出记录的部分字段再用List去修改。Series这里表示的是记录但通常表示列List通常表示记录但也可以表示列这些规则初学者容易混淆。
SPL
T.modify(5,aaa:NAME,1000:SALARY)SPL直接提供了修改函数符合初学者的常识。当然SPL也可以取出记录再修改两种方法各自适合不同的场景。
在指定位置插入新记录。Pandas
recordpd.DataFrame([[100,wang,lao,Femal,CA, pd.to_datetime(1999-01-01), pd.to_datetime(2009-03-04),HR,3000]],columnsdf.columns)
df pd.concat([df.loc[:2], record,df.loc[3:]],ignore_indexTrue)Pandas没有真正的记录对象也没有直接提供插入记录的方法间接实现起来较麻烦先构造一条单记录的DataFrame再将原DataFrame按指定位置拆成前后两个DataFrame最后把三个DataFrame拼起来。很多易忽略的细节也要处理好否则无法获得理想结果比如构造记录时要保证字段名与原DataFrame相同拼接新DataFrame时不能保留原来的行号。
SPL
T.insert(3,100,wang,lao,Femal,CA,date(1999-1-1),date(2009-3-4),HR,3000)SPL对记录比较重视直接提供了插入记录的方法代码简洁易于理解。
添加计算列。Pandas
today datetime.datetime.today().year
df[Age] today-pd.to_datetime(df[BIRTHDAY]).dt.year
df[Fullname]df[NAME] df[SURNAME]Pandas没有提供添加计算列的函数虽然实现起来问题不大但添加多个列就要处理多次还是比较麻烦。Pandas的时间函数也不够丰富计算年龄比较麻烦。
SPL
T.derive(age(BIRTHDAY):Age, NAMESURNAME:Fullname)SPL提供了添加计算列的函数一次可以添加多个列且时间函数更加丰富。
结构化数据计算
计算函数
Pandas内置丰富的库函数支持多种结构化数据计算包括遍历循环apply\map\transform\itertuples\iterrows\iteritems、过滤Filter\query\where\mask、排序sort_values、唯一值unique、分组groupby、聚合agg(max\min\mean\count\median\ std\var\cor)、关联join\merge、合并append\concat、转置transpose、移动窗口rolling、shift整体移行。
Pandas没有专门的函数进行记录集合的交、并、差等运算只能间接实现代码比较繁琐。Pandas会为类似的计算提供多个函数比如过滤这些函数的主体功能互相覆盖只是参数约定\输出类型\历史版本不同学习时要注意区分。
SPL的计算函数也很丰富包括遍历循环.()、过滤select、排序sort、唯一值id、分组group、聚合max\min\avg\count\median\top\icount\iterate、关联join、合并conj、转置pivot。
SPL对记录集合的集合运算支持较好针对来源于同一集合的子集可使用高性能集合运算函数包括交集isect、并集union、差集diff对应的中缀运算符是^、、\。对于来源不同的集合可用merge函数搭配选项进行集合运算包括交集i、并集u、差集d。
除了集合运算SPL还有以下独有的运算函数分组汇总groups、外键切换switch、有序关联joinx、有序归并merge、迭代循环iterate、枚举分组enum、对齐分组align、计算序号pselect\psort\ptop\pmax\pmin。Pandas没有直接提供这些函数需要硬编码实现。
有大量功能类似的函数时Pandas要用不同的名字或者参数进行区分使用不太方便。而SPL提供了非常独特的函数选项使功能相似的函数可以共用一个函数名只用函数选项区分差别。比如select函数的基本功能是过滤如果只过滤出符合条件的第1条记录可使用选项1
T.select1(Amount1000)对有序数据用二分法进行快速过滤使用b
T.selectb(Amount1000)函数选项还可以组合搭配比如
Orders.select1b(Amount1000)结构化运算函数的参数有些很复杂Pandas需要用选项或参数名来区分复杂的参数这样易于记忆和理解但代码难免冗长也使语法结构不统一。比如左关联
pd.merge(Orders, Employees, left_onSellerId, right_onEId, howleft, suffixes[_o,_e])SPL使用层次参数简化了复杂参数的表达即通过分号、逗号、冒号自高而低将参数分为三层不过这样会增加一些记忆难度。同样左关联
join1(Orders:o,SellerId ; Employees:e,EId)层次参数的表达能力也很强比如join函数里的分号用于区分顶层参数序表如果进行多表关联只要继续加分号就可以。Pandas参数的表达能力就差多了merge函数里表示DataFrame的选项只有left和right因此只能进行两表关联。
Pandas和SPL都提供了足够丰富的计算函数进行单个函数的基础计算时区别不算大。但实际工作中的数据准备通常有一定复杂度需要灵活运用多个函数且配合原生的语法才能实现这种情况下两者的区别就比较明显了。
同期比
先按年、月分组统计每个月的销售额再计算每个月比去年同月份的销售额的增长率。Pandas
sales[y]sales[ORDERDATE].dt.year
sales[m]sales[ORDERDATE].dt.month
sales_g sales[[y,m,AMOUNT]].groupby(by[y,m],as_indexFalse)
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分组汇总时Pandas很难像SQL那样边计算边分组通常要先追加计算列再分组这导致代码变复杂。计算同期比时Pandas用shift函数进行整体移行从而间接达到访问“上一条记录”的目的再加上要处理零和空值等问题整体代码就更长了。
SPL
A 2 sales.groups(year(ORDERDATE):y,month(ORDERDATE):m;sum(AMOUNT):x) 3 A2.sort(m) 4 A3.derive(if(mm[-1],x/x[-1] -1,null):yoy)
分组汇总时SPL可以像SQL那样边计算边分组灵活的语法带来简练的代码。计算同期比时SPL直接用[-1]表示“上一条记录”且可自动处理数组越界和被零除等问题整体代码较短。
除了用[x]表示相对位置SPL还可以用[x:y]表示相对区间比如股票的3日移动平均值
T.derive(Amount[-2:0].avg():ma)Pandas也可以表示相对区间但由于语言整体性不佳无法从语法层面直接支持所以提供了一个新函数rolling。同样计算股票的3日移动平均值
df[ma]df[Close].rolling(3, min_periods1).mean()贷款分期
根据多项贷款的基本信息金额、期数、利息计算每项贷款每一期的还款明细当期还款额、当期利息、当期本金、剩余本金。Pandas
loan_data ...... #省略loan_data的取数过程
loan_data[mrate] loan_data[Rate]/(100*12)
loan_data[mpayment] loan_data[LoanAmt]*loan_data[mrate]*np.power(1loan_data[mrate],loan_data[Term]) \ /(np.power(1loan_data[mrate],loan_data[Term])-1)
loan_term_list []
for i in range(len(loan_data)):loanid np.tile(loan_data.loc[i][LoanID],loan_data.loc[i][Term])loanamt np.tile(loan_data.loc[i][LoanAmt],loan_data.loc[i][Term])term np.tile(loan_data.loc[i][Term],loan_data.loc[i][Term])rate np.tile(loan_data.loc[i][Rate],loan_data.loc[i][Term])payment np.tile(np.array(loan_data.loc[i][mpayment]),loan_data.loc[i][Term])interest np.zeros(len(loanamt))principal np.zeros(len(loanamt))principalbalance np.zeros(len(loanamt))loan_amt loanamt[0]for j in range(len(loanamt)):interest[j] loan_amt*loan_data.loc[i][mrate]principal[j] payment[j] - interest[j]principalbalance[j] loan_amt - principal[j]loan_amt principalbalance[j]loan_data_df pd.DataFrame(np.transpose(np.array([loanid,loanamt,term,rate,payment,interest,principal,principalbalance])),columns [loanid,loanamt,term,rate,payment,interest,principal,principalbalance])
loan_term_list.append(loan_data_df)
loan_term_pay pd.concat(loan_term_list,ignore_indexTrue)上面代码用两层循环作为主体结构先循环每项贷款再循环生成该项贷款的每一期然后将各期明细转置为DataFrame并追加到事先准备好的list里继续循环下一项贷款循环结束后将list里的多个小DataFrame合并为一个大DataFrame。业务逻辑是比较清晰的就是按公式计算各项数据项但因为两层循环的结构比较复杂数据类型的转换比较麻烦导致代码显得冗长。
SPL
A 1 //省略loan_data的取数过程 2 loan_data.derive(Rate/100/12:mRate,LoanAmt*mRate*power((1mRate),Term)/(power((1mRate),Term)-1):mPayment) 3 A2.news((tLoanAmt,Term);LoanID, LoanAmt, mPayment:payment, Term, Rate, t* mRate:interest, payment-interest:principal, tt-principal:principlebalance)
业务逻辑上SPL和Pandas几乎一样但因为语言整体性强两层循环可以用一个news函数实现也不需要麻烦的类型转换因此代码大幅简化。
按工龄分组
按员工工龄将员工分组并统计每组的员工人数有些组之间有重复。Pandas
#省略员工信息emp的取数过程
def eval_g(dd:dict,ss:str):
return eval(ss,dd)
employed_list[Within five years,Five to ten years,More than ten years,Over fifteen years]
employed_str_list[(s5),(s5) (s10),(s10),(s15)]
todaydatetime.datetime.today()
emp[HIREDATE]pd.to_datetime(emp[HIREDATE])
employed((today-emp[HIREDATE])/np.timedelta64(1,Y)).apply(math.floor)
emp[EMPLOYED]employed
dd{s:emp[EMPLOYED]}
group_cond []
for n in range(len(employed_str_list)):emp_g emp.groupby(eval_g(dd,employed_str_list[n]))
emp_g_index[index for index in emp_g.size().index]
if True not in emp_g_index:sum_emp0
else:groupemp_g.get_group(True)sum_emplen(group)
group_cond.append([employed_list[n],sum_emp])
group_dfpd.DataFrame(group_cond,columns[EMPLOYED,NUM])Pandas擅长等值分组也可实现简单的区间枚举分组遇到本题这种可重复的枚举分组只能硬编码实现大概过程循环分组条件转为等值分组解决问题处理分组子集最后合并结果。此外Pandas没有计算工龄的函数也要手工实现。
SPL
A B 1 /省略员工信息emp的取数过程 2 [?5,?5 ?10,?10,?15] /条件 3 [Within five years,Five to ten years, More than ten years, Over fifteen years] /组名 4 emp.derive(age(HIREDATE):EMPLOYED) /计算工龄 5 A4.enumr(A2, EMPLOYED).new(A3(#):EMPLOYED,~.len():NUM) /枚举分组
函数enum用于枚举分组选项r处理重复分组的情况再配合SPL高效的表达能力整体代码比Pandas简短得多。
通过上面的几个例子可以看出来Pandas适合简单的数据准备场景遇到复杂些的结构化数据计算代码就很难写了。SPL语言整体性好无论简单场景还是复杂计算代码量都不多。
大数据量计算
如果文件或库表的数据量较大指超出内存而不是Big Data最终都要用循环分段的办法来处理即每次读取并计算少量数据再保留本次计算的中间计算结果循环结束后合并多个中间计算结果比如过滤或对合并结果做二次计算比如分组汇总。即使是基本的结构化数据计算数据量大时也很麻烦如果涉及关联、归并、并集或综合性计算代码将更加复杂。
聚合
Pandas
chunk_data pd.read_csv(orders.txt,sep\t,chunksize100000)
total0
for chunk in chunk_data:totalchunk[amount].sum()对于聚合这种简单的大文件计算Pandas代码还算简单。打开大文本时Pandas提供了一个选项chunksize用来指定每次读取的记录数之后就可以用循环分段的办法处理大文本每次读入一段并聚合再将计算结果累加起来。
SPL
file(orders.txt).cursortc().total(sum(amount))SPL同样采用循环分段的办法处理大文本但SPL封装了代码细节提供了方便的游标机制允许用类似处理小数据量的语法直观地处理较大的数据量所以代码里看不到循环累加的过程。
过滤
Pandas
chunk_data pd.read_csv(d:/orders.txt,sep\t,chunksize100000)
chunk_list []
for chunk in chunk_data:chunk_list.append(chunk[chunk.stateNew York])
res pd.concat(chunk_list)Pandas没有提供游标只能硬编码进行循环分段每次将部分数据读入内存进行过滤过滤的结果也存储于内存中。
上面的方法只适合结果集小于内存的场景如果结果集大于大内存就要把每次过滤的结果写入文件中代码变化较大
chunk_data pd.read_csv(d:/orders.txt,sep\t,chunksize100000)
isNewTrue
for chunk in chunk_data:need_data chunk[chunk.stateNew York]if isNew True:need_data.to_csv(orders_filter.txt,indexNone)isNew Falseelse:need_data.to_csv(orders_filter.txt,indexNone,modea,headerNone)首次创建文件和后续追加记录不同代码细节要小心处理代码难度显著增加。
SPL:
A 1 file(d:/orders.txt).cursortc() 2 A1.select(stateNew York) 3 A2.fetch()
游标机制隐藏了底层细节解题难度显著降低代码量显著缩小。不难看出SPL语言的整体性较好因此能够从底层提供游标机制。
结果集大于内存时只要简单地把A3改为
file(orders_filter.txt).exporttc(A2)得益于游标机制SPL不必手工区分首次创建文件和后续追加代码简短得多。
排序
pandas
def parse_type(s):if s.isdigit():return int(s)try:res float(s)return resexcept:return s
def pos_by(by,head,sep):by_num 0for col in head.split(sep):if col.strip()by:breakelse:by_num1return by_num
def merge_sort(directory,ofile,by,ascendingTrue,sep,):with open(ofile,w) as outfile:file_list os.listdir(directory)file_chunk [open(directory/file,r) for file in file_list]k_row [file_chunk[i].readline()for i in range(len(file_chunk))]by pos_by(by,k_row[0],sep)outfile.write(k_row[0])k_row [file_chunk[i].readline()for i in range(len(file_chunk))]k_by [parse_type(k_row[i].split(sep)[by].strip())for i in range(len(file_chunk))]with open(ofile,a) as outfile:while True:for i in range(len(k_by)):if i len(k_by):breaksorted_k_by sorted(k_by) if ascending else sorted(k_by,reverseTrue)if k_by[i] sorted_k_by[0]:outfile.write(k_row[i])k_row[i] file_chunk[i].readline()if not k_row[i]:file_chunk[i].close()del(file_chunk[i])del(k_row[i])del(k_by[i])else:k_by[i] parse_type(k_row[i].split(sep)[by].strip())if len(k_by)0:break
def external_sort(file_path,by,ofile,tmp_dir,ascendingTrue,chunksize50000,sep,,usecolsNone,index_colNone):os.makedirs(tmp_dir,exist_okTrue)try:data_chunk pd.read_csv(file_path,sepsep,usecolsusecols,index_colindex_col,chunksizechunksize)for chunk in data_chunk:chunk chunk.sort_values(by,ascendingascending)chunk.to_csv(tmp_dir/chunkstr(int(time.time()*10**7))str(uuid.uuid4()).csv,indexNone,sepsep)merge_sort(tmp_dir,ofileofile,byby,ascendingascending,sepsep)except Exception:print(traceback.format_exc())finally:shutil.rmtree(tmp_dir, ignore_errorsTrue)
infile D:/orders.txt
ofile D:/extra_sort_res_py.txt
tmp D:/tmp
external_sort(infile,amount,ofile,tmp,ascendingTrue,chunksize1000000,sep\t)将大文件分成多段每段分别排序分别写入N个临时文件再打开N个临时文件并维持一个N个成员的数组指向每个临时文件的当前读取位置初始位置是第一条记录之后比较该数组对应的N条记录将最小记录i写入结果文件并下移i对应的临时文件的当前读取位置继续比较N条记录直至排序结束。这是大文件排序时常用的归并算法实现过程比较复杂Pandas缺乏方便的游标机制只能硬编码实现代码冗长且不易解读。
SPL
A 1 file(D:/orders.txt).cursortc() 2 A1.sortx(amount) 3 file(D:/extra_sort_res_py.txt).exporttc(A2)
上面同样采用归并法实现大文件排序由于SPL支持游标机制复杂的细节被隐藏起来只要写出简短的代码就能实现。
大数据量计算还有很多种比如分组汇总、关联、交集等很多都比排序复杂比如分组汇总的第一步通常就是大排序追求效率就要用更复杂的哈希分堆。Pandas的语言整体性差不支持游标只能硬编码实现这些计算难度非常大至于综合性的大数据量计算基本就不用考虑Pandas了。SPL语言整体性较好有方便的游标机制代码都不难写比如大结果集的分组汇总
A 1 file(file_path).cursortc() 2 A1.groupx(key;sum(coli):total) 3 file(out_file).exporttc(A2)
综合性的计算每种商品销售额最大的3笔订单
A 1 file(file_path).cursortc() 2 A1.groups(product;top(3; -amt):three) 3 A2.conj(three)
Pandas提供了丰富的库函数但因为没有参与Python的统一设计无法获得Python的底层支持导致语言的整体性不佳只擅长简单的数据准备工作不适合一般的场景。esProc SPL的语言整体性较好结构化数据类型更加专业可以用简洁直观的代码实现一般的数据准备工作包括解析不规则的数据源表达多层数据进行复杂的结构化数据计算完成大数据量计算。
SPL资料
SPL下载SPL源代码
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/90585.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!