实施KNN算法时出错

时间:2018-09-14 03:13:07

标签: python python-3.x pandas numpy scipy

对于stackoverflow和编程来说都是新手,我来自以下统计背景,是KNN算法的实现。遇到错误 TypeError: unsupported operand type(s) for -: 'str' and 'str'

这些是我遇到的其他错误。预先感谢您的答复。

  

文件“ knn.py”,第78行,在       main()

     

文件“ knn.py”,第71行,位于主目录中       邻居= getNeighbors(trainingSet,testSet [x],k)

     

getNeighbors中的文件“ knn.py”,第33行       dist = euclideanDistance(testInstance,trainingSet [x],长度)

     

文件“ knn.py”,第26行,在euclideanDistance中       距离+ = pow((instance1 [x]-instance2 [x]),2)

import csv
import random
import math
import pandas
import numpy

def loadDataset(filename, split, trainingSet=[] , testSet=[]):

    filename = 'data1.csv'
    raw_data = open(filename, 'rt')
    reader = csv.reader(raw_data, delimiter=',', quoting=csv.QUOTE_NONE)
    dataset = list(reader)


    for x in range(len(dataset)-1):
        for y in range(4):
            dataset[x][y] = float(dataset[x][y])
        if random.random() < split:
            trainingSet.append(dataset[x])
        else:
            testSet.append(dataset[x])

def euclideanDistance(instance1, instance2, length):
    distance = 0
    for x in range(length):
        distance += pow((instance1[x] - instance2[x]), 2)
    return math.sqrt(distance)

def getNeighbors(trainingSet, testInstance, k):
    distances = []
    length = len(testInstance)-1
    for x in range(len(trainingSet)):
        dist = euclideanDistance(testInstance, trainingSet[x], length)
        distances.append((trainingSet[x], dist))
    distances.sort(key=operator.itemgetter(1))
    neighbors = []
    for x in range(k):
        neighbors.append(distances[x][0])
    return neighbors

def getResponse(neighbors):
    classVotes = {}
    for x in range(len(neighbors)):
        response = neighbors[x][-1]
        if response in classVotes:
            classVotes[response] += 1
        else:
            classVotes[response] = 1
    sortedVotes = sorted(classVotes.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedVotes[0][0]

def getAccuracy(testSet, predictions):
    correct = 0
    for x in range(len(testSet)):
        if testSet[x][-1] == predictions[x]:
            correct += 1
    return (correct/float(len(testSet))) * 100.0

def main():
# prepare data
    trainingSet=[]
    testSet=[]
    split = 0.67
    loadDataset('data1.csv', split, trainingSet, testSet)
    print ('Train set: ' + repr(len(trainingSet)))
    print ('Test set: ' + repr(len(testSet)))
# generate predictions
    predictions=[]
    k = 3
    for x in range(len(testSet)):
        neighbors = getNeighbors(trainingSet, testSet[x], k)
        result = getResponse(neighbors)
        predictions.append(result)
        print('> predicted=' + repr(result) + ', actual=' + repr(testSet[x][-1]))
    accuracy = getAccuracy(testSet, predictions)
    print('Accuracy: ' + repr(accuracy) + '%')

main()

2 个答案:

答案 0 :(得分:0)

Vamsi,

我注意到您正在使用numpy和pandas。在进行调试时,我确实想推荐另一个很棒的软件包,即sci-kit learning。

他们已经内置了KNN的实现:http://scikit-learn.org/stable/modules/neighbors.html

编辑: 我相信第26行存在强制转换问题。看来您正在尝试减去2个字符串。我认为,如果您使用整数数据,这可能会解决您的问题

def euclideanDistance(instance1, instance2, length):
     distance = 0
     for x in range(length):
         distance += pow((int(instance1[x]) - int(instance2[x])), 2)
     return math.sqrt(distance)

如果您使用浮点数据,那么以下方法将起作用:

distance += pow((float(instance1[x]) - float(instance2[x])), 2)

答案 1 :(得分:0)

编辑

df = pd.read_csv(filename) 默认返回字符串。您需要将适当的项目转换为浮点数。

此外,您可以直接使用

获取熊猫数据框
data = df.values

并使用

获得一个Numpy数组(如果您愿意)
euclideanDistance

然后对这些数据进行操作。


csv的第一行可能是标题,并且您没有跳过它。因此,您的第一个训练实例实际上是构成标头的字符串,并且您尝试在length = len(testInstance) - 1 for x in range(len(trainingSet)): dist = euclideanDistance(testInstance, trainingSet[x], length) distances.append((trainingSet[x], dist)) 函数中减去字符串。

话虽如此,您的代码非常 unpythonic

例如,

length

您不需要传递len,因为可以使用euclideanDistance中的trainingSet函数来查找

您可以直接遍历for x in trainingSet: dist = euclideanDistance(testInstance, x, length) distances.append((x, dist)) 中的实例

distances = [(x, euclideanDistance(testInstance, x) for x in trainingSet)]

或更佳

neighbors = []
for x in range(k):
    neighbors.append(distances[x][0])

同样,

neighbors = [x[0] for x in distances[:k]]

可以是

def loadDataset(filename, split, trainingSet=[] , testSet=[]):

Python还允许您一次返回多个项目,并且像其他语言中常见的那样,通过引用返回是极其糟糕的做法。所以

def loadDataset(filename, split):
    trainingSet = []
    testSet = []
    # ...

    return trainingSet, testSet

trainingSet, testSet = loadDataset(filename, ',')

应该是

testInstance

对于这种应用程序,在这种情况下,应避免使用Python列表,而应使用Numpy数组存储数据。这样,许多操作都可以向量化,从而极大地提高了性能。

例如,要计算trainingSet# I'm deliberately converting them to numpy array but in general # you should keep them in this form right from the start testInstance = np.asarray(testInstance).reshape(1, -1)[:-1] # Your last item is label. Ideally remove them at the beginning trainingSet = np.vstack(trainingSet)[:, :-1] # Same case as above. # Here we use broadcasting to obtain difference # between each row in trainingSet and testInstance distances = np.linalg.norm(trainingSet - testInstance, axis=1)**2 之间的距离

https://start.spring.io/

如果允许/愿意使用Scipy,那么还有其他机会来减少代码行并加快操作速度。

用这种方式编写代码不仅可以提供更好的性能,而且还更简洁,更接近原始数学表达式。