Сопоставление нечетких строк Python в виде таблицы/матрицы стиля корреляции

У меня есть файл с x количеством строковых имен и связанных с ними идентификаторов. По сути два столбца данных.

Что бы я хотел, так это таблицу стиля корреляции с форматом x x (имея данные, о которых идет речь, как по оси x, так и по оси y), но вместо корреляции мне бы хотелось, чтобы функция библиотеки fuzzywuzzy fuzz.ratio(x ,y) в качестве вывода, используя имена строк в качестве ввода. По сути, запуск каждой записи против каждой записи.

Это как раз то, что я имел в виду. Просто чтобы показать мое намерение:

import pandas as pd
from fuzzywuzzy import fuzz

df = pd.read_csv('random_data_file.csv')

df = df[['ID','String']]
df['String_Dup'] = df['String'] #creating duplicate of data in question
df = df.set_index('ID')

df = df.groupby('ID')[['String','String_Dup']].apply(fuzz.ratio())

Но очевидно, что этот подход не работает для меня в данный момент. Любая помощь приветствуется. Это не обязательно должны быть панды, это просто среда, с которой я относительно лучше знаком.

Я надеюсь, что моя проблема ясно сформулирована, и действительно, любой вклад ценится,


person WayOutofDepth    schedule 12.11.2018    source источник


Ответы (3)


Используйте функцию pandas crosstab, а затем столбец apply для вычисления пух. Это значительно элегантнее, чем мой первый ответ.

import pandas as pd
from fuzzywuzzy import fuzz

# Create sample data frame.
df = pd.DataFrame([(1, 'abracadabra'), (2,'abc'), (3,'cadra'), (4, 'brabra')],
                  columns=['id', 'strings'])
# Create the cartesian product between the strings column with itself.
ct = pd.crosstab(df['strings'], df['strings'])
# Note: for pandas versions <0.22, the two series must have different names.
# In case you observe a "Level XX not found" error, the following may help:
# ct = pd.crosstab(df['strings'].rename(), df['strings'].rename())

# Apply the fuzz (column-wise). Argument col has type pd.Series.
ct = ct.apply(lambda col: [fuzz.ratio(col.name, x) for x in col.index])

# This results in the following:
#       strings      abc  abracadabra  brabra  cadra
#       strings
#       abc          100           43      44     25
#       abracadabra   43          100      71     62
#       brabra        44           71     100     55
#       cadra         25           62      55    100

Для простоты я пропустил операцию groupby, как было предложено в вашем вопросе. Если вам нужно применить нечеткое сопоставление строк к группам, просто создайте отдельную функцию:

def cross_fuzz(df):
    ct = pd.crosstab(df['strings'], df['strings'])
    ct = ct.apply(lambda col: [fuzz.ratio(col.name, x) for x in col.index])
    return ct

df.groupby('id').apply(cross_fuzz)
person normanius    schedule 12.11.2018
comment
Спасибо! Это работает до тех пор, пока я не пытаюсь сопоставить одну и ту же серию. то есть мне пришлось создать копию «строк» ​​и назвать ее как-то иначе, иначе это дало мне ошибку «строки уровня не найдены». Я рад принять это как ответ в противном случае. - person WayOutofDepth; 12.11.2018
comment
Приведенный выше код работает для панд 0.22.0. Но вы правы, предыдущие версии панд (например, 0.20.3) требовали, чтобы у серии были разные имена. Я отредактировал свой ответ соответственно. Спасибо за указание на это. - person normanius; 12.11.2018

В pandas декартово перекрестное произведение между двумя столбцами может быть создано с использованием фиктивной переменной и pd.merge. Операция fuzz применяется с использованием apply. Последняя операция поворота извлечет формат, который вы имели в виду. Для простоты я опустил операцию groupby, но, конечно, вы можете применить процедуру ко всем групповым таблицам, вынеся приведенный ниже код в отдельную функцию.

Вот как это может выглядеть:

import pandas as pd
from fuzzywuzzy import fuzz

# Create sample data frame.
df = pd.DataFrame([(1, 'abracadabra'), (2,'abc'), (3,'cadra'), (4, 'brabra')],
                  columns=['id', 'strings'])

# Cross product, using a temporary column.
df['_tmp'] = 0
mrg = pd.merge(df, df, on='_tmp', suffixes=['_1','_2'])

# Apply the function between the two strings.
mrg['fuzz'] = mrg.apply(lambda s: fuzz.ratio(s['strings_1'], s['strings_2']), axis=1)

# Reorganize data.
ret = mrg.pivot(index='strings_1', columns='strings_2', values='fuzz')
ret.index.name = None 
ret.columns.name = None

# This results in the following:
#              abc  abracadabra  brabra  cadra
# abc          100           43      44     25
# abracadabra   43          100      71     62
# brabra        44           71     100     55
# cadra         25           62      55    100
person normanius    schedule 12.11.2018

import csv
from fuzzywuzzy import fuzz
import numpy as np  

input_file = csv.DictReader(open('random_data_file.csv')) 
string = []
for row in input_file: #file is appended row by row into a python dictionary
    string.append(row["String"]) #keys for the dict. are the headers



#now you have a list of the string values
length = len(string)
resultMat = np.zeros((length, length)) #zeros 2D matrix, with size X * X

for i in range (length):
    for j in range (length):
        resultMat[i][j] = fuzz.ratio(string[i], string[j])

print resultMat

Я выполнил реализацию в двумерной матрице numby. Я не очень хорошо разбираюсь в pandas, но я думаю, что вы добавляли еще один столбец и сравнивали его со строковым столбцом, что означает: string[i] будет сопоставляться с string_dub[i], все результаты будут 100

Надеюсь, поможет

person Hossam26644    schedule 12.11.2018