HOME
HOME
文章目录
  1. 1. 读写文件
  2. 2. 分组筛选合并
  3. 3. 交集并集差集
  4. 4. 其他

pandas常用代码总结

从九月的那次出差开始,这几个月一直跟表格打交道,最开始用openpyxl,循环对表格的数据进行处理,数据一多或者字段一多,就导致运行速度非常慢,经常处理一个几百行的数据量都要几分钟的时间。如果涉及到多数据的循环对比,那就更慢了,每一次对比就是一次笛卡尔积,python的运行效率肯定跟不上。

某天,一个老哥拿着我的代码学习,问了我一句,为什么你不用pandas,openpyxl和pandas有什么区别。瞬时茅舍顿开,知乎老是给我推送pandas如何如何的强,数据分析必备,为啥我没用,于是马上转移到pandas上,pandas不得不说是数据分析的神器,底层的关键操作用c语言进行了实现,结合python的易用性,pandas不愧为数据分析的必备神器。本文就记录一下我这几个月用到的常见的分析代码。

1. 读写文件

openpyxl读表格费时费力,读取sheet后,还需要循环读取每一个cell的值,然后才能得到整张表,在循环的时候,python运行起来就很慢了,如果表格数据量成千上万,那么读取时间会非常长。

pandas虽然读取表格的底层也是openpyxl,但是做了优化,读取时间短很多,而且读取出来直接是一个DataFrame,直接能够二维操作,非常方便,而且读取的代码也异常简单。

1
2
3
4
# 读取表格
import pandas as pd

data = pd.read_excel(filename) # 读取后返回一个DataFrame对象

以上代码在读取数据时,会默认设置数据的类型,比如是一个字符,读取后的类型就是str,如果是一个数字,读取后的类型就是int。但是有时候,需要将读取的所有数据为str,方便我们做字符串的拼接,那么就需要按照如下的方式进行读取

1
2
3
import pandas as pd

data = pd.read_excel(filename, dtype='str') # 使用dtype参数,将读取的数据全部设置为str类型

经常在读取人工管理表的时候,存在合并单元格的情况,在使用默认方法读取表格的时候,数据只会填充第一行,合并单元格的剩余行会显示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
2
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
2
3
groups = df.groupby('系统名称')
for sysname, item in groups:
print(sysname)

在使用groupby的时候,会返回一个迭代器,通过循环可以读取分组后的结果,如上面代码所示,sysname为分组的依据,通过什么字符串进行的分组,item则是分组结果形成的DataFrame。

3. 交集并集差集

在数据分析中经常需要对两个或者多个数据求交集、并集和差集,pandas也提供了非常方便的merge函数,实现逻辑基本与SQL语言的join类似。

内连接

merge函数中的how参数需要传入连接方式的参数,参数的值有left, right, outer, inner,与SQL语言保持一致,分别是左连接,右连接,外连接和内连接。

默认的值为inne,inner连接的作用为求两个集合的交集部分。

merge函数的使用如下:

1
2
3
4
5
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True,
suffixes=('_x', '_y'), copy=True, indicator=False,
validate=None)

left需要指定在左边的DataFrame,right指定在右边的DataFrame,on是一个列表,指定相交的字段,如果两边的DataFrame没有相同名称的字段,那么就需要left_on和right_on指定对应的列名。

所有连接产生的结果,均包含两边DataFrame的所有字段,如果字段名相同,会重命名为字段_x字段_y

实例代码参考如下:

1
2
3
4
common_df = pd.merge(left=self.last_baseline_result, right=self.this_baseline_result,
on=['风险名称', '影响资产IP(公网)', '影响资产IP(私网)', '影响资产备注名称',
'检查项分类', '检查项名称', '检查项描述', '风险等级', '检测提示', '修复建议'],
how='inner', )

左连接、右连接

左连接会保留左边所有的值,并且保留右边与左边相交的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd

X = pd.DataFrame({'name':['A','B','C','D'],'age':[1,2,3,4]})
Y = pd.DataFrame({'name':['A','B','E'],'age':[1,5,6]})

print(pd.merge(X, Y, on='name', how='left'))

# output
name age_x age_y
0 A 1 1.0
1 B 2 5.0
2 C 3 NaN
3 D 4 NaN

同理,右连接会保留右边所有的值,并且保留左边与右边相交的值

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd

X = pd.DataFrame({'name':['A','B','C','D'],'age':[1,2,3,4]})
Y = pd.DataFrame({'name':['A','B','E'],'age':[1,5,6]})

print(pd.merge(X, Y, on='name', how='right'))

# output
name age_x age_y
0 A 1.0 1
1 B 2.0 5
2 E NaN 6

外连接

外连接就是求并集。取两边加起来的所有的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd

X = pd.DataFrame({'name':['A','B','C','D'],'age':[1,2,3,4]})
Y = pd.DataFrame({'name':['A','B','E'],'age':[1,5,6]})

print(pd.merge(X, Y, on='name', how='outer'))

#output
name age_x age_y
0 A 1.0 1.0
1 B 2.0 5.0
2 C 3.0 NaN
3 D 4.0 NaN
4 E NaN 6.0

差集

还有一种情况,就是需要求A中不包含B的部分,这个就需要用到差集。pandas的merge函数不支持直接求差集,但是经过inner连接得到交集后,能够通过取非的运算把差集求出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd

X = pd.DataFrame({'name':['A','B','C','D'],'age':[1,2,3,4]})
Y = pd.DataFrame({'name':['A','B','E'],'age':[1,5,6]})

inner = pd.merge(X, Y, on='name', how='inner')
print(inner)

print(X[~X['name'].isin(inner['name'])])

# ouput
name age_x age_y
0 A 1 1
1 B 2 5
name age
2 C 3
3 D 4

4. 其他

除上面常用的几种操作外,还有一些小细节的东西,都记录在这里。

合并单元格

经常在表格输出的时候,需要对单元格进行合并,但是pandas并不支持单元格合并,所以需要用到集成在pandas库中的openpyxl引擎。

代码可以按照如下写法:

1
2
3
4
5
writer = pd.ExcelWriter('xxx.xlsx', engine='openpyxl')
sheet1 = writer.sheets[0]
sheet1.merge_cells(f'A{row}:A{end}')
writer.save()
writer.close()

隐藏列

1
2
3
4
5
6
writer = pd.ExcelWriter('xx.xlsx', engine='openpyxl')
for col in writer.sheets[0].columns:
writer.sheets[0].column_dimensions[str(col[0].column_letter)].hidden = True
writer.save()
writer.close()

对一列值进行操作

要对一列值进行操作,不需要使用循环,直接使用apply加上匿名函数,就能搞定一列值的修改,修改速度非常快。

1
2
3
4
5
6
7
8
9
10
df['name'] = df['name'].apply(lambda x: get_problem(x))

def get_problem(item):
"""
分割问题字段,先分割逗号,再分割冒号,生成字典
:return:
"""
ziduan = item.split(',')
result = dict(x.split(':') for x in ziduan)
return result

取两列生成字典

1
netword_df = netword_df.set_index(['网段名称'])['地址范围'].to_dict()