数据预处理(二):缺失值处理 —— KingbaseES 函数 + Java 代码协同方案

——别让 NULL 毁了你的特征管道

大家好,我是那个总在训练日志里看到 NaN loss、又在数据血缘图里追查缺失源头的老架构。今天不聊模型调参,也不谈特征工程——我们解决一个看似简单却致命的问题:

当你从多个业务系统汇聚用户画像,发现“收入”字段有 30% 是 NULL,是该直接删掉这些样本,还是用某种策略填补?

很多人第一反应是:“用平均值填一下。”
但现实远比这复杂:不同特征、不同业务场景、不同缺失机制,需要不同的处理策略。而更关键的是——这个策略,应该在哪里执行?

是在 Java 里拉全量数据再补?还是在电科金仓 KingbaseES(KES) 里用 SQL 预处理?

答案是:两者协同,各司其职

今天我们就构建一个分层缺失值处理体系,让 KES 做高效批量填充,Java 做智能策略决策,共同守护 AI 数据质量。


一、缺失值不是“错误”,而是“信号”

首先得理解:缺失本身可能携带信息

  • 用户没填收入 → 可能是低收入群体不愿透露;
  • 设备未上报 GPS → 可能处于地下车库;
  • 日志中无点击事件 → 可能是新用户尚未活跃。

因此,处理缺失值的核心原则是:

不要盲目删除,也不要盲目填充;先理解缺失机制,再选择策略

常见策略包括:

  • 删除:仅当缺失比例极低(<1%)且随机缺失;
  • 常量填充:如“未知”、“0”;
  • 统计填充:均值、中位数、众数;
  • 模型预测填充:用其他特征预测缺失值(高级,本期暂不展开)。

二、KES 侧:用 SQL 函数做高效批量填充

对于大规模、规则明确的缺失处理,数据库是最高效的执行引擎

场景:用户表 user_profileincome 字段大量 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 SQL 填充

Java 查询

添加 missing indicator

原始表 raw_features.user_profile

v_clean_user_profile

UserProfileCleaner

ai_features.cleaned_profile

模型训练


五、写回 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 数据底座。

下一期,我们会讲:数据预处理(三):异常值检测与修正的工程实践
敬请期待。

—— 一位相信“数据的质量,决定了智能的上限”的架构师

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