Как использовать sklearn.metrics.pairwise pairwise_distances с вызываемой метрикой? - PullRequest
0 голосов
/ 17 декабря 2018

Я делаю некоторый анализ поведения, где отслеживаю поведение с течением времени и затем создаю n-грамм этого поведения.

sample_n_gram_list = [['scratch', 'scratch', 'scratch', 'scratch', 'scratch'],
                      ['scratch', 'scratch', 'scratch', 'scratch', 'smell/sniff'],
                      ['scratch', 'scratch', 'scratch', 'sit', 'stand']]

Я хочу иметь возможность кластеризовать эти n-граммы, но мне нужносоздать предварительно вычисленную матрицу расстояний, используя пользовательскую метрику.Кажется, моя метрика работает нормально, но когда я пытаюсь создать матрицу расстояний с помощью функции sklearn, я получаю ошибку:

ValueError: could not convert string to float: 'scratch'

Я посмотрел документацию https://scikit -learn.org / stable / modules / генерируется / sklearn.metrics.pairwise_distances.html и по этому вопросу не совсем ясно.

Кто-нибудь знает, как правильно использовать это?


Полный код ниже:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
import math
import hashlib 
import networkx as nx
import itertools
import hdbscan
from sklearn.metrics.pairwise import pairwise_distances

def get_levenshtein_distance(path1, path2):
    """
    https://en.wikipedia.org/wiki/Levenshtein_distance
    :param path1:
    :param path2:
    :return:
    """
    matrix = [[0 for x in range(len(path2) + 1)] for x in range(len(path1) + 1)]

    for x in range(len(path1) + 1):
        matrix[x][0] = x
    for y in range(len(path2) + 1):
        matrix[0][y] = y

    for x in range(1, len(path1) + 1):
        for y in range(1, len(path2) + 1):
            if path1[x - 1] == path2[y - 1]:
                matrix[x][y] = min(
                    matrix[x - 1][y] + 1,
                    matrix[x - 1][y - 1],
                    matrix[x][y - 1] + 1
                )
            else:
                matrix[x][y] = min(
                    matrix[x - 1][y] + 1,
                    matrix[x - 1][y - 1] + 1,
                    matrix[x][y - 1] + 1
                )

    return matrix[len(path1)][len(path2)]

sample_n_gram_list = [['scratch', 'scratch', 'scratch', 'scratch', 'scratch'],
                      ['scratch', 'scratch', 'scratch', 'scratch', 'smell/sniff'],
                      ['scratch', 'scratch', 'scratch', 'sit', 'stand']]

print("should be 0")
print(get_levenshtein_distance(sample_n_gram_list[1],sample_n_gram_list[1]))
print("should be 1")
print(get_levenshtein_distance(sample_n_gram_list[1],sample_n_gram_list[0]))
print("should be 2")
print(get_levenshtein_distance(sample_n_gram_list[0],sample_n_gram_list[2]))

clust_number = 2
distance_matrix = pairwise_distances(sample_n_gram_list, metric=get_levenshtein_distance)
clusterer = hdbscan.HDBSCAN(metric='precomputed')
clusterer.fit(distance_matrix)
clusterer.labels_

1 Ответ

0 голосов
/ 17 декабря 2018

Это потому, что pairwise_distances в sklearn предназначен для работы с числовыми массивами (так что все различные встроенные функции расстояния могут работать должным образом), но вы передаете ему список строк.Если вы можете преобразовать строки в числа (закодировать строку в конкретное число) и затем передать ее, она будет работать правильно.

Быстрый быстрый способ сделать это:

# Get all the unique strings in the input data
uniques = np.unique(sample_n_gram_list)
# Output:
# array(['scratch', 'sit', 'smell/sniff', 'stand'])

# Encode the strings to numbers according to the indices in "uniques" array
X = np.searchsorted(uniques, sample_n_gram_list)

# Output:
# array([[0, 0, 0, 0, 0],    <= scratch is assigned 0, sit = 1 and so on
         [0, 0, 0, 0, 2],
         [0, 0, 0, 1, 3]])


# Now this works
distance_matrix = pairwise_distances(X, metric=get_levenshtein_distance)

# Output
# array([[0., 1., 2.],
         [1., 0., 2.],
         [2., 2., 0.]])
...