使用Sklearn KNN Imputer填充后仍有缺失值,原因何在?
KNNImputer填充后仍存在NaN的原因分析
问题背景
尝试用Sklearn的KNNImputer填充instrumentalness列的NaN值,填充前该列有3452个NaN,填充后仍剩余472个NaN。以下是数据清洗及填充的代码和结果:
数据清洗代码及输出
# Create row for both the singer and track name train.insert(2,'Artist Track',(train['Artist Name']+ " " + train['Track Name'])) # Remove duplicates for same Artist, Song, and Class # Sort values by Artist Track then columns with NaNs to possibly drop duplicates with NaNs train.sort_values(by=['Artist Track','Popularity','key','instrumentalness'], inplace=True) train.drop_duplicates(subset=['Artist Track', 'Class'], keep='first', inplace=True) # Remove duplicates of tracks if instrumentalness duplicate is NaN train.sort_values(by=['Artist Track','instrumentalness'], inplace=True) dups_ins = train[train.duplicated(subset=['Artist Track'], keep='first')==True].index ins_nans = np.where(train['instrumentalness'].isna())[0] drop_ins = set(dups_ins).intersection(ins_nans) train.drop(drop_ins, inplace=True) # Remove duplicates of tracks if key duplicate is NaN train.sort_values(by=['Artist Track','key'], inplace=True) dups_key = train[train.duplicated(subset=['Artist Track'], keep='first')==True].index key_nans = np.where(train['key'].isna())[0] drop_key = set(dups_key).intersection(key_nans) train.drop(drop_key, inplace=True) # Remove duplicates of tracks if popularity duplicate is NaN train.sort_values(by=['Artist Track','Popularity'], inplace=True) dups_pop = train[train.duplicated(subset=['Artist Track'], keep='first')==True].index pop_nans = np.where(train['Popularity'].isna())[0] drop_pop = set(dups_pop).intersection(pop_nans) train.drop(drop_pop, inplace=True) train['instrumentalness'].isna().sum()
输出:
3452
KNN填充代码及输出
from sklearn.impute import KNNImputer fea_transformer = KNNImputer(n_neighbors=3) values = fea_transformer.fit_transform(train[['instrumentalness']]) train['instrumentalness'] = pd.DataFrame(values) train['instrumentalness'].isna().sum()
输出:
472
可能的原因
- 仅用单一列做KNN填充,无法处理该列全NaN的样本组
KNNImputer依赖特征相似度匹配邻近样本,用邻居均值填充NaN。但你只传入了instrumentalness这一列,如果某个样本该列是NaN,且所有其他样本该列也都是NaN(比如某歌手某首歌的所有样本该列全空),KNN找不到任何有有效值的邻居,只能保留NaN。 - 孤立NaN样本的邻居无有效值
你设置了n_neighbors=3,如果某个NaN样本的3个最近邻居(仅按instrumentalness列值计算)也都是NaN,就无法计算填充值,该样本的NaN会被保留。比如数据中存在连续的NaN块,块内样本找不到足够的有值邻居。 - 数据清洗未覆盖全NaN的样本组
你的清洗步骤仅删除重复的NaN样本,但对于单个Artist Track下仅存的唯一样本且该样本instrumentalness为NaN的情况,清洗后依然保留,这类样本在单一列填充时无法被KNN处理。
解决建议
- 使用多特征列做KNN填充:加入和
instrumentalness相关的特征(比如Popularity、key),让KNN能基于多维度相似度找邻居,示例代码:from sklearn.impute import KNNImputer fea_transformer = KNNImputer(n_neighbors=3) # 传入多个相关特征列 filled_values = fea_transformer.fit_transform(train[['instrumentalness', 'Popularity', 'key']]) # 将填充后的instrumentalness列放回原数据集 train['instrumentalness'] = filled_values[:, 0] - 检查剩余NaN样本:查看这472个样本的其他特征是否也全为空,或者是否属于同一类全NaN的组,这类样本可以单独用全局均值/中位数填充,或者直接删除。
- 调整KNN参数:尝试减小
n_neighbors(比如设为1),让样本更容易找到至少一个有值的邻居,但注意避免过拟合问题。
内容的提问来源于stack exchange,提问作者Esraa Abdelmaksoud
相关产品推荐
相关产品推荐

