Scipy отрицательное расстояние? Какой?

У меня есть входной файл, который содержит числа с плавающей запятой до 4 знаков после запятой:

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

(первый идентификатор). Мой класс использует метод 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()):

person disappearedng    schedule 07.04.2010    source источник
comment
у меня была эта проблема в Scipy - неожиданные отрицательные значения. Проблема (для меня) заключалась в том, что я не знал, что триггерные функции в Scipy по умолчанию ожидают значения в радианах.   -  person doug    schedule 07.04.2010


Ответы (5)


Это происходит из-за неточности с плавающей запятой, поэтому некоторые расстояния между вашими векторами вместо 0 равны, например, -0,00000000000000000002. Используйте функцию scipy.clip() для устранения проблемы. Если ваша матрица расстояний dmatr, используйте numpy.clip(dmatr,0,1,dmatr), и все будет в порядке.

person dkar    schedule 05.06.2012

Я почти уверен, что это потому, что вы используете метрику косинуса, когда вызываете fclusterdata. Попробуйте использовать euclidean и посмотрите, исчезнет ли ошибка.

Косинусная метрика может стать отрицательной, если скалярное произведение двух векторов в вашем наборе больше 1. Поскольку вы используете очень большие числа и нормализуете их, я почти уверен, что скалярные произведения больше 1 в большинстве случаев. в вашем наборе данных. Если вы хотите использовать косинусную метрику, вам необходимо нормализовать данные таким образом, чтобы скалярное произведение двух векторов никогда не превышало 1. См. формулу на эту страницу, чтобы увидеть, как определяется метрика косинуса в Scipy.

Изменить:

Что ж, глядя на исходный код, я думаю, что формула, указанная на этой странице, на самом деле не является формулой, которую использует Scipy (что хорошо, потому что исходный код выглядит так, как будто он использует нормальную и правильную формулу косинусного расстояния). Однако к тому времени, когда он создает связь, по какой-либо причине в связи явно есть отрицательные значения. Попробуйте найти расстояние между вашими векторами с помощью scipy.spatial.distance.pdist() с методом = 'косинус' и проверьте отрицательные значения. Если их нет, то это связано с тем, как формируется связь с использованием значений расстояния.

person Justin Peel    schedule 07.04.2010
comment
Отличный ответ. Что касается нормализации ваших данных, каковы мои варианты нормализации моих данных, чтобы я все еще мог использовать родное косинусное расстояние в scipy? Я пытался вычислить без какой-либо нормализации (используя только собственные значения tfidf). Излишне говорить, что проблема все еще сохраняется из-за неточностей числа с плавающей запятой, добавляемого при такой большой длине. Что бы вы мне порекомендовали сделать? - person disappearedng; 07.04.2010
comment
Во-первых, вы должны проверить, где проблема. Это после расчета расстояний? Если метод косинуса выполнен правильно (что я думаю, что сейчас это так, несмотря на то, что документация говорит об обратном), то нормализация не требуется. Кстати, попробуйте использовать «old_cosine» в качестве метрики и посмотрите, по-прежнему ли вы получаете ошибку. - person Justin Peel; 07.04.2010

«Связь Z содержит отрицательные значения». Эта ошибка также возникает в процессе иерархической кластеризации scipy, когда любому индексу кластера связи в матрице связи присваивается -1.

По моим наблюдениям, любому индексу кластера связи присваивается значение -1 во время процессов объединения, когда расстояние между всеми парами кластеров или точек для объединения оказывается равным минус бесконечности. Таким образом, функция сцепления объединяет кластеры, даже если расстояние связи между ними равно бесконечности. И назначьте один из кластерных или точечных отрицательных индексов

резюме Итак, дело в том, что если вы используете косинусное расстояние в качестве метрики, и если норма или величина любой точки данных равна нулю, то возникает эта ошибка

person Alok Nayak    schedule 27.06.2015

Я была такая же проблема. Что вы можете сделать, так это переписать функцию косинуса. Например:

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')
person Indira Kurmantayeva    schedule 23.02.2016

Я не могу улучшить ответ Джастина, но еще одним замечанием является ваша обработка данных.

Вы говорите, что делаете что-то вроде int( float("0.0003") * 10000 ) для чтения данных. Но если вы сделаете это, вы получите не 3, а 2.9999999999999996. Это потому, что неточности с плавающей запятой просто умножаются.

Лучше или, по крайней мере, точнее. способом было бы выполнение умножения в строке. То есть, используя манипуляции со строками, чтобы перейти от 0.0003 к 3.0 и так далее.

Возможно, где-то даже есть расширение типа данных Python, которое может считывать такие данные без потери точности, на которых вы можете выполнять умножение перед преобразованием. Я не разбираюсь в SciPy/цифрах, поэтому не знаю.

ИЗМЕНИТЬ

Джастин прокомментировал, что в python есть сборка десятичного типа. И это может интерпретировать строки, умножать на целые числа и преобразовывать в число с плавающей запятой (я проверял это). В этом случае я бы рекомендовал обновить вашу логику, например:

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

Это немного уменьшит ваши проблемы с округлением.

person extraneon    schedule 07.04.2010
comment
Да, есть такой модуль. Он называется десятичным. docs.python.org/library/decimal.html - person Justin Peel; 07.04.2010