【问题标题】:Getting error while implementing KNN algorithm实现 KNN 算法时出错
【发布时间】:2019-02-18 19:46:14
【问题描述】:

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

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

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

文件“knn.py”,第 71 行,在 main 邻居 = getNeighbors(trainingSet, testSet[x], k)

文件“knn.py”,第 33 行,在 getNeighbors dist = euclideanDistance(testInstance, trainingSet[x], length)

文件“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()

【问题讨论】:

  • 调用loadDataset后,能否查看print(type(trainingSet[0]))的结果
  • 类型为列表

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


【解决方案1】:

Vamsi,

我注意到您正在使用 numpy 和 pandas。同时,我正在调试,我确实想推荐一个更棒的包,sci-kit learn。

他们已经内置了 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)

【讨论】:

  • 我想使用 numpy 和 pandas 来实现,因为 scikit learn 需要更多时间来执行它,因为它需要编译整个库。
【解决方案2】:

编辑

csv.reader 默认返回 string。您需要将适当的项目转换为浮点数。

另外,您可以使用直接获取 Pandas 数据框

df = pd.read_csv(filename)

如果你愿意,可以使用一个 Numpy 数组

data = df.values

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


您的 csv 的第一行可能是标题,您没有跳过它。因此,您的第一个训练实例实际上是构成标题的字符串,而您正试图在 euclideanDistance 函数中减去字符串。

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

例如,

length = len(testInstance) - 1
for x in range(len(trainingSet)):
    dist = euclideanDistance(testInstance, trainingSet[x], length)
    distances.append((trainingSet[x], dist))

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

你可以直接遍历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]]

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

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

应该是

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

    return trainingSet, testSet

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

对于这种应用程序,您应该避免在这种情况下使用 Python 列表,而是使用 Numpy 数组来存储数据。通过这种方式可以对许多操作进行矢量化,从而极大地提高性能。

例如,计算testInstancetrainingSet 之间的距离

# 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

如果您被允许/愿意使用 Scipy,那么还有其他机会可以减少代码行数并加快操作。

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

【讨论】:

  • 不,csv的第一行不是表头。
  • 我来自统计背景,所以我的 python 代码真的很愚蠢
猜你喜欢
  • 2020-05-18
  • 2012-07-01
  • 1970-01-01
  • 2017-04-03
  • 2019-09-13
  • 2015-03-18
  • 2019-06-22
  • 1970-01-01
  • 2019-03-12
相关资源
最近更新 更多