3. 数据表结构统一与字段对齐
污染源监测数据往往来自多个渠道:排污单位自行监测的Excel报表、在线监控系统导出的CSV、第三方检测机构提供的汇总表,以及历史归档的旧版台账。这些表在业务上描述的是同一件事——某个排口在某段时间的污染物浓度与排放量,但落到文件层面,列名、列序、缺失情况和数据类型几乎不可能天然一致。批量清洗的第二步,就是把这些“各说各话”的表结构,收敛成一套统一的字段规范,让后续的合并、计算和校验有稳定的输入。
这一步的核心不是简单改个列名,而是建立“标准字段集 + 映射规则 + 对齐执行”三层结构。标准字段集定义清洗后每张表必须长什么样;映射规则描述每个来源表的列如何对应到标准字段;对齐执行则按规则批量重排、补列、改名并统一类型。三者缺一,批量处理就会退化成逐表手工调整。
先定义标准字段集,再谈对齐
标准字段集是整个清洗流程的“契约”。它应当由业务需求反推:后续要做浓度超标判断,就必须有污染物、浓度、限值关联字段;要做排放量核算,就必须有流量和浓度;要追溯数据可信度,就必须保留数据来源。以本课程使用的示例为例,标准字段集可定义为:排口编号、监测日期、污染物、浓度、流量、排放量、数据来源、单位,共8列,列序固定。其中排口编号、监测日期、污染物、浓度为必填,流量允许为空,排放量为计算列,单位默认mg/L。
字段类型也要一并约定。监测日期统一为YYYY-MM-DD的日期类型,浓度和流量为浮点数,排口编号为字符串(避免前导零丢失)。这些约定写进一个配置字典,后续所有来源表都向它看齐,而不是每张表单独判断。
用映射字典描述列名差异
列名不一致是最常见的障碍。来源A叫“排口编号”,来源B叫“OUTLET_ID”,来源C叫“点位名称”。处理方式不是写一堆if分支,而是为每个来源维护一份映射字典,键是来源列名,值是标准字段名。例如来源B的映射可写成:
MAP_B = {
"OUTLET_ID": "排口编号",
"TIME": "监测日期",
"ITEM": "污染物",
"VALUE": "浓度",
"FLOW": "流量",
"UNIT": "单位",
}
来源C的“点位名称”不能直接改名,因为它是名称而非编号,需要先经过一张点位对照表转换为排口编号,再进入映射。这类“值映射”与“列映射”要分开处理:列映射解决列名,值映射解决列内取值口径。来源C多出的“检测方法”列不在标准字段集内,对齐时直接丢弃;来源A缺少的“排放量”和“数据来源”,则分别用计算和常量补齐。
对齐执行的四个动作
有了标准字段集和映射规则,批量对齐可以归纳为四个动作,按固定顺序执行:
- 改名:按映射字典把来源列名替换为标准字段名,未出现在字典中的列标记为待丢弃。
- 补列:对照标准字段集,缺失的列补为空值(
None或NaN),计算列和常量列在此步填充。 - 重排:按标准字段集的列序重新索引,保证所有表列序完全一致。
- 转类型:按约定把日期、数值列转换为目标类型,无法转换的值置为缺失并记录。
这四个动作对每张表执行同一套逻辑,差异只体现在映射字典和来源标记上。这样,新增一个数据来源时,只需增加一份映射配置,而不必改动对齐代码本身。
一个可执行的批量对齐函数
把上述逻辑封装成函数,输入是原始表、映射字典和来源名称,输出是符合标准结构的表:
import pandas as pd
STANDARD_COLS = ["排口编号","监测日期","污染物","浓度",
"流量","排放量","数据来源","单位"]
def align_table(df, mapping, source_name):
df = df.rename(columns=mapping)
# 丢弃非标准列
df = df[[c for c in df.columns if c in STANDARD_COLS]]
# 补缺失列
for col in STANDARD_COLS:
if col not in df.columns:
df[col] = None
# 固定列序
df = df[STANDARD_COLS]
# 类型转换
df["监测日期"] = pd.to_datetime(df["监测日期"], errors="coerce").dt.date
for col in ["浓度","流量","排放量"]:
df[col] = pd.to_numeric(df[col], errors="coerce")
df["数据来源"] = source_name
df["单位"] = df["单位"].fillna("mg/L")
return df
注意两个细节:一是errors="coerce"让无法解析的值变成缺失而不是抛错,保证批量处理不中断;二是“数据来源”在补列之后统一赋常量,避免来源表自带的值干扰。排放量若来源表未提供,可在对齐后按“浓度×流量×换算系数”计算,换算系数依单位而定,示例中假设浓度mg/L、流量m³/h,则排放量(kg/h)= 浓度×流量÷1000,该系数需在配置中显式声明,不能硬编码在函数里。
对齐结果如何判断
批量执行后,不能只看“没报错”就认为成功。应检查三项:所有输出表的列名与列序是否与标准字段集完全一致;必填字段的缺失比例是否在可接受范围;类型转换失败的数量是否被记录。例如对来源B执行后,若“监测日期”出现大量NaT,往往说明原始时间格式与预期不符,需要回到映射规则中补充格式说明,而不是在对齐阶段强行填充。对齐的目标是结构一致且问题可见,而不是把异常悄悄抹平。
结构统一完成后,各表已经具备相同的“骨架”,下一步才能安全地纵向合并、按排口与日期关联限值,并进入缺失值与异常值的处理。字段对齐看似机械,实则是整个批量清洗流程能否稳定复现的分水岭。