从九月的那次出差开始,这几个月一直跟表格打交道,最开始用openpyxl
,循环对表格的数据进行处理,数据一多或者字段一多,就导致运行速度非常慢,经常处理一个几百行的数据量都要几分钟的时间。如果涉及到多数据的循环对比,那就更慢了,每一次对比就是一次笛卡尔积,python的运行效率肯定跟不上。
某天,一个老哥拿着我的代码学习,问了我一句,为什么你不用pandas,openpyxl和pandas有什么区别。瞬时茅舍顿开,知乎老是给我推送pandas如何如何的强,数据分析必备,为啥我没用,于是马上转移到pandas上,pandas不得不说是数据分析的神器,底层的关键操作用c语言进行了实现,结合python的易用性,pandas不愧为数据分析的必备神器。本文就记录一下我这几个月用到的常见的分析代码。
1. 读写文件
openpyxl读表格费时费力,读取sheet后,还需要循环读取每一个cell的值,然后才能得到整张表,在循环的时候,python运行起来就很慢了,如果表格数据量成千上万,那么读取时间会非常长。
pandas虽然读取表格的底层也是openpyxl,但是做了优化,读取时间短很多,而且读取出来直接是一个DataFrame,直接能够二维操作,非常方便,而且读取的代码也异常简单。
1 | # 读取表格 |
以上代码在读取数据时,会默认设置数据的类型,比如是一个字符,读取后的类型就是str,如果是一个数字,读取后的类型就是int。但是有时候,需要将读取的所有数据为str,方便我们做字符串的拼接,那么就需要按照如下的方式进行读取
1 | import pandas as pd |
经常在读取人工管理表的时候,存在合并单元格的情况,在使用默认方法读取表格的时候,数据只会填充第一行,合并单元格的剩余行会显示Nan
,在处理数据的时候会非常麻烦,pandas支持默认填充方式,代码如下:
1 | df = pd.read_excel(filename).fillna(method='ffill') |
上面的代码会填充合并单元格中的除第一排以外的单元格,method='ffill'
表示用前一个值去填充后面的值,还有一个bfill
,就是用后面的值填充前面的值,一般情况下,都是用ffill
。
写表格的代码如下,可直接将DataFrame输出为excel表格:
1 | df.to_excel('out.xlsx') |
默认输出的表格中,第一列会有序列号,默认带有索引值,但我们大多数时候都是不需要索引值输出的,所以一般情况下使用的输出代码如下:
1 | df.to_excel('out.xlsx', index=None) |
如果要指定sheet页的名称的话,加上sheet_name=xxx
就可以了。
2. 分组筛选合并
pandas的筛选很简单,通过[]
就能根据字段或者条件进行筛选。
取某几列
1 | new_df = df[['columns1', 'columns2', 'columns3']] |
对某列的值进行筛选,选除满足条件的值
以下代码可以筛选出状态列包含通过的行
1 | df = df[df['状态'].str.contains('通过')] |
如果存在多条件筛选,需要使用&
和|
进行逻辑计算
1 | df = common_df[common_df['风险状态_x'].str.contains('未通过') & common_df['风险状态_y'].str.contains('未通过')].copy() |
合并多个DataFrame
pandas在使用append函数的时候,不会改变原来的DataFrame,因此需要将返回的新的值赋值给一个DataFrame
1 | df = df.append([df1, df2]) |
分组
对数据的分组也是经常用到的,比如将值为某某的分成一组进行输出。pandas中用到的函数为groupby
,跟SQL中的groupby极为相似,可以非常方便的使用
1 | groups = df.groupby('系统名称') |
在使用groupby的时候,会返回一个迭代器,通过循环可以读取分组后的结果,如上面代码所示,sysname为分组的依据,通过什么字符串进行的分组,item则是分组结果形成的DataFrame。
3. 交集并集差集
在数据分析中经常需要对两个或者多个数据求交集、并集和差集,pandas也提供了非常方便的merge
函数,实现逻辑基本与SQL语言的join类似。
内连接
merge函数中的how参数需要传入连接方式的参数,参数的值有left, right, outer, inner,与SQL语言保持一致,分别是左连接,右连接,外连接和内连接。
默认的值为inne
,inner连接的作用为求两个集合的交集部分。
merge函数的使用如下:
1 | pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, |
left需要指定在左边的DataFrame,right指定在右边的DataFrame,on是一个列表,指定相交的字段,如果两边的DataFrame没有相同名称的字段,那么就需要left_on和right_on指定对应的列名。
所有连接产生的结果,均包含两边DataFrame的所有字段,如果字段名相同,会重命名为字段_x
、字段_y
。
实例代码参考如下:
1 | common_df = pd.merge(left=self.last_baseline_result, right=self.this_baseline_result, |
左连接、右连接
左连接会保留左边所有的值,并且保留右边与左边相交的值。
1 | import pandas as pd |
同理,右连接会保留右边所有的值,并且保留左边与右边相交的值
1 | import pandas as pd |
外连接
外连接就是求并集。取两边加起来的所有的值
1 | import pandas as pd |
差集
还有一种情况,就是需要求A中不包含B的部分,这个就需要用到差集。pandas的merge函数不支持直接求差集,但是经过inner连接得到交集后,能够通过取非的运算把差集求出来。
1 | import pandas as pd |
4. 其他
除上面常用的几种操作外,还有一些小细节的东西,都记录在这里。
合并单元格
经常在表格输出的时候,需要对单元格进行合并,但是pandas并不支持单元格合并,所以需要用到集成在pandas库中的openpyxl引擎。
代码可以按照如下写法:
1 | writer = pd.ExcelWriter('xxx.xlsx', engine='openpyxl') |
隐藏列
1 | writer = pd.ExcelWriter('xx.xlsx', engine='openpyxl') |
对一列值进行操作
要对一列值进行操作,不需要使用循环,直接使用apply加上匿名函数,就能搞定一列值的修改,修改速度非常快。
1 | df['name'] = df['name'].apply(lambda x: get_problem(x)) |
取两列生成字典
1 | netword_df = netword_df.set_index(['网段名称'])['地址范围'].to_dict() |