2. 监测数据读取与初步审视
在开始任何指标对比或分组统计之前,第一步永远不是画图,也不是算均值,而是把数据“看清楚”。多监测点数据通常来自不同站点、不同设备或不同导出批次,常见问题包括:列名不一致、时间列被读成字符串、数值列混入单位符号、缺失值以多种形式出现、同一时刻的记录被重复导入。如果这些问题没有在读取阶段暴露出来,后续的分组统计就会建立在错误的基础上,而且错误往往很难从结果图中反查。
本节的任务是:用 pandas 把多个监测点的 CSV 或 Excel 文件读入,检查数据形状、列类型、缺失值与重复记录,形成一份可复查的“数据质量快照”。这份快照不是最终分析报告,而是后续清洗与建模的依据。
一、读取多文件:先合并,再审视
多监测点数据一般有两种组织方式:一种是每个监测点一个文件,另一种是一个文件内用“监测点编号”列区分。对前者,推荐先批量读取并追加为一个长表,同时保留来源信息。示例假设有三个监测点文件 site_A.csv、site_B.csv、site_C.csv,每个文件包含 timestamp、pm25、temperature、humidity 四列:
import pandas as pd
from pathlib import Path
frames = []
for fp in sorted(Path("data").glob("site_*.csv")):
df = pd.read_csv(fp, encoding="utf-8")
df["source_file"] = fp.stem # 保留来源,便于追溯
frames.append(df)
raw = pd.concat(frames, ignore_index=True)
print(raw.shape)
print(raw.dtypes)
这里有两个细节值得注意。第一,source_file 列是后续排查问题的关键线索:当发现某监测点缺失率异常时,可以直接定位到具体文件。第二,ignore_index=True 避免多个文件各自的行索引在合并后重复,否则后续去重和切片会出现歧义。如果数据是 Excel,把 read_csv 换成 read_excel 即可,但要注意 sheet_name 参数,多 sheet 文件应显式指定或遍历。
二、数据质量快照的四个维度
读取完成后,围绕形状、类型、缺失、重复四个维度建立快照。下面的图示概括了从原始文件到质量快照的判断路径,以及每个维度对应的典型问题与处理方向。
形状检查最直接:raw.shape 给出总行数与列数,再与各文件行数之和比对。如果总行数小于各文件之和,说明合并时发生了列对齐问题或索引冲突;如果列数多于预期,通常是某个文件多出辅助列。类型检查用 raw.dtypes,重点看时间列是否为 object、数值列是否被读成字符串。缺失检查用 raw.isna().mean() 得到各列缺失率,再按 source_file 分组看各监测点的缺失分布。重复检查分两层:raw.duplicated().sum() 统计完全重复行,raw.duplicated(subset=["source_file","timestamp"]).sum() 统计键重复。
三、把快照落成可复查的表
零散地打印几个数字不利于复查。建议把四个维度的结果整理成一张按监测点汇总的质量表,示例结构如下(数值为教学假设,非实测):
| 监测点 | 行数 | 时间列类型 | pm25 缺失率 | temperature 缺失率 | 完全重复行 | 键重复 |
|---|---|---|---|---|---|---|
| site_A | 1440 | object | 0.8% | 0.0% | 0 | 0 |
| site_B | 1432 | object | 3.5% | 1.2% | 6 | 8 |
| site_C | 1440 | object | 0.2% | 0.0% | 0 | 0 |
这张表立刻暴露出两个问题:所有监测点的时间列都是 object,需要统一转换;site_B 行数偏少且存在重复,说明该文件可能被重复导出过,同时有部分记录缺失。此时不要急着 drop_duplicates,而应先确认重复是“完全重复”还是“同键不同值”——后者可能是设备重传导致数值被覆盖,处理方式完全不同。
四、类型转换与缺失的初步处理
时间列转换是读取阶段最容易被忽略的一步。直接 pd.to_datetime(raw["timestamp"]) 在格式混杂时会报错,稳妥做法是加 errors="coerce",把无法解析的值转为 NaT,再统计转换失败的数量:
raw["timestamp"] = pd.to_datetime(raw["timestamp"], errors="coerce")
bad_time = raw["timestamp"].isna().sum()
print("时间解析失败:", bad_time)
num_cols = ["pm25", "temperature", "humidity"]
for c in num_cols:
raw[c] = pd.to_numeric(raw[c], errors="coerce")
errors="coerce" 的价值在于把“隐性错误”变成“显性缺失”:原本混在数值列里的 "--"、"N/A"、带单位的字符串会变成 NaN,从而在缺失率统计中被看见。转换后应重新计算一次缺失率,与转换前对比,差值就是“格式问题导致的伪缺失”。这一步做完,数据质量快照才算完整。
quality_snapshot.csv,并在文件名或表内记录生成时间与数据版本。多监测点项目往往反复导入数据,有了历史快照,才能判断某次异常是数据本身变化还是读取方式变化。完成上述检查后,你手里应该有一份包含行数、列类型、各列缺失率、重复计数的按监测点汇总表,以及转换失败与伪缺失的记录。这份快照是后续分组统计的起点:缺失率高的监测点在对比时应标注置信度,存在键重复的监测点需要先确定去重规则,时间列未成功转换的记录不能进入按小时或按天的聚合。把这些判断依据固定下来,多监测点指标对比才不会在源头失真。