无论是打数模竞赛,还是日常办公,总会遇到以$Excel$ 表格提供源数据的场景。然后我们就需要对其中的数据作处理、运算,最终再以$Excel$ 表格的形式把结果给呈现回去。如果是简单的一行两列的操作,直接在$office$ 上就能完成,而一旦有一些稍微复杂点的需求,那么使用程序来编写脚本则是非常合适的选择!其中,pythonpandas 库能满足大部分需求。

就不能不用 pandas 么

起初我也有这个质疑,毕竟在程序员眼里,表格不就是个二维数组嘛,那不如只使用读/写接口,其他的运算就转为普通的数组之后再自己实现嘛。而如果仅仅是对$Excel$ 表格(后缀为.xlsx的文件)作读写,python 内置的 csv 也能完成这个任务,根本不需要用 pandas,还搞得那么复杂。

对于数模比赛,因为表格中一般没有字符串,确实可以只用读/写接口。而一旦涉及日常办公,里面往往出现较多的字符串,此时表格的每一列的格式不同,不能直接无脑遍历,然后还要记住字符串列的下标,再用 if 作额外判断。列数多起来时,变量数蹭蹭往上涨,难度不亚于$CSP$ 第3题的大模拟,而且可读性极差,要一边打开表格,一边对照数组的下标看看是用的哪一个数,过个两天根本看不懂自己写了啥。

所以,用 pandas 也许不能优化算法的时间复杂度,但一定可以让代码变得美观易懂,并提升开发效率。

先学IO

当然是先学输入输出啦!读入都不会,怎么处理数据呢,QwQ。话不多说,先放代码:

import pandas as pd

df = pd.read_excel("./输入.xlsx") # 普通读入,会自动忽略第一行,将其视为表头
df = pd.read_excel("./输入.xlsx", header = None) # 把第一行视为数据,额外添加0,1,2,...作为默认表头

df.to_excel("./输出") # 普通输出,加一列填入行下标
df.to_excel("./输出",index = None) # 不输出行下标

安装库的话,一句pip install pandas即可。导入库时,往往会命名为 pd,这只是大伙的习惯,跟 numpy 命名为 np是一个道理,在网上搜或是问$ChatGPT$ 也会得到这种命名方式。使用pandas库时,存储表格的对象类型叫做 DataFrame,所以往往命名为 df

让我们看上述代码,输入用一句read_excel函数即可搞定,但需要注意自己的$Excel$ 表格的真正的数据是从第几行开始的,read_excel函数默认最顶上的 1 行为表头,读入后成为列下标,实践中不注意这个的话可能会漏掉一条数据。输出也是用一句to_excel函数就行,也一样要区分是否输出行下标。

pandas 的类的性质

使用 pandas 库时,它的对象类型跟数组有关系,但依然有较大的区别。

首先是存储表格的对象类型为 DataFrame,它类似一个二维数组,但是使用单个下标访问 DataFrame时,访问的是一整列,而不是一整行,而我们平时使用的numpy数组,单下标会访问一整行,这两者有一个转置的关系。所以想遍历表格的每一行时,不能用i作为下标去遍历,要用专门的遍历方式(后面会说)。

其次,我们常常会接触到的一个类型为 Series,它类似一个一维数组,其实就可以理解为 DataFrame的某一行或某一列,当使用单下标去访问 DataFrame时,返回的对象就是 Series类型的。

DataFrame 中,既有行下标,也有列下标。这两种下标既可以是自然数$0,1,2,\cdots$,也可以是字符串或布尔值等,它综合了python 中的列表和字典的访问方式,甚至额外加了其他的方式。所以后面一定要分清哪个是下标,哪个是真正返回的数。

DataFrame 的访问

当我们手上读入一个表格时,无论要遍历还是查询还是修改,前提都是访问数据。我们可能需要访问其中的某一列、某一行、某一格、满足条件的某一列、满足条件的某一行等等,这些其实都属于语法范畴,按模板抄就行。下面将列出访问表格数据的种种情况。

转为numpy数组

返璞归真式解决方案,如果表格本身是纯数值(没有字符串等),那么直接转为numpy数组处理是非常简单易读的,而且 DataFramenumpy数组的相互转换非常方便。

numpy_array = df.to_numpy()

