数据预处理(二):缺失值处理,KingbaseES函数+Java代码协同方案
摘要: 缺失值处理是AI数据治理的关键环节,需根据业务场景和缺失机制选择策略(删除/填充/标记)。本文提出KES+Java协同方案: KES侧:用SQL函数高效批量填充(如按城市中位数),创建物化视图优化性能; Java侧:实现动态策略(如缺失指示器、模型预测兜底),增强灵活性; 分层处理:KES处理规则化填充,Java处理复杂逻辑,通过监控缺失率预警上游故障。 方案结合数据库批量处理能力与Jav
数据预处理(二):缺失值处理 —— KingbaseES 函数 + Java 代码协同方案
——别让 NULL 毁了你的特征管道
大家好,我是那个总在训练日志里看到 NaN loss、又在数据血缘图里追查缺失源头的老架构。今天不聊模型调参,也不谈特征工程——我们解决一个看似简单却致命的问题:
当你从多个业务系统汇聚用户画像,发现“收入”字段有 30% 是 NULL,是该直接删掉这些样本,还是用某种策略填补?
很多人第一反应是:“用平均值填一下。”
但现实远比这复杂:不同特征、不同业务场景、不同缺失机制,需要不同的处理策略。而更关键的是——这个策略,应该在哪里执行?
是在 Java 里拉全量数据再补?还是在电科金仓 KingbaseES(KES) 里用 SQL 预处理?
答案是:两者协同,各司其职。
今天我们就构建一个分层缺失值处理体系,让 KES 做高效批量填充,Java 做智能策略决策,共同守护 AI 数据质量。
一、缺失值不是“错误”,而是“信号”
首先得理解:缺失本身可能携带信息。
- 用户没填收入 → 可能是低收入群体不愿透露;
- 设备未上报 GPS → 可能处于地下车库;
- 日志中无点击事件 → 可能是新用户尚未活跃。
因此,处理缺失值的核心原则是:
不要盲目删除,也不要盲目填充;先理解缺失机制,再选择策略。
常见策略包括:
- 删除:仅当缺失比例极低(<1%)且随机缺失;
- 常量填充:如“未知”、“0”;
- 统计填充:均值、中位数、众数;
- 模型预测填充:用其他特征预测缺失值(高级,本期暂不展开)。
二、KES 侧:用 SQL 函数做高效批量填充
对于大规模、规则明确的缺失处理,数据库是最高效的执行引擎。
场景:用户表 user_profile 中 income 字段大量 NULL
CREATE TABLE raw_features.user_profile (
user_id VARCHAR(64) PRIMARY KEY,
age INT,
income BIGINT, -- 可能为 NULL
city VARCHAR(32)
);
步骤 1:创建填充函数(按城市中位数)
-- 先建中间表存储各城市收入中位数
CREATE MATERIALIZED VIEW mv_city_income_median AS
SELECT
city,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY income) AS median_income
FROM raw_features.user_profile
WHERE income IS NOT NULL
GROUP BY city;
-- 创建填充函数
CREATE OR REPLACE FUNCTION fill_income_by_city(user_city VARCHAR)
RETURNS BIGINT AS $$
DECLARE
med BIGINT;
BEGIN
SELECT median_income INTO med
FROM mv_city_income_median
WHERE city = user_city;
RETURN COALESCE(med, 0); -- 若城市无数据,默认 0
END;
$$ LANGUAGE plpgsql STABLE;
✅ 优势:
- 利用 KES 的
PERCENTILE_CONT精确计算中位数;MATERIALIZED VIEW避免重复计算;- 函数可复用、可测试。
步骤 2:生成清洗后视图(供 Java 查询)
CREATE OR REPLACE VIEW ai_features.v_clean_user_profile AS
SELECT
user_id,
age,
COALESCE(income, fill_income_by_city(city)) AS income_filled,
city
FROM raw_features.user_profile;
这样,Java 应用只需查询 v_clean_user_profile,即可获得已填充数据,无需关心底层逻辑。
三、Java 侧:做策略决策与异常兜底
但有些场景,SQL 无法覆盖。比如:
- 需要根据用户行为序列动态判断是否缺失;
- 需要记录“该字段曾缺失”作为新特征(missing indicator);
- 需要对接外部风控系统判断是否可信。
这时,Java 就该上场。
示例:Java 实现“缺失指示器 + 智能兜底”
public class UserProfileCleaner {
public CleanedUser clean(UserRawProfile raw) {
// 缺失指示器:新增布尔特征
boolean incomeMissing = (raw.getIncome() == null);
Long incomeFilled;
if (raw.getIncome() != null) {
incomeFilled = raw.getIncome();
} else if (raw.getCity() != null) {
// 尝试从 KES 获取城市中位数(缓存优化)
incomeFilled = cityMedianCache.getOrDefault(raw.getCity(), 0L);
} else {
// 最终兜底:全局中位数(从配置中心获取)
incomeFilled = globalConfig.getDefaultIncomeMedian();
}
return new CleanedUser(
raw.getUserId(),
raw.getAge(),
incomeFilled,
incomeMissing, // 新增特征!
raw.getCity()
);
}
}
💡 关键洞察:“是否缺失”本身就是一个强特征,很多金融风控模型都依赖它。
四、协同方案:分层处理,各取所长
| 任务 | 执行位置 | 原因 |
|---|---|---|
| 大规模统计填充(均值/中位数) | KES SQL | 利用列存、并行扫描,性能高 |
| 缺失指示器生成 | Java | 需要构造新特征,逻辑灵活 |
| 复杂规则(如跨表关联) | KES 存储过程 | 避免多次网络往返 |
| 模型预测填充 | Java + ML 库 | 需要加载 sklearn/DL4J 模型 |
数据流示例:
五、写回 KES:持久化清洗结果
清洗后的数据,应写入专用表,供后续训练/推理使用:
String sql = """
INSERT INTO ai_features.cleaned_profile
(user_id, age, income, income_missing, city)
VALUES (?, ?, ?, ?, ?)
""";
try (Connection conn = KESDataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
conn.setAutoCommit(false);
for (CleanedUser u : cleanedUsers) {
ps.setString(1, u.userId);
ps.setInt(2, u.age);
ps.setLong(3, u.income);
ps.setBoolean(4, u.incomeMissing);
ps.setString(5, u.city);
ps.addBatch();
}
ps.executeBatch();
conn.commit();
}
🔗 驱动请从 电科金仓官网下载,确保支持
BOOLEAN类型。
同时,在 KES 中建立索引加速查询:
CREATE INDEX idx_cleaned_income_missing ON ai_features.cleaned_profile (income_missing);
六、监控与告警:缺失率突增即预警
最后,别忘了监控:
-- 每日统计各字段缺失率
SELECT
'income' AS field,
COUNT(*) FILTER (WHERE income IS NULL) * 1.0 / COUNT(*) AS null_ratio
FROM raw_features.user_profile;
Java 定时任务读取该结果,若 null_ratio > 阈值,立即告警——因为缺失率突增往往意味着上游埋点故障。
结语:缺失值处理,是数据治理的缩影
AI 的可靠性,不在于模型多复杂,而在于对数据缺陷的敬畏与应对。
电科金仓的 KES,通过强大的 SQL 函数和物化视图能力,让你能高效完成批量填充;
而 Java,则提供灵活性与智能决策能力。
两者协同,才能构建一个既高效又鲁棒的 AI 数据底座。
下一期,我们会讲:数据预处理(三):异常值检测与修正的工程实践。
敬请期待。
—— 一位相信“数据的质量,决定了智能的上限”的架构师
更多推荐


所有评论(0)