职场人员使用 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,本质上是从矩阵中抽取指定行的运算,用来对付过滤就有点绕。

  1. 过滤 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 也提供了丰富的分组运算函数,能够完成大多数的分组运算,但在理解和使用上并没有那么容易。

分组理解

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

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

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

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)

按照部门分组

汇总各部门人数

运行结果:

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

是滴,这个结果不再是二维的 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) 就可以看到分组的结果了。如下图:

看到上图以后就会明白,被称为“对象”的东西里面原来是这样的。本质上它也确实是个集合的集合(姑且把矩阵理解成集合吧),但它并不能像普通的集合那样直接取某个成员 (如 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 本质是矩阵

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

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

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

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

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

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

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

还是有的。

esProc SPL

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

  1. 序表是记录的集合

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

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

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

  1. 知其然且知其所以然

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

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

序表

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

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

过滤

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

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

  A B
1 =file("Employees.csv").import@tc() /导入数据
2 =A1.select(DEPT=="R&D") /过滤

A2 结果:

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

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

  1. 修改子集中的数据

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

  A B
1 =file("Employees.csv").import@tc() /导入数据
2 =A1.select(DEPT=="R&D") /过滤
3 =A2.run(SALARY=SALARY*1.05) /修改工资
4 =A1 /查看结果

A4 结果:

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

  1. 子集求交

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

  A B
1 =file("Employees.csv").import@tc() /导入数据
2 =A1.select(DEPT=="R&D") /R&D部门员工
3 =A1.select(STATE=="New   York") /纽约州员工
4 =A2^A3 /交集

A4 结果:

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

分组

分组理解

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

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

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

  A B
1 =file("Employees.csv").import@tc()  
2 =A1.groups(DEPT;count(~):cnt) /分组

A2 结果:

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

再来看下 SPL 的分组结果

  A B
1 =file("Employees.csv").import@tc()  
2 =A1.group(DEPT) /分组

A2 结果:

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

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

分组子集处理

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

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

  A B
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

ython 并不合适职场编程,SPL 才行的更多相关文章

  1. 职场选择之大公司 VS 小公司

    其实这是个非常难回答的问题,很多职场新人都会有类似的顾虑和疑问. 这个问题就好比业界比较容易引起争议的编程语言哪个是最好的一样.大公司还是小公司里面发展,只有身处其中才能体会,如人饮水,冷暖自知. 笔 ...

  2. for程序员:这些你可能遇到的职场难题,我们帮你整理好了答案

    “迷茫”是当下青年谈论的最多的词汇之一,无论高矮胖瘦富穷美丑,每个人都有自己独特的难题.造成“迷茫”的原因有很多种,比如生存压力,情感问题,以及困扰着相当一部分人的职场焦虑.今天这篇关于“职场迷茫”的 ...

  3. 从小工到专家 ——读《Java程序员职场全攻略》有感

    从小工到专家 ——读<Java程序员职场全攻略>有感   <Java程序员职场全攻略>是以故事的形式,向读者介绍Java程序员的职场经验.作者牛开复在北京从事软件开发,已经是一 ...

  4. 初入职场的建议--摘自GameRes

    又开始一年一度的校招了,最近跑了几个学校演讲,发现很多话用短短的一堂职业规划课讲还远远不够,因为那堂课仅仅可能帮大家多思考怎样找到一份合适的工作,并没有提醒大家怎样在工作中发展自己的职业. 见过这么多 ...

  5. 【转】工科男IT职场求生法则

    转自:http://www.36dsj.com/archives/3459 我在IT职场打滚超过10年了,从小小的程序员做到常务副总.相对于其它行业,IT职场应该算比较光明的了,但也陷阱重重,本文说说 ...

  6. IT职场生存法则

    转!!!!!!!!!!!!! 摘要我在IT职场打滚超过15年了,从小小的程序员做到常务副总.相对于其它行业,IT职场应该算比较光明的了,但也陷阱重重,本文说说我的亲身体会,希望大家能在IT职场上战无不 ...

  7. 【转】IT职场人生系列之四:怎样写简历

    本文是IT职场人生系列的第四篇. 因为早年跳槽无数,所以积累了不少"技巧",逐渐变成写简历的"专家",最长的时候简历到了12页,所以现在练就一手写长篇博客的功夫 ...

  8. 职场PPT达人装酷的13条秘诀

    对<说服力-让你的PPT会说话>读者调查显示,88.8%的白领认为“做出漂亮的幻灯片对晋升有帮助”,99.9%的白领一致认为职场装酷神器排行榜第一位是PPT,甚至有位程序员说哥最牛的编程环 ...

  9. 一个普通底层.NET程序员关于职场瓶颈期的思考,辗转自我提升/跳槽/转行之间

    徒有工龄,没技术没学历没平台没家底,工作几年,无车无房无存款还前景不明. 时常有身边的亲友问怎么学开发怎么转互联网,说起IT行业都说工资高,动辄月薪上万动辄年薪几十万. 再看看自己,我可能是假的程序员 ...

  10. Android 开发者,如何提升自己的职场竞争力?

    前言 该文章是笔者参加 Android 巴士线下交流会成都站 的手写讲稿虚拟场景,所以大家将就看一下. 开始 大家好,我是刘世麟,首先感谢安卓巴士为我们创造了这次奇妙的相遇.现场的氛围也让我十分激动. ...

随机推荐

  1. PlatformIO+esp32+添加自己的库(.c.h文件)

        什么都放main.c的话,很有可能堆积成屎山,所以我想给分开写,每个功能有自己的.c..h文件. 在lib下新建文件夹,例如led,再在里面分别建led.c.led.h; 写好内容后再main ...

  2. nginx rewrite 语法

    nginx rewrite 语法 一 定义 Rewrite主要实现url地址重写, 以及地址重定向,就是将用户请求web服 务器的地址重新定向到其他URL的过程. 二 语法格式 reweite fia ...

  3. [MAUI 项目实战] 音乐播放器(二):播放内核

    播放控制服务 IMusicControlService: 播放控制类,用于当前平台播放器对象的操作,对当前所播放曲目的暂停/播放,下一首/上一首,快进快退(寻迹),随机.单曲模式等功能的控制. 播放控 ...

  4. Git Flow 的正确使用姿势 - 分支 branch - master dev 使用方式

    Git Flow 的正确使用姿势 https://www.jianshu.com/p/41910dc6ef29

  5. SourceTree 摘樱桃 === 遴选 [不要使用这个功能!!不要使用!不要使用!]

    SourceTree 摘樱桃 === 遴选 不要使用摘樱桃!!不要使用!不要使用! 我找了一个文本的git,进行的测试,发现很不好用,文档我又恢复过来了,因为就改了几个字,代码的话,会造成 不可挽回的 ...

  6. JavaScript js 教程 视频教程

    一个完整的JavaScript实现应该由以下三个部分构成: ECMAScript,DOM和BOM 1 特点: JS的特点 解释型语言 类似于 C 和 Java 的语法结构 动态语言 基于原型的面向对象 ...

  7. Tornadofx学习笔记(3)——使用Maven编译成jar包

    之前我都是使用的IDEA自带的工具来编译jar包 但是增加了新的依赖,又得去修改project structure的依赖,过于麻烦 某天Android开发的时候,想到gradle可以一键打包,是不是m ...

  8. Web Audio API 第1章 基础篇

    Web Audio API 第1章 基础篇 我查了一下 Web Audio API 蝙蝠书居然在 2013 年就出版了 我又看了一下我的"豆瓣读书"频道内,这本书加入到" ...

  9. 利用log4j打印sql的log日志

    默认情况下,使用ibatis是不打印ibatis相关的log的,因为内部的sql执行都是内部调用,在server的控制台是不 会 打印log的. 在log4j的配置文件log4j.properties ...

  10. 掌握python的dataclass,让你的代码更简洁优雅

    dataclass是从Python3.7版本开始,作为标准库中的模块被引入.随着Python版本的不断更新,dataclass也逐步发展和完善,为Python开发者提供了更加便捷的数据类创建和管理方式 ...