2. Pandas数据结构与农业监测数据加载
农业监测数据清洗的第一步,不是急着删空值或改格式,而是先把数据“装进”合适的容器里,并弄清楚它长什么样。Pandas 提供了两种核心数据结构:Series 与 DataFrame。Series 是带索引的一维数组,适合表示单个监测指标的时间序列,例如某块试验田逐日的土壤含水率;DataFrame 是带行索引和列标签的二维表格,适合承载多指标、多站点的监测记录,例如同一张表里同时存放空气温度、相对湿度、光照强度和土壤 pH。理解二者的关系,是后续所有清洗操作的基础。
Series 与 DataFrame 的对应关系
可以把 DataFrame 看作“共享同一行索引的多个 Series 的集合”。取 DataFrame 的一列,得到的就是一个 Series;取一行,得到的也是一个 Series(索引变为列名)。这种结构决定了清洗时的操作粒度:按列处理指标异常值,用 Series 方法;按行做记录级校验,则面向 DataFrame 或逐行 Series。下面这张图展示了农业监测数据从原始文件到 DataFrame、再拆分为列 Series 的结构关系,以及加载阶段需要同步完成的类型检查位置。
从文件与数据库读取监测数据
农业监测数据最常见的三种来源是 CSV、Excel 和数据库表。CSV 通常由田间记录仪或气象站导出,读取时用 pd.read_csv();Excel 常见于多站点分表汇总,用 pd.read_excel(),并通过 sheet_name 指定工作表;数据库则通过 SQLAlchemy 连接后使用 pd.read_sql()。读取阶段就要把关键参数定好,否则后续清洗会反复返工。
import pandas as pd
from sqlalchemy import create_engine
# CSV:指定日期列解析、缺失值标记
df_csv = pd.read_csv(
"field_station_2024.csv",
parse_dates=["record_date"],
na_values=["", "NA", "-999", "缺测"],
encoding="utf-8"
)
# Excel:读取指定工作表,跳过前两行说明
df_xlsx = pd.read_excel(
"multi_station.xlsx",
sheet_name="Station_A",
skiprows=2
)
# 数据库:只取需要的列与时间范围
engine = create_engine("mysql+pymysql://user:pwd@host:3306/agri")
df_db = pd.read_sql(
"SELECT station_id, record_date, air_temp, humidity "
"FROM sensor_log WHERE record_date >= '2024-01-01'",
engine
)
这里有两个容易被忽略的细节。第一,na_values 必须按实际数据填写:很多农业设备用 -999 表示传感器故障,用“缺测”表示人工未记录,如果不声明,它们会被当成正常数值参与均值计算,直接污染统计结果。第二,parse_dates 让日期列在加载时就转为 datetime64,否则后续按日聚合、按时间排序都会出错。数据库读取时应在 SQL 里完成列筛选和时间过滤,避免把整张历史表拉进内存。
数据类型初步检查
数据读进来后,第一件事是看 df.dtypes。农业监测数据常见的类型问题有三类:数值列被读成 object(例如温度列里混入了“25.3℃”这样的单位字符)、日期列被读成字符串、站点编号被读成整数而丢失前导零。判断依据很直接:温度、湿度、光照、pH 应为浮点型;记录日期应为 datetime64[ns];站点编号、作物品种应为字符串或分类类型。
除了 dtypes,还应同时看 df.shape、df.head() 和 df.isna().sum()。形状用于核对记录条数是否与预期一致(例如 3 个站点 × 365 天应接近 1095 行);head() 用于发现表头错位、首行是说明文字等问题;缺失统计则帮助判断哪些指标需要重点处理。下面给出一个可直接套用的加载检查清单,字段按农业监测场景设计。
| 检查项 | 调用方式 | 正常表现(示例) | 异常信号与处理方向 |
|---|---|---|---|
| 记录形状 | df.shape |
(1095, 6) | 行数远少于站点×天数,检查是否漏读分表 |
| 列名完整性 | df.columns |
含 station_id、record_date、air_temp 等 | 出现 Unnamed: 0,说明首列是行号,需 index_col=0 |
| 数值列类型 | df.dtypes |
air_temp、humidity 为 float64 | 为 object 时检查单位字符与全角符号 |
| 日期列类型 | df.dtypes |
record_date 为 datetime64[ns] | 为 object 时补 parse_dates 或 pd.to_datetime |
| 缺失分布 | df.isna().sum() |
各列缺失比例可解释 | 某列大面积为空,核对 na_values 是否误判 |
| 首尾样本 | df.head() / df.tail() |
首行为真实监测记录 | 首行是说明文字,需 skiprows |
parse_dates、na_values、dtype 三个参数写清楚,比读进来再反复转换更省事,也能避免“缺测值被当成真实低温”这类隐蔽错误。完成上述检查后,数据才算真正“可用”。此时你应当能回答三个问题:这张表有多少行多少列、每个监测指标是什么类型、哪些列存在缺失或类型异常。带着这三个答案进入后续的缺失值处理、异常值识别与单位统一,清洗工作才有明确的起点,而不是盲目地对整张表做通用处理。