DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
晚安云;香港 CN2 ¥15/月
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

IterativeImputer 保留空列时改了原数组,或报只读错误

scikit-learn 1.9.1 的 IterativeImputer 在 keep_empty_features=True 路径可能写回输入。为插补建立独立可写工作数组,并保留列顺序和原缺失掩码。

IterativeImputer(keep_empty_features=True) 完成后,原数组的全空列变成零;输入换成 pandas DataFrame,又可能报 ValueError: assignment destination is read-only。这两种表现可以来自同一个输入共享问题,不能只看是否抛异常。

明确故障环境为 scikit-learn 1.9.1、pandas 3.0.6。F 连续的浮点数组可能直接被复用,pandas 输入则可能提供只读数据视图。保留空特征的初始化分支将中间变量指向这个输入,再尝试写入;即使没有空列,该赋值路径也可能触发只读检查。

为插补单独准备可写数值数组

若特征都是适合该插补器的数值,可以在调用边界创建独立工作数据:

import numpy as np
from sklearn.experimental import enable_iterative_imputer  # noqa: F401
from sklearn.impute import IterativeImputer

work = np.array(X, dtype=np.float64, order="F", copy=True)
imputer = IterativeImputer(keep_empty_features=True, max_iter=10)
result = imputer.fit_transform(work)

X 是保留的原输入。这里强制复制和写入能力,允许受影响实现修改 work,避免写入调用方的唯一原件。新增数组会增加内存消耗,大数据应在原资源约束下安排;这不是零成本操作。

如果 X 是 DataFrame,转换会丢掉索引和列名这一层结构。先保存列顺序、行索引及需要的元数据,结果按同一顺序使用;下游依赖 feature_names_in_ 或 pandas 输出时,需明确适配,不能仅为复制就让训练和预测的特征顺序发生变化。非数值列也不能不经建模设计就强转为 float64。

transform() 同样需要独立工作输入;只保护训练数组,预测时仍直接传原件,会留下相同风险。不要设置 pandas 底层数组的 writeable=True 来绕过保护,也不要直接改 sklearn 私有方法。

关掉 keep_empty_features 会改变输出列

keep_empty_features=False 可能避开这条分支,却会移除拟合时全缺失的特征。模型、列名映射或后续拼接若依赖固定列数,这样修改会改变输入契约。应先决定业务是否允许删列,不能把开关翻转当作等价修复。

已被改写的数组无法仅凭修改后的值恢复缺失位置。应从原始数据或可信缺失掩码恢复;不能把所有零都换成 NaN,原数据中的零可能合法。

验收分别覆盖 F 连续数组、实际 pandas 输入、全空列与无全空列,对比调用前后的原输入、缺失掩码、输出列数与列顺序。同时检查 fit_transform 和 transform,确认副本处理没有改变插补器的训练配置。

1.9.1 正式源码仍有这条原地赋值路径。不能因为某台机器的 C 连续数组碰巧发生隐式复制,就宣称其他布局也安全;后续更换版本应保留这组输入布局对照,再决定是否继续保留显式工作副本。