在数据处理工作中,我们经常拿到一份 Excel 报表,里面有两列分别代表预期值和实际值,需要在旁边新增一列来标记每一行的比对状态,例如“一致”“不一致”或“缺失”。Pandas 作为 Python 主流的数据分析库,提供了非常高效的向量化操作,可以让我们不写循环就完成这个任务,并把结果直接写回 Excel 文件。
读取 Excel 与基础环境准备
开始之前,请确保已安装 pandas 和 openpyxl(用于读写 xlsx 格式)。如果还没有安装,可以在命令行执行 pip install pandas openpyxl。Pandas 读取 Excel 主要依赖 read_excel 函数,它会返回一个 DataFrame 对象,我们可以像操作表格一样操作它。
下面示例假设本地有一个名为 sales.xlsx 的文件,里面有两列:expected(预期销量)和 actual(实际销量)。我们要新增一列 status 来标记比较结果。注意,Excel 中的空单元格在 Pandas 里通常会变成 NaN,这在后续比较时需要特别处理。
import pandas as pd
import numpy as np
# 读取 Excel 文件中的第一个sheet
df = pd.read_excel('sales.xlsx', sheet_name=0)
# 查看前几行数据和字段类型
print(df.head())
print(df.dtypes)
使用向量化比较新增状态列
最直观的做法是利用 Pandas 的向量化运算,直接对两列做等号比较,会得到一个布尔型 Series。再配合 numpy 的 where 函数,就能把 True 和 False 映射成我们想要的文字状态。这种方法比用 for 循环逐行判断快得多,在处理几万行数据时优势明显。
如果两列可能出现空值,直接用 == 比较会得到 False 而不是 NaN,这可能不符合业务预期。我们可以先使用 fillna 给缺失值一个特定标记,或者在比较后用 mask 把含 NaN 的行单独标为“缺失”。下面的代码展示了标准比对与空值处理的结合方式。
# 基础比对:一致 / 不一致
df['status'] = np.where(df['expected'] == df['actual'], '一致', '不一致')
# 改进版:先判断任意一侧为空则标为缺失
df['status'] = np.where(
df['expected'].isna() | df['actual'].isna(),
'缺失',
np.where(df['expected'] == df['actual'], '一致', '不一致')
)
print(df.head())
除了 np.where,也可以链式使用 map 或者 DataFrame.loc 进行赋值。但在多条件场景下,np.where 嵌套可读性更好,而且完全向量化,不会引入 Python 层循环开销。如果状态分类超过三种,建议改用 pd.cut 或字典映射。
基于多列复杂规则的状态标记
实际业务中,状态往往不是单纯比大小或比相等。例如当实际销量低于预期的 80% 时标记为“预警”,高于预期标记为“超标”,其余为“正常”。这时可以用多个条件组合,仍然保持向量化写法。
下面例子使用 numpy.select 来声明条件列表和对应取值,比多层 np.where 更清晰。同时演示了如何将结果写回新的 Excel 文件,而不覆盖原文件,保证数据安全。
conditions = [
df['actual'].isna() | df['expected'].isna(),
df['actual'] < df['expected'] * 0.8,
df['actual'] > df['expected'],
df['actual'] == df['expected']
]
choices = ['缺失', '预警', '超标', '一致']
df['status'] = np.select(conditions, choices, default='正常')
# 写出到新 Excel,不保留原索引列
df.to_excel('sales_with_status.xlsx', index=False)
使用 np.select 时要注意 conditions 的顺序,因为它会返回第一个满足的条件对应的选择。比如“相等”应放在“超标”之后,否则相等时会被前面条件拦截。写出文件时 index=False 可以避免多出一行 unnamed 索引,保持 Excel 整洁。
数据类型对齐与常见坑
一个容易踩的坑是两列数据类型不一致,比如一列被读成 float(带小数点),另一列是 int 或被读成 object 字符串。此时直接用 == 比较可能永远返回 False。可以在读取后强制转换,例如 df['expected'] = df['expected'].astype(float)。
另外,如果 Excel 里某些单元格含有不可见字符(如空格),也会导致比对失败。可以先用 str.strip 清洗文本列。下例展示了对文本类列做标准化后再比较的做法,这对单据号、编码类字段尤其重要。
# 假设两列都是文本型编码
df['code_a'] = df['code_a'].astype(str).str.strip()
df['code_b'] = df['code_b'].astype(str).str.strip()
df['status'] = np.where(
df['code_a'] == df['code_b'],
'匹配',
'不匹配'
)
当数据量极大时,还可以考虑分块读取 Excel(chunksize 参数)以避免内存溢出,但一般报表类文件在百万行内 Pandas 都能轻松应对。最终生成的状态列可以直接用 Excel 的条件格式做颜色标记,也可以在 Pandas 里用 groupby 统计各状态数量,快速产出核对摘要。
小结与扩展思路
通过 Pandas 在 Excel 中添加基于列比较的状态列,核心就是利用向量化比较加条件映射函数(np.where / np.select)。它比 Excel 公式更易复用、能批处理、方便接入自动化脚本。后续你还可以把这些逻辑封装成函数,定时从邮箱或接口拉取 Excel 并自动生成核对报告,进一步提升数据处理效率。