2014-01-24 1 views
2

Я использую соотношение difflib для вычисления подобия между 2 строками:Векторизованное/Матричные вычисления между 2 панды dataframe столбцы

ratio = difflib.SequenceMatcher(None, 'string1', 'string2').ratio() 

Выход одно значение с плавающей точкой 0-1, которое можно интерпретировать как счет матча.

То, что я пытаюсь сделать, это создать столбец, который содержит наилучшее соответствие на основе max(ratio) между значением и списком других значений.

Так что если:

df.col1 = 'maria','fred','john' 

и:

df2.col1 = 'mary','orange','maria' 

df.bestmatch будет содержать наиболее подходящий для 'maria', 'fred' and 'john' на основе df2.col1 значений.

Я чувствую, что это возможно с использованием метода .apply, но я просто не могу обернуть вокруг себя, как рассчитать каждое значение в df.col1 против df2.col1.

ОБНОВЛЕНИЕ: метод difflib.get_close_matches смог обработать большие массивы намного лучше и дал мне все, что я хотел, за исключением оценки отношения (не большое дело). Ответ Тома ниже работал для меньших наборов данных, но получил MemoryError, когда каждый столбец был ~ 19 000 значений.

ответ

1

Отредактированный в ответ на Ваш комментарий:

In [164]: df = pd.DataFrame({'col1': ['maria','fred','john'], 'col2': ['mary','orange','maria']}) 

Делает все комбо (Мария, мэри), (мария, оранжевый), (мария, мария), (Фредом ...)

In [165]: combos = itertools.product(df.col1, df.col2) 

combos будет плоский список кортежей, как ('maria', 'mary') ..., 9 в общей сложности. Поскольку нам нужно наилучшее совпадение для каждого имени, нам нужно сгруппировать кортежи по имени от col1.

In [166]: groups = [list(g) for k, g in itertools.groupby(combos, lambda x: x[0])] 

Теперь у нас есть список из трех списков: [[('maria', 'mary'), ('maria', 'orange'), ('maria', 'maria')], [...]]. Второй аргумент groupby - это ключ, который разбивает группы. Проверьте itertools docs.

In [167]: groups 
Out[167]: 
[[('maria', 'mary'), ('maria', 'orange'), ('maria', 'maria')], 
[('fred', 'mary'), ('fred', 'orange'), ('fred', 'maria')], 
[('john', 'mary'), ('john', 'orange'), ('john', 'maria')]] 

Определим вспомогательную функцию:

def get_best(group): 
    k = group[0][0] 
    ratios = {x[1]: difflib.SequenceMatcher(None, *x).ratio() for x in group} 
    winner = max(ratios.iteritems(), key=lambda x: x[1]) 
    return winner[1] # mess with this to return original name, mathcihng name, ratio 

Это функция, которую вы будете применять к каждому из списков в groups. Так же, как перед тем, как мы передадим пару SequenceMatcher, чтобы получить коэффициент. Только теперь нам нужно сохранить имя. Таким образом, в этой функции x есть кортеж, такой как ('maria', 'mary'). Нам нужно знать имя в лучшем совпадении и соотношение наилучшего соответствия, поэтому я бросил их в dict с {name: ratio}. Другое дело, что max принимает второй аргумент. На этот раз это просто говорит, что нужно максимизировать x[1], соотношение.

и получить лучшие матчи:

In [173]: best = [get_best(group) for group in groups] 

In [175]: df['best_match'] = best 

In [176]: df 
Out[176]: 
    col1 col2 best_match 
0 maria mary  maria 
1 fred orange  orange 
2 john maria  orange 

[3 rows x 3 columns] 

Это должно быть довольно эффективным.

+0

Эй, Том, это меня подводит, но то, что мне нужно, - это значение col2 с самым высоким коэффициентом. Так, например, maria будет оцениваться против mary, orange, maria и sam, а затем, основываясь на коэффициенте отношения, maria будет возвращена. Следующий fred будет оцениваться по тем же значениям, и значение с наивысшим результатом будет возвращено и так далее. Имеет ли это смысл? Вероятно, это можно сделать с некоторыми уродливыми вложенными для циклов, но я надеюсь на более элегантное/эффективное решение. – ChrisArmstrong

+0

Ничего себе, это довольно сложно. Нет ли более простого способа? Я чувствую себя некомфортно, используя код, где я не понимаю 90% того, что происходит ... – ChrisArmstrong

+0

Просто добавил некоторые комментарии. Надеюсь, они помогут! Возможно, будет более простой способ, но когда вы нарушаете проблему, я думаю, что все здесь необходимо. Сначала создайте список всех совпадений ('combos'). Затем разбейте этот список по имени из 'df.col1' (' groups'). Наконец, получите имя и соотношение, которое наилучшим образом подходит для каждого имени ('get_best'). строка '[173]' может быть выполнена с помощью 'df.col1.apply (get_best)'. – TomAugspurger

 Смежные вопросы

  • Нет связанных вопросов^_^