用Python+SPSS搞定数学建模数据清洗:以2023深圳杯A题居民健康数据为例
·
Python+SPSS数学建模数据清洗实战:从问卷数据到分析建模的完整指南
数学建模竞赛中,数据清洗往往是最耗时却最容易被忽视的环节。面对一份包含数百个字段、数千条记录的复杂问卷数据,如何高效地将其转化为可供建模的"干净"数据集?本文将以2023年深圳杯A题居民健康数据为例,手把手教你使用Python和SPSS构建一套可复用的数据清洗流程。
1. 数据清洗前的准备工作
拿到原始数据的第一件事不是立即开始清洗,而是需要做好充分的准备工作。对于深圳杯A题附件2这样的复杂问卷数据(包含200+字段),我们需要先理解数据结构,制定清洗策略。
1.1 数据理解与探索
import pandas as pd
import numpy as np
# 读取原始数据
raw_data = pd.read_excel('慢性病及相关因素流调数据.xlsx', header=None)
# 查看数据概览
print(f"数据集形状: {raw_data.shape}")
print("前5行数据:")
print(raw_data.head(5))
典型输出结果:
数据集形状: (1500, 230)
前5行数据:
0 1 2 3 4 5 ... 225 226 227 228 229
0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN
1 ID Q1 Q2 Q3 Q4 Q5 ... Q228 Q229 Q230 Q231 Q232
2 1 2 1 3 1 2 ... 0 1 0 1 0
3 2 1 2 1 2 1 ... 1 0 1 0 1
4 3 2 1 3 2 1 ... 0 1 0 1 0
关键观察点 :
- 数据包含1500行、230列
- 前两行可能是标题或说明信息
- 存在NaN值需要处理
- 第一列似乎是ID号
1.2 制定清洗路线图
针对此类问卷数据,建议按照以下顺序进行清洗:
- 元数据处理 :提取或构建有效的数据字典
- 结构清洗 :处理表头、删除空行、添加ID等
- 缺失值处理 :识别、分析和填补缺失数据
- 异常值检测 :查找并处理不合理数值
- 数据转换 :创建衍生变量、标准化编码等
- 数据导出 :保存为适合建模的格式
2. 结构化清洗:从原始数据到规整表格
2.1 构建规范的数据框架
# 重新读取数据,正确处理表头
data = pd.read_excel('慢性病及相关因素流调数据.xlsx', header=1)
# 删除全空的行
data = data.dropna(how='all')
# 检查并修复ID列
if 'ID' not in data.columns:
data.insert(0, 'ID', range(1, len(data)+1))
else:
data['ID'] = data['ID'].fillna(pd.Series(range(1, len(data)+1)))
print(f"清洗后数据形状: {data.shape}")
print("ID列示例:")
print(data['ID'].head())
2.2 问卷数据的特殊处理
问卷数据往往需要额外的清洗步骤:
# 处理多选题的拆分(示例)
mchoice_cols = ['Q15', 'Q27', 'Q48'] # 假设这些是多选题
for col in mchoice_cols:
if col in data.columns:
# 拆分多选题选项
expanded = data[col].str.get_dummies(sep=',')
expanded.columns = [f"{col}_{c}" for c in expanded.columns]
data = pd.concat([data, expanded], axis=1)
data.drop(col, axis=1, inplace=True)
# 处理文本型分类变量
text_cols = data.select_dtypes(include=['object']).columns
for col in text_cols:
data[col] = data[col].str.strip() # 去除前后空格
data[col] = data[col].replace({'': np.nan}) # 空字符串转为NaN
3. 缺失值处理:策略与实战
3.1 系统性缺失模式分析
# 计算各列缺失率
missing_rate = data.isnull().mean().sort_values(ascending=False)
# 可视化缺失模式
import matplotlib.pyplot as plt
plt.figure(figsize=(12,6))
missing_rate[missing_rate > 0].plot(kind='bar')
plt.title('各变量缺失率分布')
plt.ylabel('缺失比例')
plt.show()
常见缺失模式处理策略 :
| 缺失类型 | 判断标准 | 处理方式 | SPSS实现方法 |
|---|---|---|---|
| 完全随机缺失 | 缺失与任何变量无关 | 直接删除或简单填补 | 删除或均值/中位数填补 |
| 随机缺失 | 缺失与观测变量相关 | 多重填补或模型预测 | MULTIPLE IMPUTATION |
| 非随机缺失 | 缺失与未观测因素相关 | 创建缺失指标变量 | 创建新变量标记缺失 |
3.2 Python与SPSS协同处理缺失值
Python端处理 :
# 简单填补示例
data.fillna({
'年龄': data['年龄'].median(),
'性别': data['性别'].mode()[0],
'收入': -1 # 特殊编码表示缺失
}, inplace=True)
# 复杂缺失使用插值
data['体重'] = data.groupby(['性别','年龄'])['体重'].apply(
lambda x: x.fillna(x.mean()))
SPSS端处理 :
* 多重填补示例.
MULTIPLE IMPUTATION
/IMPUTE METHOD=FCS
/IMPUTE VARIABLES=收入 体重 饮食习惯
/IMPUTE MAXITER=100
/IMPUTE NBITER=10
/IMPUTE SEED=12345.
4. 异常值与数据一致性检查
4.1 基于统计的异常值检测
# 数值型变量异常值检测
num_cols = data.select_dtypes(include=['number']).columns
for col in num_cols:
q1 = data[col].quantile(0.25)
q3 = data[col].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5*iqr
upper_bound = q3 + 1.5*iqr
outliers = data[(data[col] < lower_bound) | (data[col] > upper_bound)]
if not outliers.empty:
print(f"{col}发现{len(outliers)}个异常值,范围[{outliers[col].min()}, {outliers[col].max()}]")
4.2 逻辑一致性检查
问卷数据常常需要检查回答的逻辑一致性:
# 示例:吸烟者应该有吸烟量数据
mask = (data['是否吸烟'] == 1) & (data['每日吸烟量'].isna())
print(f"发现{len(data[mask])}条吸烟但无吸烟量记录")
# 示例:BMI计算验证
data['计算BMI'] = data['体重'] / (data['身高']/100)**2
inconsistent_bmi = data[abs(data['BMI'] - data['计算BMI']) > 0.5]
print(f"发现{len(inconsistent_bmi)}条BMI记录不一致")
5. 数据转换与特征工程
5.1 分类变量编码
# 有序分类变量映射
education_map = {
'小学及以下': 1,
'初中': 2,
'高中/中专': 3,
'大专': 4,
'本科': 5,
'研究生及以上': 6
}
data['教育程度编码'] = data['教育程度'].map(education_map)
# 无序分类变量哑变量化
data = pd.get_dummies(data, columns=['职业类型', '居住地区'], prefix_sep=':')
5.2 创建衍生变量
# 健康行为评分
data['健康行为分'] = (
data['每日蔬菜摄入量'].clip(0, 500)/100 +
data['每周运动次数']*0.5 +
(data['吸烟'] == 0)*1 +
(data['饮酒'] <= 2)*1
)
# 时段特征提取
data['调查时段'] = pd.to_datetime(data['调查时间']).dt.hour
data['是否晚间'] = (data['调查时段'] >= 18).astype(int)
6. SPSS数据验证与高级处理
6.1 数据质量验证语法
* 检查变量范围.
VALIDATE DATA
/VARIABLES=年龄 收入 BMI
/RANGES LOWEST=0 HIGHEST=100 FOR 年龄
/RANGES LOWEST=0 HIGHEST=50000 FOR 收入
/RANGES LOWEST=12 HIGHEST=40 FOR BMI.
* 识别重复记录.
MATCH FILES
/FILE=*
/BY ID
/FIRST=first
/LAST=last.
SELECT IF first.
EXECUTE.
6.2 变量聚类与降维
* 变量聚类减少维度.
VARIABLE CLUSTER
/VARIABLES=Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 Q10
/MEASURE=EUCLID
/METHOD=BINARY
/CLUSTER=WARD
/PRINT SCHEDULE
/PLOT DENDROGRAM.
7. 数据导出与建模准备
7.1 导出清洗后数据
# 保存为CSV
data.to_csv('cleaned_health_data.csv', index=False, encoding='utf-8-sig')
# 保存为SPSS格式
import pyreadstat
pyreadstat.write_sav(data, 'cleaned_health_data.sav')
# 保存为Excel
data.to_excel('cleaned_health_data.xlsx', index=False)
7.2 建模前的最后检查清单
-
变量类型检查 :
- 分类变量是否正确编码
- 连续变量是否已标准化
-
缺失值确认 :
- 是否所有缺失已处理
- 缺失标记是否一致
-
数据分布验证 :
- 异常值是否已处理
- 偏态分布是否已转换
-
数据字典更新 :
- 所有新变量是否已记录
- 变量含义是否清晰
# 最终数据质量报告
report = pd.DataFrame({
'变量名': data.columns,
'类型': data.dtypes,
'缺失数': data.isnull().sum(),
'唯一值数': data.nunique()
})
print(report)
在实际数学建模竞赛中,一套完整、高效的数据清洗流程可以节省大量时间。本文介绍的方法不仅适用于深圳杯A题,也可迁移到其他问卷类数据分析任务中。关键在于建立系统化的清洗思维,而不是零散地解决问题。
更多推荐
所有评论(0)