Список недиагональных значений из матрицы расстояния Левенштейна - PullRequest
0 голосов
/ 05 сентября 2018

Используя следующие данные, как я могу создать DataFrame со столбцом id в качестве индекса и вторым столбцом, содержащим список недиагональных значений из матрицы расстояний Левенштейна для списка строк, соответствующих каждому идентификатору?

d = {'id':[1,1,1,2,2],'string':['roundys','roundys','ppg','brewers','cubs']}
df = pd.DataFrame(data=d)

Цель состоит в том, чтобы создать DataFrame, похожий на

df_diag = pd.DataFrame({'id':[1,2],'diag_val':['0.0,7.0,7.0','6.0']})

Я построил некоторые грубые фрагменты, которые работают с одним списком, но не смогли выполнить итерацию по 'id' в нескольких списках. Я использую панд как 'pd', numpy как 'np' и расстояние от Левенштейна как 'dist'

Шаг 1 Создание списка тестов

aTest = ['roundys','roundys','ppg']

Шаг 2 Создать функцию, которая возвращает Редактировать матрицу расстояний из aTest

def editDistance(list_o_strings):
    matrix = np.zeros(shape = (len(list_o_strings),len(list_o_strings)))

    for i in range(len(list_o_strings)):
        for j in range(i, len(list_o_strings)):
            matrix[i][j] = dist(list_o_strings[i],list_o_strings[j])
    for i in range(0, len(list_o_strings)):
        for j in range(0,len(list_o_strings)):
            if i == j:
                matrix[i][j] = 0
            elif i > j:
                matrix[i][j] = matrix[j][i]
    return matrix

Шаг 3 Создать функцию, которая возвращает недиагональные значения для редактирования расстояния

def selectElements(matrix):
    ws = []
    for i in range(0, matrix.shape[0]):
        for j in range(0, matrix.shape[1]):
            if i <> j and i>j:
                ws.append(matrix[i,j])
    return ws 

Шаг 4 Проверка списка примеров

testDistance = editDistance(aTest)
testOffDiag = selectElements(testDistance)

Мой следующий шаг - перебрать функции по уникальным значениям id в наборе данных. Я создал новый фрейм данных id в паре со списком строк с

df1 = df.groupby('id').agg(lambda x: ','.join(x))

Мои попытки сделать так, чтобы функции циклически проходили через термины id, потерпели неудачу, какие-либо предложения?

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

У Сципи есть функция scipy.spatial.distance.pdist, которая позволяет вам вычислять попарные расстояния между элементами в n-мерном пространстве. Функция также позволяет использовать пользовательский параметр metric.

Мы можем передать эту функцию вашим значениям вместе с метрической функцией, рассчитанной с использованием библиотеки python-Levenshtein.

Настройка

from Levenshtein import distance
from scipy.spatial.distance import pdist

Использование pdist с пользовательской метрикой:

def lm(x):
    return pdist(x.values.reshape(-1, 1), lambda x,y: distance(x[0],y[0]))

res = pd.DataFrame(
    [(i, lm(g)) for i, g in df.groupby('id').string],
    columns=['id', 'diag_val']
)

   id         diag_val
0   1  [0.0, 7.0, 7.0]
1   2            [6.0]
0 голосов
/ 05 сентября 2018

Вы можете получить расстояние Левенштейна с pip установкой

pip install python-Levenshtein

Тогда вы можете сделать что-то вроде этого

from Levenshtein import distance
from itertools import combinations

def lm(a):
  return [distance(*b) for b in combinations(a, 2)]

df.groupby('id').string.apply(lm).reset_index(name='diag_val')

   id   diag_val
0   1  [0, 7, 7]
1   2        [6]

Или

def lm(a):
  return ','.join([str(distance(*b)) for b in combinations(a, 2)])

df.groupby('id').string.apply(lm).reset_index(name='diag_val')

   id diag_val
0   1    0,7,7
1   2        6
...