Scipy 负距离?什么?

10 投票
5 回答
7540 浏览
提问于 2025-04-15 21:19

我有一个输入文件,里面包含了小数点后四位的浮点数:

i.e. 13359    0.0000    0.0000    0.0001    0.0001    0.0002`    0.0003    0.0007    ... 

(第一个是ID)。我的类使用了一个叫做 loadVectorsFromFile 的方法,这个方法会把这些数字乘以10000,然后用 int() 转换成整数。此外,我还会遍历每个向量,确保里面没有负值。但是,当我进行 _hclustering 操作时,我总是看到一个错误提示,内容是 "LinkageZ包含负值"

我真的觉得这可能是个bug,因为:

  1. 我检查过我的数值,
  2. 这些数值绝对没有小到或者大到接近浮点数的限制,
  3. 我用来计算文件中数值的公式使用了绝对值(我的输入绝对是正确的)。

有没有人能告诉我,为什么我会看到这个奇怪的错误?到底发生了什么,导致这个负距离的错误?

=====

def loadVectorsFromFile(self, limit, loc, assertAllPositive=True, inflate=True):
    """Inflate to prevent "negative" distance, we use 4 decimal points, so *10000
    """
    vectors = {}
    self.winfo("Each vector is set to have %d limit in length" % limit)
    with open( loc ) as inf:
        for line in filter(None, inf.read().split('\n')):
            l = line.split('\t')
            if limit:
                scores = map(float, l[1:limit+1])
            else:
                scores = map(float, l[1:])

            if inflate:        
                vectors[ l[0]] = map( lambda x: int(x*10000), scores)     #int might save space
            else:
                vectors[ l[0]] = scores                           

    if assertAllPositive:
        #Assert that it has no negative value
        for dirID, l in vectors.iteritems():
            if reduce(operator.or_, map( lambda x: x < 0, l)):
                self.werror( "Vector %s has negative values!" % dirID)
    return vectors

def main( self, inputDir, outputDir, limit=0,
        inFname="data.vectors.all", mappingFname='all.id.features.group.intermediate'):
    """
    Loads vector from a file and start clustering
    INPUT
        vectors is { featureID: tfidfVector (list), }
    """
    IDFeatureDic = loadIdFeatureGroupDicFromIntermediate( pjoin(self.configDir, mappingFname))
    if not os.path.exists(outputDir):
        os.makedirs(outputDir)

    vectors = self.loadVectorsFromFile( limit, pjoin( inputDir, inFname))
    for threshold in map( lambda x:float(x)/30, range(20,30)):
        clusters = self._hclustering(threshold, vectors)
        if clusters:
            outputLoc = pjoin(outputDir, "threshold.%s.result" % str(threshold))
            with open(outputLoc, 'w') as outf:
                for clusterNo, cluster in clusters.iteritems():
                    outf.write('%s\n' % str(clusterNo))
                    for featureID in cluster:
                        feature, group = IDFeatureDic[featureID]
                        outline = "%s\t%s\n" % (feature, group)
                        outf.write(outline.encode('utf-8'))
                    outf.write("\n")
        else:
            continue

def _hclustering(self, threshold, vectors):
    """function which you should call to vary the threshold
    vectors:    { featureID:    [ tfidf scores, tfidf score, .. ]
    """
    clusters = defaultdict(list)
    if len(vectors) > 1:
        try:
            results = hierarchy.fclusterdata( vectors.values(), threshold, metric='cosine')
        except ValueError, e:
            self.werror("_hclustering: %s" % str(e))
            return False

        for i, featureID in enumerate( vectors.keys()):

5 个回答

1

“Linkage Z 包含负值”。这个错误在使用 scipy 的层次聚类过程中出现,当链接矩阵中的某个链接聚类索引被赋值为 -1 时,就会发生这个问题。

根据我的观察,在合并过程中,如果要合并的所有聚类或点之间的距离计算结果是负无穷大,那么就会给某个链接聚类索引赋值为 -1。也就是说,链接函数在聚类之间的链接距离是负无穷大的情况下,仍然会合并这些聚类,并给其中一个聚类或点分配一个负的索引。

总结一下,如果你使用的是 余弦距离 作为度量标准,而任何数据点的范数或大小为零,那么就会出现这个错误。

5

我觉得你遇到的问题是因为在调用fclusterdata的时候使用了余弦距离。你可以试试用欧几里得距离,看看错误是不是消失了。

余弦距离可能会出现负值,如果你数据集中两个向量的点积大于1。因为你使用的是非常大的数字并进行了归一化,所以我猜你的数据集中点积大于1的情况很多。如果你想使用余弦距离,你需要对数据进行归一化,确保两个向量的点积永远不会超过1。你可以在这个页面上查看Scipy中余弦距离的定义。

编辑:

从源代码来看,我觉得那个页面上列出的公式其实不是Scipy使用的公式(这还不错,因为源代码看起来是用的正常且正确的余弦距离公式)。不过,在生成链接的时候,链接中明显有一些负值,不知道是什么原因。你可以试着用scipy.spatial.distance.pdist()来计算你向量之间的距离,方法设置为'cosine',然后检查一下是否有负值。如果没有负值,那就说明问题出在使用这些距离值生成链接的方式上。

9

这是因为浮点数不准确的问题,所以你的一些向量之间的距离,可能不是0,而是比如说-0.000000000000000002。你可以使用 scipy.clip() 函数来解决这个问题。如果你的距离矩阵叫 dmatr,那么可以用 numpy.clip(dmatr,0,1,dmatr) 这个方法,这样就没问题了。

撰写回答