3. 环境监测数据采集基础

环境监测工作的第一步往往不是建模,而是把现场记录、仪器导出文件或实验室台账“读进来”。这些文件通常以 CSV 或 Excel 形式存在,看似简单,却常常因为编码、分隔符、表头行、缺失值等问题导致读取失败或数据错位。本节围绕一个典型场景展开:假设我们拿到某监测点一周的逐小时空气质量导出文件,需要把它加载为 pandas DataFrame,并快速判断数据是否完整、字段是否正确。

一、先认识两类文件与三个关键参数

CSV 是纯文本表格,字段之间用某个字符分隔,常见为英文逗号、分号或制表符;Excel 是二进制工作簿,一个文件可包含多个工作表。读取时最需要关注三个参数:编码、分隔符、表头。编码决定中文能否正确显示,Windows 下常见 gbk,跨平台交换常用 utf-8;分隔符决定列能否被正确切开;表头决定第一行是列名还是数据。

实际文件常带有“说明行”。例如仪器导出的 CSV 前两行可能是设备编号和导出时间,第三行才是字段名。此时若直接读取,pandas 会把说明行当作列名。正确做法是使用 header 参数指定列名所在行(从 0 开始计数),或用 skiprows 跳过前置行。

import pandas as pd

# 示例:读取 UTF-8 编码、逗号分隔、第 3 行为表头的 CSV
df = pd.read_csv(
    "air_quality_week.csv",
    encoding="utf-8",
    sep=",",
    header=2
)
print(df.head())
print(df.shape)
print(df.dtypes)

如果文件是 GBK 编码,把 encoding 改为 "gbk" 即可;若分隔符是分号,则设 sep=";";制表符可用 sep="\t"。读取 Excel 使用 pd.read_excel,通过 sheet_name 指定工作表,通过 header 指定表头行,编码参数对 Excel 不适用。

二、表头与缺失值的处理

表头问题通常表现为列名带空格、含单位括号、重复或为空。建议读入后立即规范化列名,例如去除首尾空格、统一小写、把中文单位保留在列名中以便识别。缺失值在监测数据中极为常见:仪器断电、通信中断、校准期间都会产生空值。pandas 默认把空字符串、NA、NaN 等识别为缺失,但有些文件用 -999 或 -- 表示无效值,需要通过 na_values 显式声明。

df = pd.read_csv(
    "air_quality_week.csv",
    encoding="gbk",
    sep=",",
    header=2,
    na_values=["-999", "--", "NA", ""]
)

df.columns = [c.strip().lower() for c in df.columns]
print(df.isna().sum())          # 每列缺失数量
print(df.isna().mean().round(3)) # 每列缺失比例

判断缺失是否可接受要结合业务:若某污染物整列缺失比例超过 30%,该列基本不具备分析价值;若只是零星缺失,可在后续按时间插值或标记剔除。注意不要在不了解含义时直接 fillna(0),把“未测到”当成“浓度为 0”会严重扭曲统计结果。

三、加载后的基本信息检查

DataFrame 读入后,应形成固定的“体检”习惯:用 shape 看行列数,用 dtypes 看字段类型,用 head() 和 tail() 看首尾记录,用 describe() 看数值分布。特别要检查时间列是否被识别为字符串——若时间列是 object 类型,后续按时间排序和重采样都会出问题,应使用 pd.to_datetime 转换。数值列若被读成字符串,往往是因为混入了单位字符或异常符号,需要清洗后再转换。

df["time"] = pd.to_datetime(df["time"], errors="coerce")
print(df["time"].isna().sum())   # 转换失败的行数
print(df.describe())
print(df.sample(5, random_state=1))

下图概括了从原始文件到可用 DataFrame 的关键判断路径,重点标出编码、分隔符、表头、缺失值和时间类型五个检查点,可作为每次读取新文件时的对照参考。

环境监测文件读取:从原始文件到可用 DataFrame 原始文件 CSV / Excel 仪器导出或台账 读取参数 encoding:utf-8 / gbk sep:, ; \t header / skiprows / na_values pandas 读取 read_csv / read_excel 返回 DataFrame 读入后五项检查 ① 编码 中文是否乱码 列名是否可读 ② 分隔符 列数是否匹配 是否挤在一列 ③ 表头 列名是否为字段 说明行是否混入 ④ 缺失值 isna().sum() 无效标记是否识别 ⑤ 类型 时间列是否 datetime 数值列是否数值型 可用 DataFrame 字段规范 · 时间可解析 · 缺失可量化 · 可进入清洗与分析

四、一个可复用的读取小函数

为避免每次重复试错,可以把常用参数封装成函数,并加入读取后的自动检查。下面的示例仅作教学参考,参数需按实际文件调整:

def load_monitor_file(path, encoding="utf-8", sep=",", header=0):
    df = pd.read_csv(
        path, encoding=encoding, sep=sep, header=header,
        na_values=["-999", "--", "NA", ""]
    )
    df.columns = [str(c).strip().lower() for c in df.columns]
    if "time" in df.columns:
        df["time"] = pd.to_datetime(df["time"], errors="coerce")
    report = {
        "shape": df.shape,
        "missing_ratio": df.isna().mean().round(3).to_dict(),
        "time_invalid": int(df["time"].isna().sum()) if "time" in df else None
    }
    return df, report

df, report = load_monitor_file("air_quality_week.csv", encoding="gbk", header=2)
print(report)

运行后若发现 shape 的列数与预期不符,优先怀疑分隔符;若列名出现乱码,优先怀疑编码;若首行数据变成了列名,优先怀疑表头行号;若时间列大量转换失败,应查看原始时间格式是否统一。把这些判断固化为习惯,后续的清洗、统计和可视化才有可靠基础。