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 的关键判断路径,重点标出编码、分隔符、表头、缺失值和时间类型五个检查点,可作为每次读取新文件时的对照参考。
四、一个可复用的读取小函数
为避免每次重复试错,可以把常用参数封装成函数,并加入读取后的自动检查。下面的示例仅作教学参考,参数需按实际文件调整:
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 的列数与预期不符,优先怀疑分隔符;若列名出现乱码,优先怀疑编码;若首行数据变成了列名,优先怀疑表头行号;若时间列大量转换失败,应查看原始时间格式是否统一。把这些判断固化为习惯,后续的清洗、统计和可视化才有可靠基础。