Python从零实现KNN算法遇类型错误:字符串无法减法运算求助
解决KNN实现中的TypeError及数据处理问题
看起来你已经找对了方向——数据类型确实是核心问题!咱们一步步拆解解决:
1. 报错根源:字符串类型无法做数值运算
你的错误TypeError: unsupported operand type(s) for -: 'str' and 'str'非常明确:用来计算距离的特征数据是字符串/字节类型,没法执行减法、平方这些数值运算。你怀疑要转成float是完全正确的,不过还要注意ARFF数据加载后的特殊处理。
2. 先处理ARFF数据的类型问题
用scipy.io.arff.loadarff加载数据后,字符串类型的字段会被解析成bytes格式(比如b'1.23'),需要先解码再转换为数值类型。你可以批量处理训练集和测试集:
from scipy.io import arff import pandas as pd import numpy as np import math # 加载并处理训练集 data_train = arff.loadarff('train.arff') train = pd.DataFrame(data_train[0]) # 批量转换字节/字符串为数值类型 for col in train.columns: if train[col].dtype == 'object': # 先解码字节为普通字符串 train[col] = train[col].str.decode('utf-8') # 尝试转为float(标签列或分类特征会报错,跳过即可) try: train[col] = train[col].astype(float) except ValueError: pass # 同样处理测试集 data_test = arff.loadarff('test1.arff') test = pd.DataFrame(data_test[0]) for col in test.columns: if test[col].dtype == 'object': test[col] = test[col].str.decode('utf-8') try: test[col] = test[col].astype(float) except ValueError: pass
3. 修正train_test_split的误用
你现在的用法完全错了!train_test_split是用来把单个数据集拆分成训练集和验证集,而不是把两个独立的数据集(train和test)传进去。正确的做法是:
- 先从你的训练集里拆分出特征(X)和标签(y)
- 再拆分训练集和验证集;如果已经有单独的测试集,直接用测试集即可
from sklearn.model_selection import train_test_split # 注意:sklearn.cross_validation已废弃,改用model_selection # 假设你的数据集最后一列是标签,替换成你实际的标签列名 label_col = 'your_label_column_name' # 拆分训练集的特征和标签 X_train_full = train.drop(label_col, axis=1) y_train_full = train[label_col] # 拆分训练集和验证集(可选,如果你需要用一部分训练数据做验证) X_train, X_val, y_train, y_val = train_test_split(X_train_full, y_train_full, test_size=0.1, random_state=42) # 处理测试集的特征和标签 X_test = test.drop(label_col, axis=1) y_test = test[label_col]
4. 修正代码里的两个小bug
distance函数的笔误:你写的是return dis,但变量名是dist,这会导致未定义错误- 遍历DataFrame的错误:直接
for testpoint in X_test会遍历列名,而不是每行数据,需要用.values获取每行的数值数组
修正后的函数:
def distance(testpoint, trainpoint): # 计算测试点与训练点之间的距离 dist = np.sqrt(np.sum(np.power(testpoint-trainpoint, 2))) return dist # 修正笔误:返回dist而不是dis def getNeighbors(X_train, y_train, X_test, k): k_neighbors_with_labels = [] for testpoint in X_test.values: # 用.values获取每行的数值数组 distances_label = [] for (trainpoint,y_train_label) in zip(X_train.values, y_train): distances_label.append((distance(testpoint, trainpoint), y_train_label)) # 排序后取前k个最近邻 k_neighbors_with_labels.append(sorted(distances_label)[0:k]) return k_neighbors_with_labels # 调用函数(如果用验证集就传X_val、y_val,用测试集就传X_test、y_test) ne = getNeighbors(X_train, y_train, X_val, k = 3) print(ne)
最后检查
运行前可以先打印X_train.dtypes和X_test.dtypes,确认所有特征列都是float64或者int64类型,这样就能正常执行数值运算了。
内容的提问来源于stack exchange,提问作者Meg
相关产品推荐
相关产品推荐

