Как применить сложную лямбда-функцию в Pandas DataFrame с длинным списком элементов в строке - PullRequest
0 голосов
/ 24 мая 2019

У меня есть пандас DataFrame, в котором у меня есть длинная строка для каждой строки в одном столбце (см. Переменную 'dframe').В отдельном списке я сохранил все ключевые слова, которые я должен сравнивать с каждым словом из каждой строки из DataFrame.Если ключевое слово найдено, я должен сохранить его как успешный и отметить, в каком предложении оно было найдено.Я использую сложный цикл for с несколькими операторами if, который дает мне правильный вывод, но он не очень эффективен.На весь мой набор уходит почти 4 часа, где у меня есть 130 ключевых слов и тысячи строк для итерации.

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

import pandas as pd
from fuzzywuzzy import fuzz


dframe = pd.DataFrame({ 'Email' : ['this is a first very long e-mail about fraud and money',
                           'this is a second e-mail about money',
                           'this would be a next message where people talk about secret information',
                           'this is a sentence where someone misspelled word frad',
                           'this sentence has no keyword']})

keywords = ['fraud','money','secret']


keyword_set = set(keywords)

dframe['Flag'] = False
dframe['part_word'] = 0
output = []


for k in range(0, len(keywords)):
    count_ = 0
    dframe['Flag'] = False
    for j in range(0, len(dframe['Email'])):
        row_list = []
        print(str(k) + '  /  ' + str(len(keywords)) + '  ||  ' +  str(j) + '  /  ' + str(len(dframe['Email'])))
        for i in dframe['Email'][j].split():
            if dframe['part_word'][j] != 0 :
                row_list = dframe['part_word'][j]


            fuz_part = fuzz.partial_ratio(keywords[k].lower(),i.lower())
            fuz_set = fuzz.token_set_ratio(keywords[k],i)

            if ((fuz_part > 90) | (fuz_set > 85)) & (len(i) > 3):
                if keywords[k] not in row_list:
                    row_list.append(keywords[k])
                    print(keywords[k] + '  found as :  ' + i)
                dframe['Flag'][j] = True
                dframe['part_word'][j] = row_list


    count_ = dframe['Flag'].values.sum()
    if count_ > 0:

        y = keywords[k] + ' ' + str(count_)
        output.append(y)
    else:
        y = keywords[k] + ' ' + '0'
        output.append(y)          

Может быть, кто-то, кто имеет опыт работы с лямбда-функциями, мог бы подсказать, как я могу применить его на моем DataFrame для выполнения аналогичной операции?Потребуется как-то применить нечеткое совпадение в лямбда-выражении после разделения целого предложения на строку для разделения слов и выбора значения с наибольшим значением соответствия с условием, что оно должно быть больше 85 или 90. Это то, с чем я запутался.Заранее спасибо за любую помощь.

1 Ответ

0 голосов
/ 24 мая 2019

У меня нет лямбда-функции для вас, но функция, которую вы можете применить к dframe.Email:

import pandas as pd
from fuzzywuzzy import fuzz

Сначала создайте тот же пример данных, что и вы:

dframe = pd.DataFrame({ 'Email' : ['this is a first very long e-mail about fraud and money',
                       'this is a second e-mail about money',
                       'this would be a next message where people talk about secret information',
                       'this is a sentence where someone misspelled word frad',
                       'this sentence has no keyword']})

keywords = ['fraud','money','secret']

Эта функция применяется:

def fct(sntnc, kwds):
    mtch = []
    for kwd in kwds:
        fuz_part = [fuzz.partial_ratio(kwd.lower(), w.lower()) > 90 for w in sntnc.split()]
        fuz_set = [fuzz.token_set_ratio(kwd, w) > 85 for w in sntnc.split()]
        bL = [len(w) > 3 for w in sntnc.split()]
        mtch.append(any([(p | s) & l for p, s, l in zip(fuz_part, fuz_set, bL)]))
    return mtch

Для каждого ключевого слова он вычисляет fuz_part > 90 для всех слов в предложении, то же самое с fuz_set > 85 и то же самое с wordlength > 3. И, наконец, для каждого ключевого слова оно сохраняется в списке, если во всех словах предложения есть ((fuz_part > 90) | (fuz_set > 85)) & (wordlength > 3).

И как это применяется и как создается результат:

s = dframe.Email.apply(fct, kwds=keywords)
s = s.apply(pd.Series).set_axis(keywords, axis=1, inplace=False)
dframe = pd.concat([dframe, s], axis=1)

Результат:

result = dframe.drop('Email', 1)
#    fraud  money  secret
# 0   True   True   False                                    
# 1  False   True   False                                     
# 2  False  False    True                                    
# 3   True  False   False                                     
# 4  False  False   False              

result.sum()
# fraud     2
# money     2                                           
# secret    1                                           
# dtype: int64                         
...