成本台账数据清洗与预算占用分摊逻辑的工程实践 - 第3节
3. 数据清洗基础:缺失值检测与处理策略
成本台账数据在采集、录入与系统对接过程中,常因人为疏忽、系统故障或字段非必填等原因产生缺失值。缺失值若不处理,将直接导致预算占用分摊计算出现偏差,甚至引发分摊逻辑中断。本节系统讲解缺失值的检测方法与三类主流处理策略,并结合成本台账场景给出选型建议。
3.1 缺失值检测方法
检测缺失值是数据清洗的第一步。在成本台账中,常见的缺失字段包括:成本中心编码、预算科目、金额、分摊比例、业务日期等。检测方法分为以下两类:
| 检测方法 | 适用场景 | 成本台账典型示例 |
|---|---|---|
| 空值扫描 | 显式缺失(NULL / NaN) | 分摊比例字段为 NULL |
| 占位符识别 | 隐式缺失(特殊值如 -1, 9999, "未知") | 成本中心编码为 "N/A" |
| 逻辑校验 | 业务规则约束缺失 | 金额字段为 0 但业务类型为"费用" |
| 完整性约束 | 主键或外键关联缺失 | 预算占用记录缺少预算科目编码 |
3.2 缺失值处理策略
针对检测到的缺失值,通常采用删除、填充、插值三种策略。选择哪种策略取决于缺失率、字段重要性以及业务上下文。
3.2.1 删除策略
删除策略适用于缺失率低且字段非关键的场景。在成本台账中,若某条记录的关键字段(如金额、成本中心)缺失且无法补全,通常直接删除该记录。
- 行删除:删除包含缺失值的整行记录。适用于缺失率 < 5% 且随机分布的场景。
- 列删除:删除缺失率过高的字段(如 > 70%)。适用于该字段在后续分摊逻辑中不被使用。
# Python 示例:删除金额字段为空的记录
import pandas as pd
df = pd.read_csv('cost_ledger.csv')
# 删除金额字段为空的记录
df_cleaned = df.dropna(subset=['amount'])
print(f"删除前记录数: {len(df)}, 删除后记录数: {len(df_cleaned)}")
3.2.2 填充策略
填充策略适用于缺失率中等(5%~30%)且字段存在业务合理默认值的场景。常用填充方法包括:
| 填充方法 | 说明 | 成本台账应用示例 |
|---|---|---|
| 固定值填充 | 使用业务默认值 | 分摊比例缺失时填充 1.0(表示全额分摊) |
| 均值/中位数填充 | 使用该字段的统计量 | 金额字段缺失时填充同类成本中心的均值 |
| 众数填充 | 使用出现频率最高的值 | 成本中心编码缺失时填充该部门最常用编码 |
| 前向/后向填充 | 使用相邻记录的值 | 时间序列中业务日期缺失时使用前一条记录日期 |
# Python 示例:使用均值填充金额字段
mean_amount = df['amount'].mean()
df['amount'].fillna(mean_amount, inplace=True)
# 使用前向填充处理业务日期
df['business_date'].fillna(method='ffill', inplace=True)
3.2.3 插值策略
插值策略适用于时间序列或有序数据,利用已知数据点之间的数学关系估算缺失值。在成本台账中,常用于月度成本趋势或预算占用序列的补全。
- 线性插值:假设缺失值前后数据呈线性关系,适用于成本平稳变化的场景。
- 多项式插值:适用于成本波动较大的场景,但需注意过拟合风险。
- 时间序列插值:考虑季节性因素,适用于周期性成本数据。
# Python 示例:线性插值处理月度成本序列
df['monthly_cost'] = df['monthly_cost'].interpolate(method='linear')
4. 异常值识别:Z-score 与 IQR 方法
异常值是指明显偏离数据集中趋势的观测值。在成本台账中,异常值可能由录入错误、系统故障或特殊业务事件引起。若不识别并处理,异常值会扭曲预算占用分摊比例,导致分摊结果失真。本节介绍两种最常用的异常值识别方法:Z-score 与 IQR。
4.1 Z-score 方法
Z-score 方法基于正态分布假设,通过计算每个数据点与均值的偏离程度(以标准差为单位)来识别异常值。公式如下:
Z = (X - μ) / σ
其中:
X = 数据点值
μ = 总体均值
σ = 总体标准差
通常,|Z| > 3 的数据点被视为异常值(对应正态分布中 99.7% 置信区间之外)。在成本台账中,Z-score 适用于金额、数量等连续型数值字段。
| Z-score 阈值 | 对应置信区间 | 异常判定 | 成本台账示例 |
|---|---|---|---|
| |Z| > 2 | 95% | 潜在异常 | 单笔金额超过均值 2 倍标准差 |
| |Z| > 3 | 99.7% | 强烈异常 | 单笔金额超过均值 3 倍标准差 |
| |Z| > 4 | 99.99% | 极端异常 | 金额为正常值的 10 倍以上 |
# Python 示例:使用 Z-score 识别金额异常值
import numpy as np
from scipy import stats
z_scores = np.abs(stats.zscore(df['amount']))
outliers = df[z_scores > 3]
print(f"检测到异常值数量: {len(outliers)}")
4.2 IQR 方法
IQR(四分位距)方法不依赖正态分布假设,适用于偏态分布或存在极端值的成本数据。其原理基于数据的四分位数:
- Q1(第一四分位数):数据中 25% 的值小于该数
- Q3(第三四分位数):数据中 75% 的值小于该数
- IQR = Q3 - Q1
- 异常值判定:值 < Q1 - 1.5×IQR 或值 > Q3 + 1.5×IQR
# Python 示例:使用 IQR 识别金额异常值
Q1 = df['amount'].quantile(0.25)
Q3 = df['amount'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = df[(df['amount'] < lower_bound) | (df['amount'] > upper_bound)]
print(f"IQR 方法检测到异常值数量: {len(outliers_iqr)}")
4.3 两种方法对比
| 对比维度 | Z-score 方法 | IQR 方法 |
|---|---|---|
| 分布假设 | 正态分布 | 无分布假设 |
| 对极端值敏感度 | 高(极端值影响均值和标准差) | 低(基于中位数和四分位数) |
| 适用数据量 | 建议 > 30 条 | 任意数据量 |
| 成本台账适用场景 | 金额分布近似正态时 | 金额分布偏态或存在极端值时 |
| 阈值可调性 | 可调整 Z 阈值(如 2.5, 3.5) | 可调整 IQR 倍数(如 1.5, 3.0) |
4.4 异常值处理策略
识别出异常值后,需根据业务上下文决定处理方式。在成本台账中,常见的处理策略包括:
- 修正:若异常值由录入错误引起(如小数点错位),直接修正为正确值。
- 截尾:将异常值替换为阈值边界值(如 Q3 + 1.5×IQR),适用于极端值但业务上仍属合理范围。
- 删除:若异常值无法修正且数量极少,直接删除该记录。
- 保留并标记:若异常值反映真实业务事件(如大额采购),保留数据并在分摊逻辑中单独处理。
# Python 示例:对异常值进行截尾处理
upper_cap = Q3 + 1.5 * IQR
lower_cap = Q1 - 1.5 * IQR
df['amount'] = df['amount'].clip(lower=lower_cap, upper=upper_cap)
print(f"截尾处理完成,上限: {upper_cap}, 下限: {lower_cap}")