Ускорить соответствие строк python - PullRequest
3 голосов
/ 31 января 2020

У меня есть 2 разных Dataframes, для которых я пытаюсь сопоставить строки столбцы (имена)

Ниже приведены лишь некоторые примеры DF

df1 (127000,3)
Code     Name     PostalCode
150      Maarc    47111
250      Kirc     41111
170      Moic     42111
140      Nirc     44111
550      Lacter   47111

df2 (38000,3)
Code     NAME     POSTAL_CODE
150      Marc     47111
250      Kikc     41111
170      Mosc     49111
140      NiKc     44111
550      Lacter   47111

Цель состоит в том, чтобы создать еще один DF3 как показано ниже

Code     NAME    Best Match   Score
150      Marc    Maarc        0.9
250      Karc    Kirc         0.9

Следующий код дает ожидаемый результат

import difflib
from functools import partial
f = partial(difflib.get_close_matches, possibilities= df1['Name'].tolist(), n=1)

matches = df2['NAME'].map(f).str[0].fillna('')

scores = [difflib.SequenceMatcher(None, x, y).ratio()
          for x, y in zip(matches, df2['NAME'])]

df3 = df2.assign(best=matches, score=scores)
df3.sort_values(by='score')

Задача

Чтобы сопоставить эти строки только для 2 строк, занимает около 30 с c. Эта задача должна быть выполнена для 1К строк, что займет несколько часов!

Вопрос

Как я могу ускорить код? Я думал о чем-то вроде fetchall?

РЕДАКТИРОВАТЬ

Были опробованы даже нечеткие библиотеки, что занимает больше времени, чем difflib со следующим кодом:

from fuzzywuzzy import fuzz

def get_fuzz(df, w):
    s = df['Name'].apply(lambda y: fuzz.token_set_ratio(y, w))
    idx = s.idxmax()
    return {'Name': df['Name'].iloc[idx], 'CODE': df['Code'].iloc[idx], 'Value': s.max()}

df2['NAME'].apply(lambda x: get_fuzz(df1, x))

df2 = df2.assign(search= df2['NAME'].apply(lambda x: get_fuzz(df1, x)))

Ответы [ 2 ]

1 голос
/ 09 марта 2020

Таким образом, я смог ускорить шаг сопоставления, используя столбец почтового индекса в качестве дискриминанта. Я смог перейти от 1 часа 40 минут до 7 минут вычислений.

Ниже приведен лишь пример кода DF

df1 (127000,3)
Code     Name     PostalCode
150      Maarc    47111
250      Kirc     41111
170      Moic     42111
140      Nirc     44111
550      Lacter   47111

df2 (38000,3)
Code     NAME     POSTAL_CODE
150      Marc     47111
250      Kikc     41111
170      Mosc     49111
140      NiKc     44111
550      Lacter   47111

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

%%time
import difflib
from functools import partial

def difflib_match (df1, df2, set_nan = True):

    # Fill NaN
    df2['best']= np.nan
    df2['score']= np.nan

    # Apply function to retrieve unique first letter of Name's column
    first= df2['POSTAL_CODE'].unique()

    # Loop over each first letter to apply the matching by starting with the same Postal code for both DF
    for m, letter in enumerate(first):

        # IF Divid by 100, print Unique values processed 
        if m%100 == 0:
            print(m, 'of', len(first))

        df1_first= df1[df1['PostalCode'] == letter]
        df2_first= df2[df2['POSTAL_CODE'] == letter]

        # Function to match using the Name column from the Web                   
        f = partial(difflib.get_close_matches, possibilities= df1_first['Name'].tolist(), n=1) 

        # Define which columns to compare while mapping with first letter
        matches = df2_first['NAME'].map(f).str[0].fillna('')

        # Retrieve the best score for each match
        scores = [difflib.SequenceMatcher(None, x, y).ratio()
              for x, y in zip(matches, df2_first['NAME'])]

        # Assign the result to the DF
        for i, name in enumerate(df2_first['NAME']):
            df2['best'].where(df2['NAME'] != name, matches.iloc[i], inplace = True)
            df2['score'].where(df2['NAME'] != name, scores[i], inplace = True)

    return df2

# Apply Function
df_diff= difflib_match(df1, df2)

# Display DF
print('Shape: ', df_diff.shape)
df_diff.head()
0 голосов
/ 31 января 2020

Самый быстрый способ найти подходящую строку - использовать Regex.

Это языковая схема поиска для поиска совпадений в строке.

Пример можно посмотреть здесь:

import re

txt = "The rain in Spain"
x = re.search("^The.*Spain$", txt)

//Outputs: x == true

* Взят с: https://www.w3schools.com/python/python_regex.asp

Поскольку я ничего не понимаю в Dataframe, я не знаю, как реализовать Regex в вашем коде, но я надеюсь, что функция Regex может вам помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...