Goal:
Look for a substring match within a text for given Keywords. Keep the substring match if the levenshtein distance is smaller than the length of the Keyword divided by (x=10), else return an empty list.
(First of all my apologies if the code is of lower quality. I am quite good in R but very new to Python.)
Current implementation:
from fuzzywuzzy import process
from fuzzysearch import find_near_matches
import math
def fuzzy_extract(qs, ls, threshold, max_dist):
ret = []
for word, _ in process.extractBests(qs, (ls,), score_cutoff = threshold):
for match in find_near_matches(qs, word, max_l_dist = max_dist):
word = word[match.start:match.end]
ret.append({"kw": qs, "word": word, "dist": match.dist})
return(ret)
def get_match(text, keywords, treshold):
keywords = [keyword.lower() for keyword in keywords]
text = text.lower()
candidates = []
for li in [fuzzy_extract(query_string, text, 0, 1) for query_string in keywords]:
if len(li) > 0:
for di in li:
if di["dist"] <= math.ceil(len(di["word"])/treshold):
candidates.append(di)
if(len(candidates) == 0):
return([])
out = str(candidates[0]["kw"])
return(out)
keywords = ["apple", "banana", "cherry"]
text = "nana is Looking for an aple."
print(get_match(text, keywords, 10))
For me the part in the middle looks neither efficient and also not very declarative. With part in the middle i mean the Code following:
for li in [fuzzy_extract(query_string, text, 0, 1) for query_string in keywords]:
I started with something longer but more declarative:
candidates = [fuzzy_extract(query_string, ocr_text, 0, 1) for query_string in keywords_ordering]
lens = [len(candidate) > 0 for candidate in candidates]
candidate_lengths = list(compress(candidates, lens))
filtered = [candidate_length[0]["dist"] < math.ceil(len(candidate_length[0]["word"])/10) for candidate_length in candidate_lengths]
candidate_filtered = list(compress(candidate_lengths, filtered))
In R i would make usage of pipes for These cases to avoid the undeclarative variables in between.
Similar Topics:
Find a best fuzzy match for a string
(Difference is that in this question match candidates are compared to a single word and to a substring of a text).
https://stackoverflow.com/a/36132391/3502164
Custom implementation, rather long.