欧几里得距离错误:不支持的操作数类型

1 投票
2 回答
674 浏览
提问于 2025-04-18 03:18

我正在尝试使用 scipy.spatial.distance 来实现 欧几里得距离,之前我都是这样写的。

from math import sqrt

critics = {'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,
                         'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,
                         'The Night Listener': 3.0},
           'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,
                            'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,
                            'You, Me and Dupree': 3.5},
           'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,
                                'Superman Returns': 3.5, 'The Night Listener': 4.0},
           'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,
                            'The Night Listener': 4.5, 'Superman Returns': 4.0,
                            'You, Me and Dupree': 2.5},
           'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                            'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
                            'You, Me and Dupree': 2.0},
           'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                             'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
           'Toby': {'Snakes on a Plane': 4.5, 'You, Me and Dupree': 1.0, 'Superman Returns': 4.0}}


def sim_distance(preference, person1, person2):
    si = {}
    for item in preference[person1]:
        if item in preference[person2]:
            si[item] = 1

    if len(si) == 0: return 0

    sum_of_scores = sum([pow(preference[person1][item] - preference[person2][item], 2)
                         for item in preference[person1] if item in preference[person2]])

    return 1 / (1 + sum_of_scores)


a = sim_distance(critics, 'Lisa Rose','Mick LaSalle')
print(a) #0.333

现在的实现运行得很好,但当我尝试使用 scipy 模块时,我不太明白应该提供什么样的输入。这是我尝试过的。

from scipy.spatial.distance import euclidean


a = euclidean(critics['Lisa Rose'], critics['Mick LaSalle'])
print(a)

错误追踪

Traceback (most recent call last):
  File "C:/Users/Ajay/PycharmProjects/SO/new.py", line 22, in <module>
    a = euclidean(critics['Lisa Rose'], critics['Mick LaSalle'])
  File "C:\Python33\lib\site-packages\scipy\spatial\distance.py", line 224, in euclidean
    dist = norm(u - v)
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

在查看 euclidean 的实现时,似乎输入应该以 元组 的形式给出,但我不太明白该如何处理这个。

def euclidean(u, v):
    """
    Computes the Euclidean distance between two 1-D arrays.

    The Euclidean distance between 1-D arrays `u` and `v`, is defined as

    .. math::

       {||u-v||}_2

    Parameters
    ----------
    u : (N,) array_like
        Input array.
    v : (N,) array_like
        Input array.

    Returns
    -------
    euclidean : double
        The Euclidean distance between vectors `u` and `v`.

    """
    u = _validate_vector(u)
    v = _validate_vector(v)
    dist = norm(u - v)
    return dist

请给我一些指点。

2 个回答

0

我做了一个不错的API,虽然代码写得不太好:

http://vectordict.readthedocs.org/en/latest/vector.html#metrics

重要提示:我不建议你使用这段代码(我正在重新写一份更好的),你可以看看它是怎么工作的,也许会对这个API有些尊重,但不要用这段代码。

通过重写加减乘除的操作,你可能会对如何用字典(dict)来做“数学”感兴趣,以及这怎么实现,能让生活变得更简单。

这里是L2范数的实现:sqrt( self.dot(self) )

https://github.com/jul/ADictAdd_iction/blob/master/vector_dict/VectorDict.py#L972

我提倡使用与线性代数定义一致的API,这样你读起来会更容易理解。

from vector_dict.VectorDict import cos
from vector_dict.VectorDict import convert_tree, VectorDict
crit = {'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,
                         'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,
                         'The Night Listener': 3.0},
           'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,
                            'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,
                            'You, Me and Dupree': 3.5},
           'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,
                                'Superman Returns': 3.5, 'The Night Listener': 4.0},
           'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,
                            'The Night Listener': 4.5, 'Superman Returns': 4.0,
                            'You, Me and Dupree': 2.5},
           'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                            'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
                            'You, Me and Dupree': 2.0},
           'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                             'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
           'Toby': {'Snakes on a Plane': 4.5, 'You, Me and Dupree': 1.0, 'Superman Returns': 4.0}}
dd = convert_tree(crit)
print "cos"
print cos(dd['Gene Seymour'], dd['Toby'])
# 0.770024275094
print "L2 distance"
print dd['Gene Seymour'].norm()
# 8.35164654425    
print "jaccard similarities"
print dd['Gene Seymour'].jaccard( dd['Toby'])
# 0.579335793358

顺便说一下,我猜如果你在做范数计算,那是为了比较,所以我推测你想做相似度测量。

1

欧几里得距离是用来衡量两个向量之间的差异的一种方法,可以理解为计算两个向量之间的“直线距离”。在代码中,这个计算可以表示为 dist = norm(u - v),这里的 euclidean 函数就是用来计算这种距离的。

不过,你提到的 critics['Lisa Rose']critics['Mick LaSalle'] 是字典类型,而字典之间是不能直接进行减法运算的。此外,norm 这个函数是专门用来处理数组类型的数据。

所以,如果你真的想在你的情况下使用 scipy.spatial.distance.euclidean,你需要为你的 critics 创建一个类,并在这个类里重写减法运算符,也就是定义一个叫 __sub__ 的方法,这个方法需要返回一个数组类型的数据。

撰写回答