【问题标题】:Scipy Negative Distance? What?Scipy负距离?什么?
【发布时间】:2016-02-23 16:58:48
【问题描述】:

我有一个输入文件,其中包含小数点后 4 位的浮点数:

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

(第一个是id)。 我的班级使用loadVectorsFromFile 方法将其乘以10000,然后int() 这些数字。最重要的是,我还循环遍历每个向量以确保内部没有负值。但是,当我执行_hclustering 时,我不断看到错误"LinkageZcontains negative values"

我真的认为这是一个错误,因为:

  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()):

【问题讨论】:

  • 我在 Scipy 中遇到了这个问题——意外的负值。问题(对我来说)是我不知道 Scipy 中的三角函数默认情况下期望弧度值。

标签: python scipy


【解决方案1】:

这是因为浮点数不准确,因此向量之间的某些距离(而不是 0)是例如 -0.000000000000000002。使用scipy.clip() 函数更正问题。如果你的距离矩阵是dmatr,使用numpy.clip(dmatr,0,1,dmatr)应该没问题。

【讨论】:

    【解决方案2】:

    我很确定这是因为您在调用 fclusterdata 时使用的是余弦度量。尝试使用欧几里得,看看错误是否消失。

    如果您的集合中两个向量的点积大于 1,余弦度量可能会变为负数。由于您使用非常大的数字并对其进行归一化,我很确定点积大于 1 很多数据集中的时间。如果您想使用余弦度量,那么您需要对数据进行归一化,以使两个向量的点积永远不会大于 1。请参阅this page 上的公式以了解余弦度量在 Scipy 中的定义.

    编辑:

    好吧,通过查看源代码,我认为该页面上列出的公式实际上并不是 Scipy 使用的公式(这很好,因为源代码看起来使用的是正常且正确的余弦距离公式) .但是,当它创建链接时,无论出于何种原因,链接中显然存在一些负值。尝试使用 scipy.spatial.distance.pdist() 和 method='cosine' 查找向量之间的距离并检查负值。如果没有,则与使用距离值如何形成链接有关。

    【讨论】:

    • 很好的答案。关于“规范化您的数据”,我在规范化我的数据方面有哪些选择,以便我仍然可以在 scipy 中使用本机的余弦距离?我尝试在没有任何形式的规范化的情况下进行计算(仅使用本机 tfidf 值)。不用说,这个问题仍然存在,因为以如此长的长度添加的浮点数的不准确性。你会推荐我做什么?
    • 首先,您应该检查一下问题出在哪里。是在计算距离之后吗?如果余弦方法正确完成(尽管文档另有说明,我认为现在是正确的),则不需要规范化。顺便说一句,尝试使用“old_cosine”作为您的指标,看看是否仍然出现错误。
    【解决方案3】:

    “链接 Z 包含负值”。当链接矩阵中的任何链接集群索引分配为-1时,此错误也会发生在 scipy 层次聚类过程中。

    根据我的观察,在组合过程中,当所有要组合的集群对或点之间的距离为负无穷大时,任何链接集群索引都会被指定为 -1。因此,即使它们之间的链接距离是无限的,链接函数也会将集群与它们结合起来。并分配一个簇或点负索引

    总结 所以重点是,如果您使用cosine distance 作为度量,并且如果任何数据点的范数或幅度为零,则会发生此错误

    【讨论】:

      【解决方案4】:

      我有同样的问题。你可以做的是重写余弦函数。 例如:

      from sklearn.metrics.pairwise import cosine_similarity
      def mycosine(x1, x2):
          x1 = x1.reshape(1,-1)
          x2 = x2.reshape(1,-1)
          ans = 1 - cosine_similarity(x1, x2)
          return max(ans[0][0], 0)
      

      ...

      clusters = hierarchy.fclusterdata(data, threshold, criterion='distance', metric=mycosine, method='average')
      

      【讨论】:

        【解决方案5】:

        我无法改进贾斯汀的答案,但另一点需要注意的是您的数据处理。

        您说您执行int( float("0.0003") * 10000 ) 之类的操作来读取数据。但如果你这样做,你得到的不是3,而是2.9999999999999996。那是因为浮点数的误差会成倍增加。

        更好,或者至少更准确。方法是在字符串中进行乘法运算。 也就是说,使用字符串操作从0.00033.0 等等。

        也许在某个地方甚至有一个 Python 数据类型扩展,它可以在不损失精度的情况下读取这种数据,您可以在转换前执行乘法运算。我不熟悉 SciPy/numerics,所以我不知道。

        编辑

        贾斯汀评论说,python 中有一个十进制类型。这可以解释字符串,乘以整数并转换为浮点数(我测试过)。在这种情况下,我建议您更新您的逻辑,例如:

        factor = 1
        if inflate:
          factor = 10000
        scores = map(lambda x: float(decimal.Decimal(x) * factor), l[1:])
        

        这会稍微减少你的舍入问题。

        【讨论】:

        猜你喜欢
        • 2010-12-08
        • 2016-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-13
        • 2020-02-21
        相关资源
        最近更新 更多