Tak więc, patrzę na wdrażanie dopasowywania logiki Fuzzy w mojej firmie i problemy z uzyskaniem dobrych wyników. Na początek próbuję dopasować nazwy firm do tych na liście dostarczonej przez inne firmy.
Moja pierwsza próba polegała na użyciu soundex, ale wygląda na to, że soundex porównuje tylko kilka pierwszych dźwięków w nazwie firmy, więc dłuższe nazwy firm były dla siebie zbyt łatwo pogmatwane.
Pracuję teraz nad drugą próbą przy użyciu porównania odległości w levensteinie. Wygląda obiecująco, zwłaszcza jeśli najpierw usunę interpunkcję. Jednak nadal nie mogę znaleźć duplikatów bez zbyt wielu fałszywych alarmów.
Jednym z problemów, które mam, są firmy takie jak widgetsco vs widgets inc. Tak więc, jeśli porówna się podciągi o długości krótszej nazwy, pobieram też rzeczy takie jak BBC University i kampus Uniwersytetu CBC. Podejrzewam, że rozwiązaniem może być wynik wykorzystujący kombinację odległości i najdłuższego wspólnego podciągu.
Czy ktoś zdołał zbudować algorytm, który wykonuje takie dopasowanie z ograniczonymi fałszywymi pozytywami?Logika rozmyta pasująca do
Odpowiedz
Chcesz użyć czegoś takiego jak Odległość Levenshteina lub inny algorytm porównywania ciągów znaków. Możesz rzucić okiem na ten projekt na Codeplex.
Podoba mi się odległość Levenshteina, ale problemem jest to, że nie jest "rozmyta". Odległość między "CBC University" i "CBC Univer" wynosi 4, mimo że prawdopodobnie to samo miejsce.Ten post SO ma ładne "rozmyte" Levenshtein [SQL Server Fuzzy Search z Procentem dopasowania] (http://stackoverflow.com/questions/26259117/sql-server-fuzzy-search-with-percentage-match) – user918967
Używasz dostęp? Jeśli tak, rozważ znak "*" bez cudzysłowów. Jeśli używasz programu SQL Server, użyj znaku "%". Jednak to naprawdę nie jest logika rozmyta, to tak naprawdę operator Like. Jeśli naprawdę potrzebujesz logiki rozmytej, wyeksportuj zestaw danych do Excela i załaduj AddIn z poniższego adresu URL.
https://www.microsoft.com/en-us/download/details.aspx?id=15011
Czytaj uważnie instrukcje. To na pewno działa i działa świetnie, ale musisz postępować zgodnie z instrukcjami i nie jest to całkowicie intuicyjne. Za pierwszym razem, kiedy go wypróbowałem, nie postępowałem zgodnie z instrukcjami i zmarnowałem dużo czasu, próbując zmusić go do działania. W końcu zorientowałem się, i działało świetnie !!
Mieliśmy dobre wyniki w dopasowywaniu nazw i adresów przy użyciu funkcji Metaphone stworzonej przez Lawrence Philipsa. Działa to podobnie jak Soundex, ale tworzy wzór dźwiękowy/spółgłoskowy dla całej wartości. Może się okazać, że jest to przydatne w połączeniu z innymi technikami, szczególnie jeśli możesz usunąć trochę puchu jak "co". i "inc." jak wspomniano w innych komentarzach:
create function [dbo].[Metaphone](@str as nvarchar(70), @KeepNumeric as bit = 0)
returns nvarchar(25)
/*
Metaphone Algorithm
Created by Lawrence Philips.
Metaphone presented in article in "Computer Language" December 1990 issue.
*********** BEGIN METAPHONE RULES ***********
Lawrence Philips' RULES follow:
The 16 consonant sounds:
|--- ZERO represents "th"
|
B X S K J T F H L M N P R 0 W Y
Drop vowels
Exceptions:
Beginning of word: "ae-", "gn", "kn-", "pn-", "wr-" ----> drop first letter
Beginning of word: "wh-" ----> change to "w"
Beginning of word: "x" ----> change to "s"
Beginning of word: vowel or "H" + vowel ----> Keep it
Transformations:
B ----> B unless at the end of word after "m", as in "dumb", "McComb"
C ----> X (sh) if "-cia-" or "-ch-"
S if "-ci-", "-ce-", or "-cy-"
SILENT if "-sci-", "-sce-", or "-scy-"
K otherwise
K "-sch-"
D ----> J if in "-dge-", "-dgy-", or "-dgi-"
T otherwise
F ----> F
G ----> SILENT if "-gh-" and not at end or before a vowel
"-gn" or "-gned"
"-dge-" etc., as in above rule
J if "gi", "ge", "gy" if not double "gg"
K otherwise
H ----> SILENT if after vowel and no vowel follows
or "-ch-", "-sh-", "-ph-", "-th-", "-gh-"
H otherwise
J ----> J
K ----> SILENT if after "c"
K otherwise
L ----> L
M ----> M
N ----> N
P ----> F if before "h"
P otherwise
Q ----> K
R ----> R
S ----> X (sh) if "sh" or "-sio-" or "-sia-"
S otherwise
T ----> X (sh) if "-tia-" or "-tio-"
0 (th) if "th"
SILENT if "-tch-"
T otherwise
V ----> F
W ----> SILENT if not followed by a vowel
W if followed by a vowel
X ----> KS
Y ----> SILENT if not followed by a vowel
Y if followed by a vowel
Z ----> S
*/
as
begin
declare @Result varchar(25)
,@str3 char(3)
,@str2 char(2)
,@str1 char(1)
,@strp char(1)
,@strLen tinyint
,@cnt tinyint
set @strLen = len(@str)
set @cnt = 0
set @Result = ''
-- Preserve first 5 numeric values when required
if @KeepNumeric = 1
begin
set @Result = case when isnumeric(substring(@str,1,1)) = 1
then case when isnumeric(substring(@str,2,1)) = 1
then case when isnumeric(substring(@str,3,1)) = 1
then case when isnumeric(substring(@str,4,1)) = 1
then case when isnumeric(substring(@str,5,1)) = 1
then left(@str,5)
else left(@str,4)
end
else left(@str,3)
end
else left(@str,2)
end
else left(@str,1)
end
else ''
end
set @str = right(@str,len(@str)-len(@Result))
end
--Process beginning exceptions
set @str2 = left(@str,2)
if @str2 = 'wh'
begin
set @str = 'w' + right(@str , @strLen - 2)
set @strLen = @strLen - 1
end
else
if @str2 in('ae', 'gn', 'kn', 'pn', 'wr')
begin
set @str = right(@str , @strLen - 1)
set @strLen = @strLen - 1
end
set @str1 = left(@str,1)
if @str1 = 'x'
set @str = 's' + right(@str , @strLen - 1)
else
if @str1 in ('a','e','i','o','u')
begin
set @str = right(@str, @strLen - 1)
set @strLen = @strLen - 1
set @Result = @Result + @str1
end
while @cnt <= @strLen
begin
set @cnt = @cnt + 1
set @str1 = substring(@str,@cnt,1)
set @strp = case when @cnt <> 0
then substring(@str,(@cnt-1),1)
else ' '
end
-- Check if the current character is the same as the previous character.
-- If we are keeping numbers, only compare non-numeric characters.
if case when @KeepNumeric = 1 and @strp = @str1 and isnumeric(@str1) = 0 then 1
when @KeepNumeric = 0 and @strp = @str1 then 1
else 0
end = 1
continue -- Skip this loop
set @str2 = substring(@str,@cnt,2)
set @Result = case when @KeepNumeric = 1 and isnumeric(@str1) = 1
then @Result + @str1
when @str1 in('f','j','l','m','n','r')
then @Result + @str1
when @str1 = 'q'
then @Result + 'k'
when @str1 = 'v'
then @Result + 'f'
when @str1 = 'x'
then @Result + 'ks'
when @str1 = 'z'
then @Result + 's'
when @str1 = 'b'
then case when @cnt = @strLen
then case when substring(@str,(@cnt - 1),1) <> 'm'
then @Result + 'b'
else @Result
end
else @Result + 'b'
end
when @str1 = 'c'
then case when @str2 = 'ch' or substring(@str,@cnt,3) = 'cia'
then @Result + 'x'
else case when @str2 in('ci','ce','cy') and @strp <> 's'
then @Result + 's'
else @Result + 'k'
end
end
when @str1 = 'd'
then case when substring(@str,@cnt,3) in ('dge','dgy','dgi')
then @Result + 'j'
else @Result + 't'
end
when @str1 = 'g'
then case when substring(@str,(@cnt - 1),3) not in ('dge','dgy','dgi','dha','dhe','dhi','dho','dhu')
then case when @str2 in('gi', 'ge','gy')
then @Result + 'j'
else case when @str2 <> 'gn' or (@str2 <> 'gh' and @cnt+1 <> @strLen)
then @Result + 'k'
else @Result
end
end
else @Result
end
when @str1 = 'h'
then case when @strp not in ('a','e','i','o','u') and @str2 not in ('ha','he','hi','ho','hu')
then case when @strp not in ('c','s','p','t','g')
then @Result + 'h'
else @Result
end
else @Result
end
when @str1 = 'k'
then case when @strp <> 'c'
then @Result + 'k'
else @Result
end
when @str1 = 'p'
then case when @str2 = 'ph'
then @Result + 'f'
else @Result + 'p'
end
when @str1 = 's'
then case when substring(@str,@cnt,3) in ('sia','sio') or @str2 = 'sh'
then @Result + 'x'
else @Result + 's'
end
when @str1 = 't'
then case when substring(@str,@cnt,3) in ('tia','tio')
then @Result + 'x'
else case when @str2 = 'th'
then @Result + '0'
else case when substring(@str,@cnt,3) <> 'tch'
then @Result + 't'
else @Result
end
end
end
when @str1 = 'w'
then case when @str2 not in('wa','we','wi','wo','wu')
then @Result + 'w'
else @Result
end
when @str1 = 'y'
then case when @str2 not in('ya','ye','yi','yo','yu')
then @Result + 'y'
else @Result
end
else @Result
end
end
return @Result
end
Jedyne dopasowanie, jakie kiedykolwiek miałem do wykonania, polegało na dopasowaniu podłańcucha np. BBC Uni i BBC University. Zakłada się, że początkowa część łańcucha jest taka sama wśród wszystkich duplikatów. Dla przypadków takich jak: widgets inc, najpierw rozpakowałbym skróty typu jednostki. Jeśli masz inne powiązane dane, np. Adres firmy, dyrektor generalny firmy, to również można porównać, aby uzyskać wynik prawdopodobieństwa, że dwie firmy są takie same. Skoncentruję się najpierw na normalizacji danych (poprzez usunięcie puchu), zanim spróbuję to porównać. – Alex
Usuwanie puchu jest zwykle dobrą praktyką, niezależnie od używanego algorytmu. – Yobik
Będę pracował nad normalizacją danych. W przypadkach takich jak "Smith & Wesson" dopasowanych do "Smith and Wesson", zamieniłbym wszystkie "&" na "i" dla celów pojedynku. Próbowałbym również rozebrać typy encji i ich skróty (np. Co.) I sprawdzić, czy to daje fałszywe alarmy. Jeśli dane mają bardziej złożone konstrukty, np. "CompanyA trading jako CompanyB", następnie trzeba je przeanalizować. IMO, dopasowywanie dobrze uporządkowanych danych, takich jak nazwy firm, jest niewłaściwym narzędziem do tego zadania. – Alex