基于Python KNN实现对照组与测试组唯一配对及报错解决
报错原因
- 赋值逻辑错误:
iat为单个标量元素的定位方法,你右侧取到的Males_Ctrl_highEd.loc[indices[0]]是一维Series结构,无法直接赋值给单个标量位置;同时你初始化的匹配结果表行数与测试组一致,不需要固定取loc[0],应该对应第i个测试样本的行位置赋值。 - KNN模型未随对照组更新重新拟合:你仅在循环外对初始对照组拟合了一次KNN模型,每次删除已匹配的对照样本后,对照组的索引和样本集合已经发生变化,旧模型返回的索引值已经失效,会导致匹配错误甚至索引不存在报错。
修复后可运行代码
以下是和你Matlab逻辑完全对应的Python实现:
import numpy as np import pandas as pd from sklearn.neighbors import NearestNeighbors # 初始化和分段代码不变 TestGroup = pd.read_csv('KNN_DummyData1.csv', names = ['Gender', 'Age', 'Education']) ControlGroup = pd.read_csv('KNN_DummyData2.csv', names = ['Gender', 'Age', 'Education']) #### 按性别、教育程度分组 Males_highEd = TestGroup.loc[(TestGroup['Gender'] == 1) & (TestGroup['Education'] == 1)] Males_highEd.reset_index(drop=True, inplace=True) Males_highEd.drop(columns=['Gender', 'Education'], inplace=True) Males_Ctrl_highEd = ControlGroup.loc[(ControlGroup['Gender'] == 1) & (ControlGroup['Education'] == 1)] Males_Ctrl_highEd.reset_index(drop=True, inplace=True) Males_Ctrl_highEd.drop(columns=['Gender', 'Education'], inplace=True) # 匹配逻辑修改 # 初始化匹配结果表,和测试组行数一致 Matched_Males_Ctrl_highEd = pd.DataFrame().reindex_like(Males_highEd) # 复制一份对照组避免修改原始数据 current_ctrl = Males_Ctrl_highEd.copy() for i in range(len(Males_highEd)): # 每次都用当前剩余的对照组重新训练KNN nbrs = NearestNeighbors(n_neighbors=1, algorithm='ball_tree').fit(current_ctrl) distances, indices = nbrs.kneighbors(Males_highEd.iloc[i:i+1]) # 取出匹配到的年龄值,赋值给对应行 matched_age = current_ctrl.iloc[indices[0][0]]['Age'] Matched_Males_Ctrl_highEd.iloc[i, Matched_Males_Ctrl_highEd.columns.get_loc('Age')] = matched_age print(f"{i+1} controls of {len(Males_highEd)} tests found") # 删除已匹配的样本,重置索引避免后续匹配错位 current_ctrl = current_ctrl.drop(current_ctrl.index[indices[0][0]]).reset_index(drop=True)
更优方案推荐
逐个匹配的方式容易出现前面的匹配占用了全局最优样本,导致后续样本匹配偏差过大的问题,可以用*匈牙利算法(线性分配)*做全局最优匹配,保证总匹配年龄差最小,且天然满足每个对照仅被匹配一次的要求,代码更简洁、匹配效果更好:
from scipy.optimize import linear_sum_assignment # 计算所有测试样本和对照样本的年龄差绝对值矩阵 age_diff = np.abs(Males_highEd['Age'].values.reshape(-1,1) - Males_Ctrl_highEd['Age'].values.reshape(1,-1)) # 匈牙利算法计算最优分配 row_ind, col_ind = linear_sum_assignment(age_diff) # 直接生成匹配结果 Matched_Males_Ctrl_highEd = Males_Ctrl_highEd.iloc[col_ind].reset_index(drop=True)
内容的提问来源于stack exchange,提问作者Pi-Wi
相关产品推荐
相关产品推荐

