将MATLAB中带seuclidean距离的knnimpute转换为Python实现
将MATLAB的
knnimpute转换为Python的解决方案 你的MATLAB代码filled_data = knnimpute(data,1, 'distance','seuclidean')核心逻辑是:用k=1的K近邻填充缺失值,采用距离加权(k=1时等价于直接取最近邻的值),距离度量为标准化欧氏距离(seuclidean)。以下是两种可行的转换方案:
方案一:使用sklearn.impute.KNNImputer自定义度量
KNNImputer支持传入自定义距离函数,我们可以实现兼容NaN的标准化欧氏距离:
- 先计算每个特征的标准差(忽略NaN,避免除以0)
- 定义处理NaN的标准化欧氏距离函数
- 初始化
KNNImputer并执行填充
import numpy as np from sklearn.impute import KNNImputer # 假设data是你的输入数组(含NaN) data = np.array(...) # 1. 计算各特征的标准差(忽略NaN),处理标准差为0的情况 data_std = np.nanstd(data, axis=0) data_std[data_std == 0] = 1e-8 # 避免除以0 # 2. 定义兼容NaN的标准化欧氏距离函数 def seuclidean_with_nan(x, y): # 筛选两个样本都非NaN的特征 valid_mask = ~(np.isnan(x) | np.isnan(y)) if not np.any(valid_mask): return np.inf # 全NaN时距离设为无穷大 # 计算标准化欧氏距离 diff = (x[valid_mask] - y[valid_mask]) / data_std[valid_mask] return np.sqrt(np.sum(diff ** 2)) # 3. 初始化KNNImputer并填充 imputer = KNNImputer(n_neighbors=1, weights='distance', metric=seuclidean_with_nan) filled_data = imputer.fit_transform(data)
方案二:手动实现MATLAB风格的knnimpute
如果需要完全贴合MATLAB的原始行为,可手动实现核心逻辑,适合小数据集或需要精细调试的场景:
import numpy as np def knnimpute_seuclidean(data, k=1): data = np.array(data, dtype=np.float64) n_samples, n_features = data.shape # 计算各特征标准差(忽略NaN) stds = np.nanstd(data, axis=0) stds[stds == 0] = 1e-8 # 获取所有无缺失值的样本索引 non_missing_idx = np.where(~np.isnan(data).any(axis=1))[0] if len(non_missing_idx) == 0: raise ValueError("没有可用的非缺失样本") # 遍历每个样本,填充缺失值 for i in range(n_samples): missing_mask = np.isnan(data[i]) if not np.any(missing_mask): continue # 计算当前样本与所有非缺失样本的标准化欧氏距离 distances = [] for j in non_missing_idx: if i == j: continue # 筛选两个样本都非NaN的特征 valid_mask = ~(missing_mask | np.isnan(data[j])) if not np.any(valid_mask): dist = np.inf else: diff = (data[i, valid_mask] - data[j, valid_mask]) / stds[valid_mask] dist = np.sqrt(np.sum(diff ** 2)) distances.append((dist, j)) # 按距离排序,取最近的k个样本 distances.sort(key=lambda x: x[0]) top_k = distances[:k] # 距离加权填充(k=1时直接取最近邻的值) if k == 1: fill_vals = data[top_k[0][1], missing_mask] else: # 加权平均(k>1时用,这里保留逻辑) total_weight = sum(1 / (d + 1e-8) for d, _ in top_k) fill_vals = sum((data[j, missing_mask] / (d + 1e-8)) for d, j in top_k) / total_weight data[i, missing_mask] = fill_vals return data # 使用示例 filled_data = knnimpute_seuclidean(data, k=1)
注意事项
- 两种方案都处理了标准差为0的情况,避免除以0错误
- 针对全NaN的样本或特征,添加了异常处理逻辑
- 当k=1时,距离加权等价于直接取最近邻的值,和MATLAB代码行为一致
内容的提问来源于stack exchange,提问作者k-c
相关产品推荐
相关产品推荐

