Python 并不适合职场编程

BI工作者 2020-11-11 19:03:28
Python pydata


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

分组理解

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

..

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

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

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)

 

 

按照部门分组

运行结果:

<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001ADBC9CE0F0>

哇,这是个什么东东?

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

不过上网搜搜还是可以看到它是一个所谓的可迭代对象,迭代以后发现它的每一条都是以分组索引 + 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 本质是矩阵

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

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 中呈现的结果一致,如下图:

..

上表中除了第一行(标题行)外,其他每一行表示一条记录,而序表就是记录的集合,相较于 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 那么费劲。

2.     子集求交

问题三:找出既是纽约州又是 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

版权声明
本文为[BI工作者]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/3980313/blog/4713252

  1. 利用Python爬虫获取招聘网站职位信息
  2. Using Python crawler to obtain job information of recruitment website
  3. Several highly rated Python libraries arrow, jsonpath, psutil and tenacity are recommended
  4. Python装饰器
  5. Python实现LDAP认证
  6. Python decorator
  7. Implementing LDAP authentication with Python
  8. Vscode configures Python development environment!
  9. In Python, how dare you say you can't log module? ️
  10. 我收藏的有关Python的电子书和资料
  11. python 中 lambda的一些tips
  12. python中字典的一些tips
  13. python 用生成器生成斐波那契数列
  14. python脚本转pyc踩了个坑。。。
  15. My collection of e-books and materials about Python
  16. Some tips of lambda in Python
  17. Some tips of dictionary in Python
  18. Using Python generator to generate Fibonacci sequence
  19. The conversion of Python script to PyC stepped on a pit...
  20. Python游戏开发,pygame模块,Python实现扫雷小游戏
  21. Python game development, pyGame module, python implementation of minesweeping games
  22. Python实用工具,email模块,Python实现邮件远程控制自己电脑
  23. Python utility, email module, python realizes mail remote control of its own computer
  24. 毫无头绪的自学Python,你可能连门槛都摸不到!【最佳学习路线】
  25. Python读取二进制文件代码方法解析
  26. Python字典的实现原理
  27. Without a clue, you may not even touch the threshold【 Best learning route]
  28. Parsing method of Python reading binary file code
  29. Implementation principle of Python dictionary
  30. You must know the function of pandas to parse JSON data - JSON_ normalize()
  31. Python实用案例,私人定制,Python自动化生成爱豆专属2021日历
  32. Python practical case, private customization, python automatic generation of Adu exclusive 2021 calendar
  33. 《Python实例》震惊了,用Python这么简单实现了聊天系统的脏话,广告检测
  34. "Python instance" was shocked and realized the dirty words and advertisement detection of the chat system in Python
  35. Convolutional neural network processing sequence for Python deep learning
  36. Python data structure and algorithm (1) -- enum type enum
  37. 超全大厂算法岗百问百答(推荐系统/机器学习/深度学习/C++/Spark/python)
  38. 【Python进阶】你真的明白NumPy中的ndarray吗?
  39. All questions and answers for algorithm posts of super large factories (recommended system / machine learning / deep learning / C + + / spark / Python)
  40. [advanced Python] do you really understand ndarray in numpy?
  41. 【Python进阶】Python进阶专栏栏主自述:不忘初心,砥砺前行
  42. [advanced Python] Python advanced column main readme: never forget the original intention and forge ahead
  43. python垃圾回收和缓存管理
  44. java调用Python程序
  45. java调用Python程序
  46. Python常用函数有哪些?Python基础入门课程
  47. Python garbage collection and cache management
  48. Java calling Python program
  49. Java calling Python program
  50. What functions are commonly used in Python? Introduction to Python Basics
  51. Python basic knowledge
  52. Anaconda5.2 安装 Python 库(MySQLdb)的方法
  53. Python实现对脑电数据情绪分析
  54. Anaconda 5.2 method of installing Python Library (mysqldb)
  55. Python implements emotion analysis of EEG data
  56. Master some advanced usage of Python in 30 seconds, which makes others envy it
  57. python爬取百度图片并对图片做一系列处理
  58. Python crawls Baidu pictures and does a series of processing on them
  59. python链接mysql数据库
  60. Python link MySQL database