df = pd.DataFrame(numpy_array) # 处理完之后可以将 numpy 转回 DataFrame

用下标访问 DataFrame

  • 自然数下标。

df[0],使用自然数作为下标时,返回的是一整列

  • 列名下标。

df["学号"],使用列名作为下标时,返回的是一整列

  • 布尔数组下标。

df[df["语文成绩"]>90]
使用布尔Seires作为下标时,返回的是布尔值为True的那些行。Seires类型可以进行整列的判断,返回相等长度的布尔数组。比如df["语文成绩"]>90语句会返回一个类似于[True,False,True]Seires数组,然后再用它作为 DataFrame 的下标,那么只有True的那几行会被返回,这种访问方式可以起到筛选的作用。

用下标访问 Seires

遍历 DataFrame 每一行

for index, row in df.iterrows(): # row的类型是 Seires
    print(row)

for i in range(len(df)): # 用自然数作下标
    row = df.iloc[i] # row的类型是 Seires
    print(row)

for row in df.itertuples():# row的类型是命名元组
    print(row) 

遍历 DataFrame 每一列

for col_name, col_data in df.iteritems(): # 用迭代器遍历
    print("列名:", col_name)
    print("对应的数据:", col_data) # 类型是 Seires

for col_name in df.columns: # 用列名作下标
    print(df[col_name]) # 类型是 Seires

遍历 Seires

for index, value in series.items(): # 用迭代器遍历
    print(value)

for i in range(len(series)): # 用下标遍历
    value = series[i]

for index, value in series.iteritems():
    print(value)

for index in series.index: # 假如series是一行,那么它的index可能是列名,即字符串
    print(series[index])

返回某几列

Dataframe支持用多个下标,只要把原本的多个下标组成一个列表,然后把该列表作为下标即可,其中也有类似于python切片的机制。

print(df[['A', 'C']]) # 用列名取出若干列
print(df.iloc[:, [0, 2]]) # 用下标取出若干列,这里取出的是第0列和第2列

返回某几行

print(df.iloc[[1, 3]]) # 用下标取出若干行,这里取出的是第0行和第3行
print(df.loc[['row1', 'row3']]) # 用行标签取出若干列,但通常情况其实行标签默认为自然数下标

返回子表格

如果只想取表格中的某几列的某几行,那么其实按顺序运用上面两节的方法就能做到,当然,也可以像下面这样一步到位。其中,iloc方法的用法跟numpy数组很像;而loc方法虽然是用行标签作索引,但大多数情况中,行标签就是自然数下标,所以可以将行下标和列名混用,个人感觉loc更好用。

print(df.iloc[2:6, [0, 2]]) # 用下标取,这里取了第0列和第2列的2到6行,用法很像numpy

print(df.loc[['row1', 'row2'], ['A', 'C']]) # 用列名、行标签取
print(df.loc[[0, 1], ['A', 'C']]) # 行标签默认为自然数下标的情况。

遍历每一格

其实,把前面的方法结合起来,就能得到遍历每一格的方法,就像遍历数组一样,一维一维依次遍历即可。下面没有提到转换成numpy的方法,但其实先转成numpy再遍历也是一种可行的方法。

# 第一种,iterrows方法,先遍历行,再遍历列
for index, row in df.iterrows():
    for column in df.columns:
        print(row[column])

# 第二种,itertuples方法,也是遍历行,再遍历列,但使用了命名元组,比上一种更高效
for row in df.itertuples():
    for column in df.columns:
        print(getattr(row, column))

# 第三种,同时访问行标签和列标签,然后用标签定位单元格
for i in df.index:
    for j in df.columns:
        print(df.at[i, j])

结语

本篇讲述了怎么用python去处理Excel文件,有了 pandas 库的各种访问方法,那么剩下的数值计算、字符串处理等无非就是简单的算法题了,当然也有可能上升到极其复杂的大模拟(笑),那些就看实际要实现的业务咯。

这一篇博客所描述的技能相当简单,但应用面其实非常广泛,现今其实除了理工科的职业,其实很多岗位每天都处理着重复的文档工作,如果可以用python将自动化办公引入这些地方,可以大幅提高工作效率。相比于令人眼花缭乱的文生视频技术,也许用python处理文档表格的技能更受欢迎。