美文网首页
Python 并不合适职场编程,SPL 才行

Python 并不合适职场编程,SPL 才行

作者: 心宇gxy | 来源:发表于2020-11-02 10:12 被阅读0次

    职场人员使用 Excel 进行数据处理已经成为家常便饭。不过相信大家一定有过很无助的情况,比如复杂计算、重复计算、自动处理等,再遇上个死机没保存,整个人崩溃掉也不是完全不可能。

    如果学会了程序语言,这些问题就都不是事了。那么,该学什么呢?

    无数培训机构和网上资料都会告诉我们:Python!

    Python 代码看起来很简单,只要几行就能解决许多麻烦的 Excel 计算,看起来真不错。

    但真是如此吗?作为非专业人员,真能学得会 Python 来协助我们工作吗?

    Python DataFrame

    日常职场业务主要是处理表格类数据(用专业的说法是结构化数据),比如这样的:

    表里除第一行外的每行数据称为一条记录,对应了一件事、一个人、一张订单……,第一行是标题,说明记录由哪些属性构成,这些记录都有相同的属性,整个表就是这样一些记录的集合。

    Python 主要是用一个叫 DataFrame 的东西来处理这类表格数据,我们来看看 DataFrame 是怎么做的。

    比如上面的表格,读入 DataFrame 后是这样的:

    看起来和 Excel 差不多,只是行号是从 0 开始的。

    但是,DataFrame 的本质是一个矩阵(大学时代的线性代数还想得起来吗?),Python 也没有记录这样的概念,它的运算都要绕到矩阵可以执行的方法上才行。

    我们来看一些简单运算。

    过滤

    过滤是个简单常见的运算,就是把满足某一条件的子集取出来,比如还是上面的表格:

    问题一:取出 R&D 部门的员工。

    Python 代码是这样的:

    import pandas as   pd

    data =   pd.read_csv('Employees.csv')

    rd = data.loc[data['DEPT']=='R&D']

    print(rd)

    导入 Pandas

    读取数据

    过滤 R&D 部门

    查看 rd 数据

    运行结果:

    代码很简单,结果也没问题。但是:

    1.     用到的函数叫 loc,是 location(定位)的缩写,完全没有过滤的意思。事实上,这里的过滤也是通过定位(location)满足条件的行的索引来实现的,函数里面的 data[‘DEPT’]==’R&D’会算出一个布尔值构成的 Series:

    和 data 的索引相同,满足条件的行为 True 否则为 False

    然后 loc 就是根据取值为 True 的行对应的索引再取出 data 中相应的行再得到一个新的 DataFrame,本质上是从矩阵中抽取指定行的运算,用来对付过滤就有点绕。

    2.     过滤 DataFrame 并不只可以使用 loc 函数过滤,还可以用 query(…) 等方法,但结果都是定位到矩阵的行列索引,然后按行列索引取数据,大体上是这样的 matrix.loc[row,col]

    无论如何,基本的过滤还算简单吧,讲明白了也能理解。下面我们再尝试对过滤后的子集做两个算不上复杂的运算看看。

    修改子集中的数据

    问题二:将 R&D 部门员工的工资上调 5%

    自然的想法,只要过滤出 R&D 部门员工,然后对这些员工的工资进行修改就可以了。

    按照这种逻辑写出代码:

    import pandas as   pd

    data = pd.read_csv('Employees.csv')

    rd =   data.loc[data['DEPT']=='R&D']

    rd['SALARY']=rd['SALARY']*1.05

    print(data)

    导入 Pandas

    读取数据

    过滤 R&D 部门

    修改 SALARY

    运行结果:

    SettingWithCopyWarning:

    A value is trying to be set on a copy of a slice from a DataFrame.

    Try using .loc[row_indexer,col_indexer] = value instead

    See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

      rd['SALARY']=rd['SALARY']*1.05

    可以看到,不仅触发了警告,修改值也没有成功。

    这是因为rd = data.loc[data['DEPT']=='R&D']是一个过滤后的矩阵,再使用rd['SALARY']=rd['SALARY']*1.05这个语句修改 SALARY 值的时候,rd['SALARY']又是一个新的矩阵了,因此修改它其实是修改的 rd 这个子矩阵,并没有修改 data 这个最初的矩阵。

    这话说着很绕,听着也绕。

    正确的代码怎么写呢?

    import pandas as   pd

    data =   pd.read_csv('Employees.csv')

    rd_salary = data.loc[data['DEPT']=='R&D','SALARY']

    data.loc[data['DEPT']=='R&D','SALARY']   = rd_salary*1.05

    print(data)

    找到 R&D 部门的员工工资

    截取 R&D 部门的员工工资并修改

    运行结果:

    这次对了。不可以先取出子集再修改,要对着原矩阵,找到要修改的成员的定位再来修改,即 loc[row=data['DEPT']=='R&D',column='SALARY'],按照行列索引取到要修改的数据,对着这个矩阵赋值。想要上调 5% 还要在此之前先拿到这份数据(rd_salary=…这句)。这种写法要进行重复的过滤,效率低也就罢了,但实在是太绕了。

    子集求交

    问题三:找出既是纽约州又是 R&D 部门的员工

    这个问题更简单,只要算出两个子集做个交集运算就完了。我们看看 Python 是如何处理的:

    import pandas as   pd

    data =   pd.read_csv('Employees.csv')

    rd =   data[data['DEPT']=='R&D']

    ny =   data[data['STATE']=='New York']

    isect_idx =   rd.index.intersection(ny.index)

    rd_isect_ny =   data.loc[isect_idx]

    print(rd_isect_ny)

    R&D部门员工

    纽约州员工

    索引求交集

    按索引交集截取数据

    运行结果:

    集合求交是非常基本的运算,很多程序语言都提供了,事实上 python 也提供了(上面有 intersection 函数)。然而, DataFrame 的本质是矩阵,两个矩阵求交集却没有什么意义,Python 也就没有提供矩阵求交集的运算。想要做到用两个 dataframe 表示的集合的交集运算,只能绕道去求两个矩阵索引的交集,最后再利用索引的交集从原数据上定位截取,有种舍近求远的感觉,不按“套路”出牌。

    工作中最常用的过滤运算都这么令人费解,绕的脑袋晕,可以想象其他更复杂的运算,一股酸爽的感觉“悠然而生”。

    下面看下稍微复杂一点的分组运算:

    分组

    分组运算是日常数据处理中最常用的运算了,Python 也提供了丰富的分组运算函数,能够完成大多数的分组运算,但在理解和使用上并没有那么容易。

    分组理解

    分组就是把一个大集合按某种规则分成一些小集合,结果是个由集合构成的集合,然后再对分组后的集合进行运算,如下图:

    need-to-insert-img

    先来看下最常用的分组聚合运算。

    问题四:汇总各部门的人数

    Python 代码:

    import pandas as   pd

    data =   pd.read_csv('Employees.csv')

    group =   data.groupby("DEPT")

    dept_num = group.count()

    print(dept_num)

    按照部门分组

    汇总各部门人数

    运行结果:

    结果好像有点尴尬,本来只需要记录每个分组中的成员数量,只要有一列就行了,为什么出来这么多列,它像是对每一列都重复做了同样的动作,好奇怪。

    别急,这个问题 Python 还是可以解决的,只不过不是用 count 函数,而是 size 函数:

    import pandas as   pd

    data =   pd.read_csv('Employees.csv')

    group =   data.groupby("DEPT")

    dept_num =   group.size()

    print(dept_num)

    按照部门分组

    汇总各部门人数

    运行结果:

    need-to-insert-img

    这个结果看起来就正常多了,不过,还是感觉哪里怪怪的。

    是滴,这个结果不再是二维的 DataFrame 了,而是个单维的 Seriese。

    count 函数计算的结果之所以奇怪,是因为它是对每一列计数,而 size 函数是查看各组的大小,但其实我们自然的逻辑还是用 count 来计数,size 很难用自然的逻辑想到(还要上网搜资料)。

    如前所述,分组结果应该是集合的集合,我们看看 Python 中的 DataFrame 分组后是什么样子呢?把上面代码中 data.groupby(“DEPT”) 的结果打印出来看。

    import pandas as   pd

    data =   pd.read_csv('Employees.csv')

    group = data.groupby("DEPT")

    print(group)

    按照部门分组

    运行结果:

    哇,这是个什么东东?

    第一次看到这个东西,直接就蒙圈了,分组的结果不应该是集合的集合吗,为什么会是这样?这对于非专业程序人员来说简直如同梦魇。

    不过上网搜搜还是可以看到它是一个所谓的可迭代对象,迭代以后发现它的每一条都是以分组索引 + DataFrame 构成的,可以使用一些方法看到里边的内容,如使用 list(group) 就可以看到分组的结果了。如下图:

    need-to-insert-img

    看到上图以后就会明白,被称为“对象”的东西里面原来是这样的。本质上它也确实是个集合的集合(姑且把矩阵理解成集合吧),但它并不能像普通的集合那样直接取某个成员 (如 group[0]),这在使用上迫使用户强行记忆这类“对象”的 N 种运算规则,理解不了就只能死记硬背了。

    看到这里,估计已经有很多读者开始晕菜了,彻底不明白上面这段话是在胡说八道些什么。嗯,这就对了,因为这才是职场人员的正常状态。

    分组中简单的聚合运算都如此难以理解,我们再烧烧脑,看下稍微复杂一点的分组后子集合的运算。

    分组子集处理

    虽然分组后经常用于聚合运算,但有时我们并不关心聚合结果,而是关心分组后的集合本身。比如分组后的集合按某一列排序。

    问题五:将各部门员工按照入职时间从早到晚进行排序 。

    问题分析:分组后对子集按照入职时间排序即可。

    Python 代码

    import pandas as pd

    employee = pd.read_csv("Employees.csv")

    employee['HIREDATE']=pd.to_datetime(employee['HIREDATE'])

    employee_new =   employee.groupby('DEPT',as_index=False).apply(lambda   x:x.sort_values('HIREDATE')).reset_index(drop=True)

    print(employee_new)

    修改入职时间格式

    按 DEPT 分组,并对各组按照 HIREDATE 排序,最后重置索引

    运行结果:

    结果没问题,各个部门员工都按照入职时间从早到晚排序了。但我们观察下代码中最核心的一句employee.groupby('DEPT',as_index=False).apply(lambda x:x.sort_values('HIREDATE')),把这句代码抽象一下就是这样:

    df.groupby(c).apply(lambda x:f(x))

    df:数据框 DataFrame

    groupby:分组函数

    c:分组依据的列

    以上三个还是比较好理解的,可是 apply 配合 lambda 就十分晦涩难懂了,超出了大多数非专业程序人员理解的范畴,这需要明白所谓“函数语言”的原理才能搞懂(自己去搜索,俺懒得解释了)。

    如果不使用这“二位”(apply+lambda)呢?也能做,就是会很麻烦。得用 for 循环,对每个分组子集分别排序,最后还得把结果合并起来。

    import pandas as pd

    employee = pd.read_csv("Employees.csv")

    employee['HIREDATE']=pd.to_datetime(employee['HIREDATE'])

    dept_g = employee.groupby('DEPT',as_index=False)

    dept_list = []

    for index,group in dept_g:

        group =   group.sort_values('HIREDATE')

          dept_list.append(group)

    employee_new = pd.concat(dept_list,ignore_index=True)

    print(employee_new)

    修改入职时间格式

    按 DEPT 分组

    初始化列表

    for循环

    每个分组排序

    排序结果放入列表

    合并各组结果

    运行结果相同,但代码复杂了很多,而且运行效率也变低了。你愿意用哪一种呢?

    Python 对于类似但不完全一样的数据设计了不同的数据类型,也对应有不同的操作方式,并不能简单地把对某种数据的知识复制到另一个类似数据上,搞得人晕死。

    说了这么多,总结下来就是一句话:Python 真的挺难懂的,它就不是一个面向非专业选手的东西。具体来说大概就是三点:

    1.     DataFrame 本质是矩阵

    所有的运算都要想办法按矩阵的方法来计算,经常会很绕。

    2.     数据类型多而且运算规则差别很大

    Python 中设计了 Series,DataFrame,分组对象等等不同的数据类型,而且不同的数据类型,计算方法也不完全相同,如 DataFrame 可以使用 query 函数过滤,而 Series 不可以,分组对象的本质完全不同于 Series 和 DataFrame,计算方法更是难以捉摸。

    3.     知其然而不知其所以然

    数据类型过多,计算方法差别又大,无形之中增加了用户的记忆量,死记硬背的成分更多,想要灵活运用太难了,这就造成了一种奇怪的现象:一个简单的运算,上网搜索 Python 代码的时间可能比用 excel 计算还要长。

    Python 代码看起来简单,但你上了培训班也大概率学不会,结果只会抄例子。

    那么,是不是就没有适合职场人员进行日常数据处理的工具了吗?

    还是有的。

    esProc SPL

    esProc SPL 也是一种程序设计语言,专注于结构化数据计算。SPL 中提供了丰富的基础计算方法,其概念逻辑也是符合我们的思维习惯的。

    1.     序表是记录的集合

    SPL 使用序表承载结构化数据,接近于日常处理的 excel 表。

    2.     数据类型少且规则一致

    SPL 进行结构化数据处理时几乎只有集合和记录两种数据类型,涉及到的方法也大体一致。

    3.     知其然且知其所以然

    只要记住两种数据类型,掌握基本的运算法则,更复杂的运算就只是简单运算规则的组合。不熟练时可能写的代码不好看,但不太可能写不出来,不会出现 Python 那种花费大量时间搜索代码写法的现象。

    下面我们就使用 SPL 来解决上述介绍的问题,大家认真体会下 SPL 是多么“平易近人”:

    序表

    esProc SPL 中用于承载二维结构化数据的数据结构是序表,它和 excel 中呈现的结果一致,如下图:

    need-to-insert-img

    上表中除了第一行(标题行)外,其他每一行表示一条记录,而序表就是记录的集合,相较于 Python 中的 DataFrame 更加直观。

    过滤

    SPL 中并不是按照矩阵定位的方式过滤,而是 select(筛选)出满足条件的记录。

    问题一:查看 R&D 部门的员工信息

    AB

    1=file("Employees.csv").import@tc()/导入数据

    2=A1.select(DEPT=="R&D")/过滤

    A2 结果:

    need-to-insert-img

    SPL 过滤后的结果非常好理解,就是原始数据集合的一个子集。

    再来看看 SPL 对子集修改和求交集运算

    1.     修改子集中的数据

    问题二:将 R&D 部门员工的工资上调 5%

    AB

    1=file("Employees.csv").import@tc()/导入数据

    2=A1.select(DEPT=="R&D")/过滤

    3=A2.run(SALARY=SALARY*1.05)/修改工资

    4=A1/查看结果

    A4 结果:

    need-to-insert-img

    SPL 完全是按照我们正常的思维方式来计算的,过滤出结果,对着结果修改工资,而不像 Python 那么费劲。

    2.     子集求交

    问题三:找出既是纽约州又是 R&D 部门的员工

    AB

    1=file("Employees.csv").import@tc()/导入数据

    2=A1.select(DEPT=="R&D")/R&D部门员工

    3=A1.select(STATE=="New   York")/纽约州员工

    4=A2^A3/交集

    A4 结果:

    need-to-insert-img

    SPL 中的交集运算就是对着集合求交集,是真正的集合运算,只使用一个简单的交集运算符“^”即可。易于理解,而且书写简单,而不用像 Python 那样因为无法求矩阵的交集而去求索引的交集,然后再从原数据中截取。SPL 中的其他集合运算如并集、差集、异或集也都有对应的运算符,使用起来简单,方便。

    分组

    分组理解

    SPL 的分组运算也是符合自然逻辑的,即分组后结果是集合的集合,显而易见。

    先来看看 SPL 的分组聚合运算。

    问题四:汇总各部门的人数

    AB

    1=file("Employees.csv").import@tc()

    2=A1.groups(DEPT;count(~):cnt)/分组

    A2 结果:

    need-to-insert-img

    分组聚合的结果,仍然是序表,可以继续使用序表的方法。并不像 Python 聚合结果成了单维的 Series。

    再来看下 SPL 的分组结果

    AB

    1=file("Employees.csv").import@tc()

    2=A1.group(DEPT)/分组

    A2 结果:

    上图是序表的集合,每个集合是一个部门的成员构成的序表;下图是点开第一个分组的成员——Administration 部门成员的序表。

    这种结果符合我们的正常逻辑,也容易查看分组的结果,更容易对分组结果进行接下来的运算。

    分组子集处理

    分组的结果是集合的集合,只要把每个子集进行处理即可。

    问题五:将各部门员工按照入职时间从早到晚进行排序

    AB

    1=file("Employees.csv").import@tc()

    2=A1.group(DEPT)/分组

    3=A2.conj(~.sort(HIREDATE))/子集排序并合并

    A3 结果:

    由于 SPL 的分组结果还是个集合,因此它可以使用集合的计算方法计算,并不需要强行记忆分组后的计算方法,更不需要使用 apply()+lambda 这种天书般的组合,非常自然的就完成了分组 + 排序 + 合并的工作,简单的 3 行代码,既好写,又好理解,而且效率很高。

    小结

    1.     Python 进行结构化处理时,本质都是矩阵运算,简单的集合运算需要绕到矩阵上去运算;esProc SPL 本质是记录的集合,集合运算简单便捷。

    2.     Python 数据类型复杂多样,运算规则不可预测,往往是知其然而不知其所以然,不太可能举一反三,写代码记忆的成分更多,想理解其原理太难了;esProc SPL 数据类型少,而且计算规则固定,只需要掌握基本的运算规则就可以举一反三的完成复杂的运算。

    3.     学习 SPL,可以到:

    http://www.raqsoft.com.cn/wx/SPL-programming.html

    相关文章

      网友评论

          本文标题:Python 并不合适职场编程,SPL 才行

          本文链接:https://www.haomeiwen.com/subject/cywpvktx.html