Python 和 SPL 对比 4——选出和定位

从集合中选出子集是常见运算,比如从公司成员中选出年龄大于 40 的成员等,本文对比 Python 和 SPL 的选出运算。

选出指定位置的成员

如:公司成员信息表如下,请选出偶数位置的公司成员。

..

Python

import pandas as   pd
file="D:\data\EMPLOYEE.csv"
emp=pd.read_csv(file)
even_emp=emp.query('index%2==1')
print(even_emp)

导入 pandas
员工信息表路径
读员工信息
利用行索引选出偶数位置
 
 

Python的索引是从 0 开始的,所以偶数位置的行索引(这里就是序号)是奇数。使用 query(…) 函数选出行索引为单数(即位置是偶数)的成员。但如果行索引不是数字或不是有序索引时,就不能这么写了,只能先建一个偶数位置的列表再用 iloc 函数选出。

SPL


A

B

1

D:\data\EMPLOYEE.csv


2

=file(A1).import@tc()

/读取员工信息表

3

=A2.select(#%2==0)

/偶数位置

A.select(…)中可以使用“~“,“#”来表示当前成员和该成员的位置,这里就是用 #%2==0 表示偶数位置的元素。

选出符合条件的成员

选出符合条件的成员是最常见的选出运算,仍使用员工信息表举例。

查询研发部门纽约州员工的全名和薪水。

Python

#续用上例中的 emp 表

RD_NY_emp=emp.query('DEPT=="R&D"&STATE=="New   York"')
name=RD_NY_emp["NAME"]+""+RD_NY_emp["SURNAME"]
name.rename("FULLNAME",inplace=True)
salary=RD_NY_emp["SALARY"]
name_salary=pd.concat([name,salary],axis=1)
print(name_salary)


按条件选出
符合条件的成员全名
为全名 Seires 命名
符合条件的成员薪水
合并为 Dataframe
 
 

Python使用 query(…) 函数选出符合条件的成员,逻辑简单。但根据选出结果拼出目标表却有点麻烦。

SPL


A

B

/A2是上例中的公司员工信息表


6

=A2.select(DEPT=="R&D"&&STATE=="New   York")

/选出符合条件的成员

7

=A6.new(NAME+" "+SURNAME:FULLNAME,SALARY)

/新建 FULLNAME 和 SALARY 组成的新序表

按位置选出和按条件选出都用 A.select(),最后拼出目标表的操作也很简单。

选出符合条件的成员位置 / 索引

在选出时我们希望得到符合条件成员的位置或索引,然后利用位置或索引信息进行其他运算。

如:请找出 50 岁以上员工的在员工信息表中的位置或索引。

Python

#续用上例中的 emp 表

import numpy as np
import datetime
import math
emp["BIRTHDAY"]=pd.to_datetime(emp["BIRTHDAY"])
age=(datetime.datetime.now() -   emp["BIRTHDAY"])/np.timedelta64(1,'Y')
age=age.apply(math.floor)
np_age=np.array(age)
age_50=np.argwhere(np_age>=50)[:,0]
print(age_50)


 
导入 numpy
  导入 datetime
  导入 math
  转换日期格式
  计算年龄
  向下取整
  Series 转成 array
  计算位置(索引)
 
 

Python中没有计算年龄的现成函数,需要借助 numpy 和 datetime 两个库来完成,而取整又要借助 math 库,已经够麻烦了。更奇葩的是 Pandas 的底层是 numpy,有些函数又不完全继承,如 Pandas 也有计算时间差的函数 pd.Timedelta()函数,但只有月日时分秒这些选项,没有以年计算的选项。而 argwhere() 函数更是只能转到数组 array 这一数据结构才可以使用,本来很简单的条件过滤需要多步过渡,搞得非常麻烦。

SPL


A

B

/A2是上例中的公司员工信息表


9

=A2.pselect@a(age(BIRTHDAY)>=50)

/选出符合条件的成员位置

SPL有 age()函数计算年龄,要方便很多。与 select 对应,SPL 还提供了 pselect@a() 用来返回选出符合条件的成员位置,一行代码轻松惬意。SPL 中很多选出函数都有对应的返回位置的函数,如:pmax(),pmin(),ptop() 等。

选出最大值 / 最小值成员

有时我们想查看最值成员的全部信息,如:

查看薪水最高的成员信息。

Python

#续用上例中的 emp 表

#选出最大值成员(1 个成员)
max_salary_idx=emp.SALARY.argmax()
max_salary_emp=emp.loc[max_salary_idx]
print(max_salary_emp)

 

#选出最大值成员(多个成员)
max_salary=emp["SALARY"].max()
max_salary_emp=emp[emp.SALARY==max_salary]
print(max_salary_emp)


 
 
薪水最高成员索引
  薪水最高成员
 
 

 


 
最高薪水
  按最高薪水选出
 
 

Python选出一个最大 / 最小值成员时可以用 argmax()或者 argmin() 来得到目标值的索引,而后按索引选出成员即可,这样的效率比较高。但 argmax()和 argmin() 并不返回相同值时的所有索引,因此需要返回多个最大最小值成员时就要遍历两遍数据(第一遍计算最大最小值,第二遍选出值),效率就要慢一半;从书写代码上两者的思路也不同,不容易记忆。

SPL


A

B

/A2是上例中的公司员工信息表


11

=A2.maxp(SALARY)

/选出 1 个最大值的成员

12

=A2.maxp@a(SALARY)

/选出所有最大值成员

SPL提供 maxp()和 minp() 函数返回最大最小值成员,@a 选项返回全部目标值成员。两者都是只遍历一遍数据就可以返回结果,而且从函数形式上也非常容易记忆。

SPL中许多函数都有三种形式:选出值本身,如 max/min;选出值对应的成员,如 maxp/minp;选出值对应的位置,如 pmax/pmin。风格统一,容易记记。

如:查找薪水最高成员在员工信息表中的位置。

=A2.pmax@a(SALARY),这一句代码就可以搞定。

选出 topN 成员

选出 topN 也是常见的运算,如:

查看公司入职最早的前 10 名成员信息。

Python

#续用上例中的 emp 表

work_year_sort=emp.sort_values(by="HIREDATE")
work_year_top10=work_year_sort.iloc[:10]
print(work_year_top10)


 
按入职时间排序
  取前 10 个
 
 

Python的 topN 是先排序,然后取前 N 个成员,当数据量不大时,这样做还可以,当数据量稍微一大,这样做的效率就低太多了。

SPL


A

B

/A2是上例中的公司员工信息表


14

=A2.top(10;HIREDATE)

/选出入职最早的前 10 的成员

SPL的 top()函数是聚合函数,类似于 max(),只不过 max()是维护一个最大值,而 top() 是维护一个 topN 的序列,不会对全数据排序,减少了排序带来的额外开销。

类似地,想看 topN 成员的位置信息可以使用 ptop() 函数。

小结

Python的简单选出比较方便,如位置选出,条件选出;有的选出有点绕,比如选出符合条件的位置,最大最小值选出;还有的效率太低,比如 topN 选出。Pandas 和 Numpy 两个库的频繁切换也是让人摸不着头脑。

SPL提供了丰富且风格统一的选出和定位函数,无论简单还是复杂选出和定位,都非常容易完成。