一、核心数据结构
1.1 DataFrame 的构建方式
使用 Pandas 进行耕地质量数据分析时,DataFrame 是最常用的数据结构。以下是多种创建方式:
从字典初始化(适用于小规模示例或测试)
import pandas as pd
import numpy as np
from datetime import datetime
cultivated_data = {
'标识码': ['1101010101010000000001', '1101010101010000000002', '1101010101010000000003'],
'要素代码': ['010101', '010102', '020101'],
'县代码': ['110101', '110101', '110101'],
'县名称': ['东城区', '东城区', '东城区'],
'面积_m2': [12500.5, 23456.7, 18900.3],
'数据年份': [2023, 2023, 2023],
'耕地质量等级': [1, 2, 3],
'土壤pH': [6.8, 7.2, 5.5],
'有机质_gkg': [25.3, 18.7, 22.1],
'监测时间': ['2023-05-15', '2023-05-16', '2023-05-17']
}
df = pd.DataFrame(cultivated_data)
print("耕地质量数据示例:")
print(df.head())
print(f"\n数据形状: {df.shape}") # (3, 10)
print(f"数据类型:\n{df.dtypes}")
[此处为图片1]
读取 CSV 文件(常见于实际项目中的数据源)
df_csv = pd.read_csv('cultivated_land_data.csv',
encoding='gbk', # 可选 utf-8
dtype={'标识码': str, '要素代码': str, '县代码': str},
parse_dates=['监测时间'],
na_values=['NULL', 'NA', '无'])
加载 Excel 表格(支持多工作表读取)
df_excel = pd.read_excel('耕地质量数据.xlsx',
sheet_name='耕地质量等级',
dtype={'标识码': str})
从数据库查询获取(如 SQLite 示例)
import sqlite3
conn = sqlite3.connect('cultivated_land.db')
df_db = pd.read_sql_query("SELECT * FROM farmland_quality", conn)
1.2 Series 的生成方法
Pandas 中的 Series 常用于表示单一字段序列,可用于分析单个变量。
手动构造一个 pH 值序列
ph_series = pd.Series([6.8, 7.2, 5.5, 6.0, 7.5],
name='土壤pH',
index=['点位1', '点位2', '点位3', '点位4', '点位5'])
从已有 DataFrame 提取列作为 Series
area_series = df['面积_m2']
[此处为图片2]
二、数据查看与基础信息分析
2.1 数据浏览与概览
在进行深入处理前,需对数据整体情况进行快速了解。
查看前几行和后几行数据
print(df.head(5)) # 显示前5条记录
print(df.tail(3)) # 显示最后3条记录
随机抽样观察部分样本
print(df.sample(5)) # 随机抽取5行
输出字段类型与非空统计
print(df.info())
数值型字段的基本统计描述
print(df.describe())
针对耕地质量的专项分布统计
print("\n耕地质量等级分布:")
print(df['耕地质量等级'].value_counts().sort_index())
print("\n各县耕地质量统计:")
print(df.groupby('县名称')['耕地质量等级'].agg(['mean', 'min', 'max', 'count']))
检查内存占用情况
print(f"内存使用: {df.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
[此处为图片3]
2.2 数据筛选操作
根据研究需求,可对数据集进行条件过滤以提取目标子集。
基于单一条件的筛选
grade_1_land = df[df['耕地质量等级'] == 1] # 等级为一等的耕地
high_ph_land = df[df['土壤pH'] > 7.0] # 土壤偏碱性地块
large_area_land = df[df['面积_m2'] > 20000] # 面积超过2万平方米的地块
组合多个条件进行筛选(逻辑与)
good_quality_land = df[(df['耕地质量等级'] <= 2) & (df['有机质_gkg'] >= 20)]
# 筛选土壤pH值异常的地块(强酸性或强碱性)
problem_land = df[(df['土壤pH'] < 5.5) | (df['土壤pH'] > 8.5)]
# 根据字符串匹配筛选北京地区的数据
bj_land = df[df['县名称'].str.contains('北京')]
# 筛选标识码以'11'开头的记录(代表北京区域)
start_with_11 = df[df['标识码'].str.startswith('11')]
# 利用query方法实现复合条件筛选,语法更清晰
medium_land = df.query('耕地质量等级 == 3 and 面积_m2 > 15000')
# 使用isin进行多个指定值的筛选
target_counties = df[df['县名称'].isin(['东城区', '西城区', '朝阳区'])]
target_grades = df[df['耕地质量等级'].isin([1, 2])]
# 基于位置索引提取特定行数据
first_10 = df.iloc[:10] # 取前10行
rows_5_to_15 = df.iloc[5:16] # 行索引从5到15
specific_rows = df.iloc[[0, 3, 7]] # 提取第0、3、7行
[此处为图片1]
三、数据操作与转换
3.1 列的操作处理
# 新增计算列:将面积单位由平方米转换为亩
df['面积_亩'] = df['面积_m2'] / 666.67
# 对土壤pH值进行区间分类,生成新的类别标签
df['pH类别'] = pd.cut(df['土壤pH'],
bins=[0, 5.5, 6.5, 7.5, 8.5, 14],
labels=['强酸性', '酸性', '中性', '碱性', '强碱性'])
# 添加固定来源信息字段
df['数据来源'] = '第三次土壤普查'
# 使用apply函数结合自定义逻辑创建有机质等级
def classify_organic_matter(value):
if value >= 30:
return '丰富'
elif value >= 20:
return '中等'
elif value >= 10:
return '缺乏'
else:
return '极缺'
df['有机质等级'] = df['有机质_gkg'].apply(classify_organic_matter)
# 利用lambda表达式判断是否为优质耕地
df['是否优质'] = df.apply(lambda row: '是' if row['耕地质量等级'] <= 2 and row['有机质_gkg'] >= 20 else '否', axis=1)
# 删除不需要的临时或测试字段
df = df.drop(columns=['临时字段', '测试字段'])
# 对列名进行规范化重命名
df = df.rename(columns={
'面积_m2': '面积_平方米',
'有机质_gkg': '有机质_g_kg'
})
# 调整列的显示顺序,统一结构化输出
cols_order = ['标识码', '要素代码', '县名称', '面积_平方米', '耕地质量等级', '土壤pH', '有机质_g_kg', '监测时间']
df = df[cols_order]
[此处为图片2]
3.2 行的操作处理
# 构造新数据行并追加至原数据集
new_row = pd.DataFrame({
'标识码': ['1101010101010000000004'],
'要素代码': ['020102'],
'县名称': ['东城区'],
'面积_m2': [21000.0],
'耕地质量等级': [2],
'土壤pH': [6.9],
'有机质_gkg': [24.5]
})
df = pd.concat([df, new_row], ignore_index=True)
# 删除指定索引位置的行
df = df.drop(index=[0, 5])
# 按条件过滤掉面积过小的地块记录
df = df[df['面积_m2'] > 1000]
# 清除重复数据行
# 按唯一标识码去重,保留首次出现的记录
df = df.drop_duplicates(subset=['标识码'], keep='first')
# 按县和监测时间组合去重,保留最后一次更新的数据
df = df.drop_duplicates(subset=['县名称', '监测时间'], keep='last')
3.3 数据排序操作
# 单一字段排序:按面积从大到小排列
df_sorted_by_area = df.sort_values('面积_m2', ascending=False)
# 多字段组合排序:优先按耕地质量等级升序,再按有机质含量降序
df_sorted = df.sort_values(['耕地质量等级', '有机质_gkg'],
ascending=[True, False])
# 按照索引顺序进行逆序排列
df_sorted_index = df.sort_index(ascending=False)
[此处为图片3]
四、数据分组与聚合分析
4.1 基础分组与统计聚合
# 按“县名称”进行分组,并对相关指标进行聚合计算
county_stats = df.groupby('县名称').agg({
'面积_平方米': ['count', 'sum', 'mean'], # 统计数量、总面积、平均面积
'耕地质量等级': 'mean', # 平均质量等级
'有机质_g_kg': 'mean', # 平均有机质含量
'土壤pH': ['min', 'max', 'mean'] # pH值的范围及均值
})
4.2 分组操作进阶
在数据分组的基础上,可以进一步实现更复杂的操作,如自定义函数应用、分组迭代以及条件筛选等。
以下是一个用于分析耕地质量等级变化趋势的自定义函数。该函数对每个县的数据按监测时间排序,并计算首个与最后一个记录之间的质量等级差值,以反映变化趋势:
def quality_trend(group):
"""计算质量等级变化趋势"""
if len(group) > 1:
group = group.sort_values('监测时间')
first_grade = group['耕地质量等级'].iloc[0]
last_grade = group['耕地质量等级'].iloc[-1]
return last_grade - first_grade
return 0
trend_by_county = df.groupby('县名称').apply(quality_trend)
此外,还可以通过分组迭代的方式逐个查看各县的数据统计信息,例如地块数量、总面积和平均质量等级:
for county_name, county_data in df.groupby('县名称'):
print(f"\n{county_name}的耕地数据:")
print(f" 地块数量: {len(county_data)}")
print(f" 总面积: {county_data['面积_m2'].sum():,.0f} 平方米")
print(f" 平均质量等级: {county_data['耕地质量等级'].mean():.2f}")
针对特定需求,也可对分组后的数据进行筛选。例如,保留每个县中面积超过该县平均面积的耕地记录:
def filter_large_plots(group):
"""筛选每个县中面积大于平均值的耕地"""
avg_area = group['面积_m2'].mean()
return group[group['面积_m2'] > avg_area]
large_plots_by_county = df.groupby('县名称').apply(filter_large_plots)
五、数据清洗与预处理
5.1 处理缺失值
在实际数据分析过程中,缺失值是常见问题之一。首先应对数据集中各字段的缺失情况进行统计分析:
missing_values = df.isnull().sum()
missing_percentage = (df.isnull().sum() / len(df)) * 100
print("缺失值统计:")
print(pd.DataFrame({
'缺失数量': missing_values,
'缺失比例%': missing_percentage.round(2)
}))
根据业务逻辑和缺失程度,可选择不同的处理方式:
- 删除含有缺失值的行(可指定特定列):
df_no_missing = df.dropna() # 删除任何列有缺失的行
df_no_missing_col = df.dropna(subset=['土壤pH', '有机质_gkg']) # 仅删除指定列缺失的行
- 使用填充策略补全缺失值:
df_filled = df.copy()
df_filled['土壤pH'] = df_filled['土壤pH'].fillna(df_filled['土壤pH'].median()) # 中位数填充
df_filled['有机质_gkg'] = df_filled['有机质_gkg'].fillna(
df_filled.groupby('耕地质量等级')['有机质_gkg'].transform('mean')) # 按质量等级均值填充
df_filled['县名称'] = df_filled['县名称'].fillna('未知') # 固定值填充
- 对于具有时间序列特性的数据,可采用前后向填充方法:
df_filled['耕地质量等级'] = df_filled['耕地质量等级'].ffill() # 向前填充
5.2 处理异常值
[此处为图片1]
异常值可能会影响模型训练或统计结果的准确性。常见的检测方法包括基于标准差、IQR(四分位距)或可视化手段(如箱线图)。处理方式通常包括截断、替换或直接剔除。
后续可通过设定阈值来识别并处理超出合理范围的数值,例如将超出3倍标准差的数据视为异常,并用上下限进行修正,或结合业务规则判断是否应排除。
聚合与透视分析
对县级单位进行汇总统计时,常需计算总面积、平均面积、地块数量、土壤指标等多项指标。以下是对关键字段进行多维度聚合的操作示例:
county_stats = df.groupby('县名称').agg({
'面积_m2': ['sum', 'mean', 'count'],
'耕地质量等级': 'mean',
'土壤pH': ['mean', 'std'],
'有机质_gkg': ['min', 'max', 'mean']
})
为提升可读性,对生成的多层列名进行重命名处理:
county_stats.columns = ['总面积', '平均面积', '地块数量',
'平均质量等级', '平均pH', 'pH标准差',
'最小有机质', '最大有机质', '平均有机质']
print("各县耕地统计:")
print(county_stats)
为进一步深入分析不同质量等级下的分布情况,可采用多级分组方式,按“县+质量等级”组合进行聚合:
multi_group = df.groupby(['县名称', '耕地质量等级']).agg({
'面积_m2': 'sum',
'有机质_gkg': 'mean'
}).round(2)
此外,利用透视表功能可快速生成类似Excel中的交叉汇总报表,支持添加总计行/列:
pivot_table = pd.pivot_table(df,
values=['面积_m2', '有机质_gkg'],
index='县名称',
columns='耕地质量等级',
aggfunc=['sum', 'mean'],
fill_value=0,
margins=True, # 添加总计
margins_name='总计')
# 使用IQR方法识别异常值
def detect_outliers_iqr(data, column):
"""基于四分位距(IQR)检测指定列的异常值"""
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return data[(data[column] < lower_bound) | (data[column] > upper_bound)]
ph_outliers = detect_outliers_iqr(df, '土壤pH')
print(f"pH异常值数量: {len(ph_outliers)}")
# 异常值处理策略:截断法
df_clean = df.copy()
df_clean['土壤pH'] = df_clean['土壤pH'].clip(
lower=df_clean['土壤pH'].quantile(0.01),
upper=df_clean['土壤pH'].quantile(0.99)
)
# 替换异常值为中位数
def replace_outliers_with_median(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
median_val = data[column].median()
mask = (data[column] < Q1 - 1.5 * IQR) | (data[column] > Q3 + 1.5 * IQR)
data.loc[mask, column] = median_val
return data
df_clean = replace_outliers_with_median(df_clean, '有机质_gkg')
5.3 数据转换操作
# 转换数据类型以满足分析需求
df['标识码'] = df['标识码'].astype(str)
df['数据年份'] = pd.to_datetime(df['数据年份'], format='%Y')
df['监测时间'] = pd.to_datetime(df['监测时间'])
# 对分类变量进行编码处理
df['县名称_编码'] = pd.factorize(df['县名称'])[0]
df['质量等级_类别'] = df['耕地质量等级'].astype('category')
# 连续型变量分箱(离散化处理)
df['面积等级'] = pd.qcut(df['面积_m2'], q=5, labels=['很小', '较小', '中等', '较大', '很大'])
df['pH等级'] = pd.cut(df['土壤pH'],
bins=[0, 5.5, 6.5, 7.5, 14],
labels=['酸性', '弱酸性', '中性', '碱性'])
# 标准化与归一化处理
from sklearn.preprocessing import StandardScaler, MinMaxScaler
scaler = StandardScaler()
df_scaled = df.copy()
df_scaled[['面积_m2', '土壤pH', '有机质_gkg']] = scaler.fit_transform(
df[['面积_m2', '土壤pH', '有机质_gkg']]
)
# 对偏态分布数据进行对数变换
df['面积_log'] = np.log1p(df['面积_m2']) # 即 log(1 + x),避免log(0)
# 执行One-Hot编码扩展分类特征
df_encoded = pd.get_dummies(df, columns=['县名称', '耕地质量等级'], prefix=['县', '等级'])
六、数据合并与连接
6.1 数据合并实现
# 构造示例数据集用于演示合并过程
df_basic = pd.DataFrame({
'标识码': ['1101010101010000000001', '1101010101010000000002', '1101010101010000000003'],
'要素代码': ['010101', '010102', '020101'],
'县名称': ['东城区', '东城区', '东城区'],
'面积_m2': [12500.5, 23456.7, 18900.3]
})
df_quality = pd.DataFrame({
以下是关于耕地质量与土壤属性数据的处理方法,涵盖多表连接、数据拼接以及时间序列分析等关键技术操作。
一、多表合并操作(Merge)
在实际数据分析中,常需将多个数据表依据公共字段进行关联。此处以“标识码”作为关键字段,实现不同数据集之间的整合。
首先构建基础信息表 df_basic 与耕地质量表 df_quality:
df_basic = pd.DataFrame({
'标识码': ['1101010101010000000001', '1101010101010000000002', '1101010101010000000003'],
'要素代码': ['020101', '020101', '020102'],
'县名称': ['东城区', '东城区', '西城区'],
'面积_m2': [20000.0, 18500.0, 22300.0]
})
df_quality = pd.DataFrame({
'标识码': ['1101010101010000000001', '1101010101010000000002', '1101010101010000000003'],
'耕地质量等级': [1, 2, 3],
'土壤pH': [6.8, 7.2, 5.5],
'有机质_gkg': [25.3, 18.7, 22.1]
})
接着定义另一个包含养分指标的数据表 df_soil:
df_soil = pd.DataFrame({
'标识码': ['1101010101010000000001', '1101010101010000000003', '1101010101010000000004'],
'全氮_gkg': [1.5, 1.2, 1.8],
'有效磷_mgkg': [25.1, 18.3, 30.5],
'速效钾_mgkg': [150, 120, 180]
})
使用 merge 方法执行各类连接方式:
- 内连接(inner):仅保留两表共有的标识码记录。
df_inner = pd.merge(df_basic, df_quality, on='标识码', how='inner')
df_left = pd.merge(df_basic, df_quality, on='标识码', how='left')
df_outer = pd.merge(df_basic, df_quality, on='标识码', how='outer')
df_all = pd.merge(pd.merge(df_basic, df_quality, on='标识码'), df_soil, on='标识码', how='left')
二、数据拼接操作(Concat)
concat 方法适用于沿指定轴方向对多个 DataFrame 进行拼接。
创建新增地块数据用于垂直扩展原始数据集:
df_new_plots = pd.DataFrame({
'标识码': ['1101010101010000000005', '1101010101010000000006'],
'要素代码': ['020102', '020103'],
'县名称': ['东城区', '东城区'],
'面积_m2': [21000.0, 19800.0]
})
- 垂直拼接(添加行):将新地块信息追加到底部,并重置索引。
df_vertical = pd.concat([df_basic, df_new_plots], ignore_index=True)
df_horizontal = pd.concat([df_basic.set_index('标识码'), df_quality.set_index('标识码')], axis=1).reset_index()
三、基于索引的连接(Join)
通过设置索引后使用 join 方法,可实现更高效的表格关联。
df_basic_indexed = df_basic.set_index('标识码')
df_quality_indexed = df_quality.set_index('标识码')
df_joined = df_basic_indexed.join(df_quality_indexed, how='left')
四、时间序列数据处理(适用于监测类数据)
针对长期观测点的时间序列数据,需进行系统性的时间维度操作。
构造模拟的监测数据集:
df_time = pd.DataFrame({
'监测点': ['点位1']*12 + ['点位2']*12,
'监测时间': pd.date_range('2023-01-01', periods=12, freq='M').tolist() * 2,
'土壤pH': np.random.uniform(6.0, 7.5, 24),
'有机质_gkg': np.random.uniform(15, 30, 24)
})
将时间字段设为索引以便后续操作:
df_time_indexed = df_time.set_index('监测时间')
- 时间重采样 - 按月统计:计算每月各指标的聚合值。
monthly_stats = df_time_indexed.resample('M').agg({
'土壤pH': 'mean',
'有机质_gkg': ['mean', 'min', 'max']
})
quarterly_stats = df_time_indexed.resample('Q').mean()
df_time['pH_3月平均'] = df_time.groupby('监测点')['土壤pH'].transform(
lambda x: x.rolling(window=3, min_periods=1).mean()
)
计算相邻时间点间的绝对变化及有机质含量的变化率。
df_time['pH变化'] = df_time.groupby('监测点')['土壤pH'].diff()
df_time['有机质变化率'] = df_time.groupby('监测点')['有机质_gkg'].pct_change() * 100
[此处为图片1]
从时间数据中提取关键的时间特征,可以方便后续的分析与建模:
df_time['年份'] = df_time['监测时间'].dt.year
df_time['月份'] = df_time['监测时间'].dt.month
df_time['季度'] = df_time['监测时间'].dt.quarter
df_time['周几'] = df_time['监测时间'].dt.day_name()
8.1 向量化操作提升计算效率
在处理大规模农业或土地数据时,使用Pandas和NumPy提供的向量化操作能够显著提高性能。相比逐行调用函数的传统方式,向量化方法直接作用于整个Series或DataFrame列,避免了Python循环的开销。
例如,传统方式可能如下定义一个函数并应用到每一行:
# 传统方法(慢)
def calculate_productivity(row):
"""计算耕地生产力指数(示例)"""
return row['有机质_gkg'] * 0.3 + (8 - abs(row['土壤pH'] - 6.5)) * 0.2 + (5 - row['耕地质量等级']) * 0.5
而采用向量化写法后,代码不仅更简洁,执行速度也大幅提升:
# 向量化方法(快)
df['生产力指数'] = df['有机质_gkg'] * 0.3 + (8 - abs(df['土壤pH'] - 6.5)) * 0.2 + (5 - df['耕地质量等级']) * 0.5
# 利用numpy进行高效数学变换
df['对数面积'] = np.log1p(df['面积_m2'])
df['标准化pH'] = (df['土壤pH'] - df['土壤pH'].mean()) / df['土壤pH'].std()
8.2 大数据集的分块处理策略
当面对超出内存容量的大型CSV文件时,可通过分块读取的方式逐步处理数据,最后合并结果:
chunk_size = 10000
chunks = []
for chunk in pd.read_csv('large_cultivated_data.csv',
chunksize=chunk_size,
encoding='gbk'):
# 对每一块数据进行过滤或其他处理
chunk_processed = chunk[chunk['面积_m2'] > 1000]
chunks.append(chunk_processed)
# 将所有处理后的块合并为完整数据框
df_large = pd.concat(chunks, ignore_index=True)
对于更大规模的数据(如分布在多个文件中的超大数据集),推荐使用Dask库实现并行化处理:
import dask.dataframe as dd
dask_df = dd.read_csv('very_large_data/*.csv',
encoding='gbk',
dtype={'标识码': 'object'})
# 执行分组聚合,并通过compute()触发实际计算
result = dask_df.groupby('县名称').面积_m2.mean().compute()
8.3 自定义显示设置优化输出效果
为了在数据分析过程中获得更好的可视化体验,可以调整Pandas的显示选项,控制输出格式与样式:
# 设置最大显示行数和列数
pd.set_option('display.max_rows', 100)
pd.set_option('display.max_columns', 50)
pd.set_option('display.width', 1000) # 控制控制台输出宽度
pd.set_option('display.float_format', '{:.2f}'.format) # 浮点数保留两位小数
pd.set_option('display.max_colwidth', 50) # 限制列内容显示长度
在Jupyter环境中,还可以进一步对表格进行样式渲染,增强可读性。例如,根据耕地质量等级进行背景色标注:
def highlight_good_quality(val):
"""高亮优质耕地"""
color = 'green' if val <= 2 else ('yellow' if val == 3 else 'red')
return f'background-color: {color}'
# 应用样式到指定列
styled_df = df.style.applymap(highlight_good_quality, subset=['耕地质量等级'])
# 格式化数值列的显示方式
styled_df.format({'面积_m2': '{:,.0f}', '有机质_gkg': '{:.1f}'})


雷达卡


京公网安备 11010802022788号







