Podobnosti Jaro-Winkler

Aktuální verze stránky ještě nebyla zkontrolována zkušenými přispěvateli a může se výrazně lišit od verze recenzované 22. prosince 2019; kontroly vyžadují 9 úprav .

V oblasti informatiky a statistiky je podobnost Jaro-Winkler mírou podobnosti řetězců pro měření vzdálenosti mezi dvěma sekvencemi znaků. Jedná se o variantu navrženou Williamem E. Winklerem v roce 1999 na základě Jarovy vzdálenosti (1989, Matthew A. Jaro). Neformálně je Jaro vzdálenost mezi dvěma slovy minimální počet jednoznakových transformací potřebných ke změně jednoho slova na druhé.

Čím menší je vzdálenost Jaro-Winkler pro dvě struny, tím jsou si tyto struny podobnější. Výsledek je normalizován, takže to neznamená žádnou podobnost a znamená  přesnou shodu. Podobnost Jaro-Winkler je .

Definice

Vzdálenost Jaro

Jarova vzdálenost mezi dvěma danými strunami a toto:

Kde:

Dva znaky z a jsou považovány za odpovídající pouze v případě, že jsou stejné a ne dále než .

Každý znak v řetězci je porovnán se všemi jeho odpovídajícími znaky v . Počet odpovídajících (ale odlišných řadových) znaků, který je dělitelný 2, určuje počet transpozic . Například při porovnávání slova CRATE se slovem TRACE jsou shodné znaky pouze 'R', 'A' a 'E', tj. m=3. Ačkoli se na obou řádcích objevují 'C' a 'T', jsou dále než 1, tedy patro(5/2)-1=1. Proto t=0. Při porovnávání DwAyNE s DuANE jsou odpovídající písmena již ve stejném pořadí DANE, takže nejsou potřeba žádné permutace.

Vzdálenost Jaro-Winkler

Vzdálenost Jaro-Winkler používá faktor škálování , který dává příznivější hodnocení řetězcům, které si navzájem odpovídají od začátku až do určité délky , nazývané prefix. Dané dva řetězce a . Jejich vzdálenost Jaro-Winkler je:

kde:

Zatímco vzdálenost Jaro–Winkler je často označována jako metrika vzdálenosti , není to metrika v matematickém smyslu slova, protože se neřídí trojúhelníkovou nerovností . Také vzdálenost Jaro-Winkler nesplňuje axiom, který říká, že [1] .

V některých implementacích algoritmu výpočtu vzdálenosti Jaro-Winkler je přidán prefixový bonus pouze v případě, že porovnávané struny mají vzdálenost Jaro nad nastavenou "práh zesílení" . Prahová hodnota ve Winklerově implementaci byla 0,7.

Příklady

Je třeba poznamenat, že Winklerův kód C se minimálně na dvou místech liší od publikované práce o metrice Jaro-Winkler. Prvním je použití tabulky errata (adjwt) a druhým jsou některé dodatečné podmínky pro dlouhé řetězce.

Příklad 1

Jsou uvedeny struny MARTHA a MARHTA. Představme si jejich průsečík ve formě tabulky:

M A R T H A
M jeden 0 0 0 0 0
A 0 jeden 0 0 0 0
R 0 0 jeden 0 0 0
H 0 0 0 0 jeden 0
T 0 0 0 jeden 0 0
A 0 0 0 0 0 jeden

Zde je maximální vzdálenost 6/2 - 1 = 2. Žluté buňky ve výše uvedené tabulce označují jedničky, pokud jsou znaky shodné (existuje shoda), a jinak nuly.

Ukazuje se:

Vzdálenost Jaro:

Abychom našli výsledek Jaro-Winkler pomocí standardní váhy, stále hledáme:

Takto:

Příklad 2

Jsou dány struny DWAYNE a DUANE. Ukazuje se:

Vzdálenost Jaro:

Abychom našli výsledek Jaro-Winkler pomocí standardní váhy, stále hledáme:

Takto:

Příklad 3

Dané řetězce DIXON a DICKSONX . Ukazuje se:

D X Ó N
D jeden 0 0 0 0
0 jeden 0 0 0
C 0 0 0 0 0
K 0 0 0 0 0
S 0 0 0 0 0
Ó 0 0 0 jeden 0
N 0 0 0 0 jeden
X 0 0 0 0 0

Zde jsou vyplněné buňky odpovídajícím oknem pro každý znak. Jednotka v buňce označuje shodu. Všimněte si, že dvě x (X) nejsou považována za shodu, protože jsou mimo třetí okno shody.

Vzdálenost Jaro:

Abychom našli výsledek Jaro-Winkler pomocí standardní váhy, stále hledáme:

Takto:

Vztahy s jinými metrikami změny vzdálenosti

Existují další populární míry změny vzdálenosti, které se počítají pomocí jiné sady platných editačních operací. Například,

Změna vzdálenosti je obvykle definována jako parametrizovatelná metrika vypočítaná pomocí určité sady platných editačních operací a každé operaci je přiřazena cena (možná nekonečná). Toto je další zobecnění algoritmů zarovnání genetických sekvencí , jako je Smith-Watermanův algoritmus , který činí náklady operace závislými na tom, kde je aplikován.

Praktická aplikace

Implementace algoritmu v různých programovacích jazycích

Implementace algoritmu v C [4] * strcmp95 . c Verze 2 */ /* Funkce strcmp95 vrací hodnotu s dvojnásobnou přesností od 0,0 (celkový nesouhlas) do 1,0 (shoda znak po znaku). Vrácená hodnota je mírou podobnosti dvou řetězců. */ /* Datum vydání: leden. 26, 1994 */ /* Upraveno: 24. dubna 1994 Opraveno zpracování řetězců znaků s jednou délkou. Autoři: Tato funkce byla napsána pomocí logiky z kódu, který napsali Bill Winkler, George McLaughlin a Matt Jaro s úpravami Maureen Lynch. Komentář: Toto je oficiální porovnávač řetězců, který má být použit pro párování během sčítání v roce 1995. */ # include <ctype.h> # include <string.h> # definovat NOTNUM(c) ((c>57) || (c<48)) # definovat INRANGE(c) ((c>0) && (c<91)) # definovat MAX_VAR_SIZE 61 # definovat NULL60 " " double strcmp95 ( char * ying , char * yang , long y_length , int * ind_c []) { /* Argumenty: ying a yang jsou ukazatele na 2 řetězce, které mají být porovnány. Řetězce nemusí být řetězce zakončené NUL, protože délka je předána. y_length je délka řetězců. ind_c je pole, které se používá k určení, zda mají být aktivovány určité možnosti. Nenulová hodnota znamená, že možnost je deaktivována. Možnosti jsou: ind_c[0] Zvyšuje pravděpodobnost shody, když je počet shodných znaků velký. Tato možnost umožňuje trochu větší toleranci, když jsou řetězce velké. Není to vhodný test při porovnávání polí s pevnou délkou, jako jsou telefonní čísla a čísla sociálního zabezpečení. ind_c[1] Všechna malá písmena jsou před porovnáním převedena na velká písmena. Zakázání této funkce znamená, že řetězec malých písmen "kód" nebude rozpoznán jako stejný jako řetězec velkých písmen "CODE". Část úpravy pro podobné znaky se také vztahuje pouze na velká písmena. Navrhované hodnoty jsou všechny nuly pro znakové řetězce, jako jsou jména. */ static int pass = 0 , adjwt [ 91 ][ 91 ]; statický znak sp [ 39 ][ 2 ] = { 'A' , 'E' , 'A' , 'I' , 'A' , 'O' , 'A' , 'U' , 'B' , 'V' , 'E' , 'I' , ' E' , 'O' , 'E' , 'U' , „I“ , „O“ , „I“ , „U“ , „O“ , „U“ , „I“ , „Y“ , „E“ , „Y“ , „C“ , „G“ , „E“ ' , 'F' , 'W' , 'U' , 'W' , 'V' , 'X' , 'K' , 'S' , 'Z' , 'X' , 'S' , 'Q' , 'C' , 'U ' , 'V' , „M“ , „N“ , „L“ , „I“ , „Q“ , „O“ , „P“ , „R“ , „I“ , „J“ , „2“ , „Z“ , „5“ ' , 'S' , '8' , 'B' , '1' , 'I' , '1' , 'L' , '0' , 'O' , '0' , 'Q' , 'C' , 'K' , 'G ' , 'J' , 'E' , ' ' , 'Y' , ' ' , 'S' , ' ' }; char ying_hold [ MAX_VAR_SIZE ], yang_hold [ MAX_VAR_SIZE ], ying_flag [ MAX_VAR_SIZE ], jang_flag [ MAX_VAR_SIZE ]; doubleweight , Num_sim ; _ long minv , search_range , lowlim , ying_length , hilim , N_trans , Num_com , yang_length ; int yl1 , yi_st , N_simi ; registr int i , j , k ; /* Inicializuje pole adjwt pouze při prvním volání funkce. Pole adjwt se používá k přidělení částečného uznání znakům, které mohou být chybami v důsledku známých fonetických chyb nebo chyb v rozpoznávání znaků. Typickým příkladem je shoda písmene „O“ s číslem „0“ */ if ( ! pass ) { projít ++ ; for ( i = 0 ; i < 91 ; i ++ ) for ( j = 0 ; j < 91 ; j ++ ) adjwt [ i ][ j ] = 0 ; for ( i = 0 ; i < 36 ; i ++ ) { adjwt [ sp [ i ][ 0 ]][ sp [ i ][ 1 ]] = 3 ; adjwt [ sp [ i ][ 1 ]][ sp [ i ][ 0 ]] = 3 ; } } /* Pokud je některý řetězec prázdný - return - přidáno ve verzi 2 */ if ( ! strncmp ( ying , NULL60 , y_length )) return ( 0.0 ); if ( ! strncmp ( yang , NULL60 , y_length )) return ( 0.0 ); /* Identifikujte řetězce, které se mají porovnávat, odstraněním všech úvodních a koncových mezer. */ k = y_délka - 1 ; for ( j = 0 ; (( ying [ j ] == ' ' ) && ( j < k )); j ++ ); for ( i = k ;(( ying [ i ] == '' ) && ( i > 0 )); i -- ); ying_length = i + 1 - j ; yi_st = j ; for ( j = 0 ;(( yang [ j ] == ' ' ) && ( j < k )); j ++ ); for ( i = k ;(( yang [ i ] == ' ' ) && ( i > 0 )); i -- ); délka_jangu = i + 1 - j ; ying_hold [ 0 ] = yang_hold [ 0 ] = 0 ; strncat ( ying_hold , & ying [ yi_st ], ying_length ); strncat ( yang_hold , & yang [ j ], yang_length ); if ( ying_length > yang_length ) { rozsah_hledání = délka_jingu ; minv = délka_jangu ; } jinak { rozsah_hledání = délka_jangu ; minv = délka_jingu ; } /* Pokud je některý řetězec prázdný - vrátí */ /* if (!minv) return(0.0); odstraněno ve verzi 2 */ /* Vymažte příznaky */ ying_flag [ 0 ] = ying_flag [ 0 ] = 0 ; strncat ( ying_flag , NULL60 , search_range ); strncat ( yang_flag , NULL60 , search_range ); rozsah_hledání = ( rozsah_hledání / 2 ) - 1 ; if ( rozsah_hledani < 0 ) rozsah_hledani = 0 ; /* přidáno ve verzi 2 */ /* Převede všechna malá písmena na velká. */ if ( ! ind_c [ 1 ]) { for ( i = 0 ; i < ying_length ; i ++ ) if ( islower ( ying_hold [ i ])) ying_hold [ i ] -= 32 ; for ( j = 0 ; j < yang_length ; j ++ ) if ( islower ( yang_hold [ j ])) yang_hold [ j ] -= 32 ; } /* Podívejte se pouze do vyhledávacího rozsahu, spočítejte a označte odpovídající páry. */ Num_com = 0 ; yl1 = délka_jangu - 1 ; for ( i = 0 ; i < ying_length ; i ++ ) { lowlim = ( i >= rozsah_hledání ) ? i - rozsah_hledání : 0 ; hilim = (( i + rozsah_hledání ) <= yl1 ) ? ( i + rozsah_hledání ) : yl1 ; for ( j = lowlim ; j <= hilim ; j ++ ) { if (( yang_flag [ j ] != '1' ) && ( yang_hold [ j ] == ying_hold [ i ])) { jang_flag [ j ] = '1' ; ying_flag [ i ] = '1' ; Num_com ++ ; zlomit ; } } } /* Pokud žádné znaky nejsou společné - vrátí */ if ( ! Num_com ) return ( 0.0 ); /* Spočítejte počet transpozic */ k = N_trans = 0 ; for ( i = 0 ; i < ying_length ; i ++ ) { if ( ying_flag [ i ] == '1' ) { for ( j = k ; j < délka_jangu ; j ++ ) { if ( yang_flag [ j ] == '1' ) { k = j + 1 ; zlomit ; } } if ( ying_hold [ i ] != yang_hold [ j ]) N_trans ++ ; } } N_trans = N_trans / 2 ; /* upravit pro podobnosti v neshodných znacích */ N_simi = 0 ; if ( minv > Num_com ) { for ( i = 0 ; i < ying_length ; i ++ ) { if ( ying_flag [ i ] == ' ' && INRANGE ( ying_hold [ i ])) { for ( j = 0 ; j < yang_length ; j ++ ) { if ( yang_flag [ j ] == ' ' && INRANGE ( yang_hold [ j ])) { if ( adjwt [ ying_hold [ i ]][ yang_hold [ j ]] > 0 ) { N_simi += adjwt [ ying_hold [ i ]][ yang_hold [ j ]]; jang_flag [ j ] = '2' ; zlomit ; } } } } } _ Num_sim = (( double ) N_simi ) / 10,0 + Num_com ; /* Výpočet hlavní hmotnosti. */ váha = Num_sim / (( double ) ying_length ) + Num_sim / (( double ) yang_length ) + (( double ) ( Num_com - N_trans )) / (( double ) Num_com ); hmotnost = hmotnost / 3,0 ; /* Pokračujte ve zvyšování hmotnosti, pokud jsou struny podobné */ if ( hmotnost > 0,7 ) { /* Upravte tak, aby byly společné až první 4 znaky */ j = ( minv >= 4 ) ? 4 : minv ; for ( i = 0 ;(( i < j ) && ( ying_hold [ i ] == yang_hold [ i ]) && ( NOTNUM ( ying_hold [ i ]))); i ++ ); if ( i ) hmotnost += i * 0,1 * ( 1,0 - hmotnost ); /* Volitelně upravit pro dlouhé řetězce. */ /* Po odsouhlasení počátečních znaků musí souhlasit alespoň dva další a souhlasné znaky musí být > 0,5 ze zbývajících znaků. */ if (( ! ind_c [ 0 ]) && ( minv > 4 ) && ( Num_com > i + 1 ) && ( 2 * Num_com >= minv + i )) if ( NOTNUM ( ying_hold [ 0 ])) hmotnost += ( double ) ( 1,0 - hmotnost ) * (( double ) ( Num_com - i -1 ) / (( double ) ( ying_length + yang_length - i * 2 + 2 ))); } návrat ( váha ); } /* strcmp95 */ Implementace algoritmu v Delphi [5] funkce JaroWinkler ( prmT1 , prmT2 : String ; p : Double = 0,1 ) : Double ; Var ecartMax , l1 , l2 , compteMatching , compteTransposition , longueurPrefix , i , j : integer ; c1 , c2 , t1Match , t2Match : string ; b1 , b2 : pole Boolean ; _ vzdálenostJaro : Double ; label endfor , exitfor2 ; function TrouverMatches ( prmTextInitial : string ; b1 : pole Boolean ) : string ; _ var i : celé číslo ; res : řetězec _ begin // Vypočítat le nombre de caractères qui match for i := 1 to Length ( prmTextInitial ) do begin if b1 [ i ] then //prmTextMatche[i]='_' then begin res := res + prmTextInitial [ i ] ; konec ; konec ; TrouverMatches := res ; konec ; begin ecartMax := round ( Max ( Length ( prmT1 ) , Length ( prmT2 )) / 2 ) - 1 ; if (( prmT1 = '' ) nebo ( prmT2 = '' )) then begin JaroWinkler := 0 ; výstup ; konec ; compteMatching := 0 ; compteTransposition := 0 ; l1 := Délka ( prmT1 ) ; l2 := Délka ( prmT2 ) ; setlength ( b1 , l1 + 1 ) ; Setlength ( b2 , l2 + 1 ) ; for i := 0 l1 do begin b1 [ i ] := false ; konec ; for i := 0 l2 do begin b2 [ i ] := false ; konec ; for i := 1 l1 do begin c1 := prmT1 [ i ] ; if ( i <= l2 ) then c2 := prmT2 [ i ] else c2 := '' ; for j := Max ( i - ecartMax , 1 ) Min ( i + ecartMax , l2 ) do begin c2 := prmT2 [ j ] ; if c1 = c2 then //compteMatching avec transposition begin b1 [ i ] := true ; b2 [ j ] := true ; //Le caractère a été matché, il n'est plus disponible Inc ( compteMatching ) ; zlomit ; konec ; konec ; konec ; if ( compteMatching = 0 ) then begin JaroWinkler := 0 ; výstup ; konec ; //Dans les caractères matchés, compte ceux qui ne matchent pas exactement t1Matche := TrouverMatches ( prmT1 , b1 ) ; t2Matche := TrouverMatches ( prmT2 , b2 ) ; if t1Matche <> t2Matche then begin for i := 1 to length ( t1Matche ) do begin if t1Matche [ i ] <> t2Matche [ i ] then Inc ( compteTransposition ) end ; end else begin compteTransposition := 0 ; konec ; distanceJaro := 1 / 3 * (( compteMatching / l1 ) + ( compteMatching / l2 ) + (( compteMatching - Int ( compteTransposition / 2 )) / compteMatching )) ; // Calcule la distance Winkler // Calcule le prefix sur les 4 premiers car aux max longueurPrefix := 0 ; for i := 1 min ( 4 , min ( l1 , l2 )) do begin c1 := prmT1 [ i ] ; c2 := prmT2 [ i ] ; if c1 = c2 then inc ( longueurPrefix ) else break ; konec ; //Valeur Constante définie par l' algo JaroWinkler := distanceJaro + ( longueurPrefix * p * ( 1 - distanceJaro )) ; konec ; Implementace algoritmu v PHP [6] <?php /* verze 1.2 Copyright (c) 2005-2010 Ivo Ugrina <[email protected]> PHP knihovna implementující vzdálenost Jaro a Jaro-Winkler , měřící podobnost mezi řetězci. Teoretické věci lze nalézt v: Winkler, W.E. (1999). „Stav propojení záznamů a aktuální problémy výzkumu“ . Statistika divize příjmů, publikace Internal Revenue Service R99/04. http://www.census.gov/srd/papers/pdf/rr99-04.pdf. Tento program je svobodný software; můžete jej redistribuovat a/nebo upravovat podle podmínek GNU General Public License, jak je zveřejněna Free Software Foundation; buď verze 3 licence, nebo (dle vašeho uvážení) jakákoli pozdější verze. Tento program je distribuován v naději, že bude užitečný, ale BEZ JAKÉKOLI ZÁRUKY; dokonce bez předpokládané záruky PRODEJNOSTI nebo VHODNOSTI PRO KONKRÉTNÍ ÚČEL. Další podrobnosti najdete v GNU General Public License. Spolu s tímto programem byste měli obdržet kopii GNU General Public License . Pokud ne, podívejte se na <http://www.gnu.org/licenses/>. === Velké díky patří Pierru Senellartovi <[email protected]> za nalezení malé chyby v kódu. */ function getCommonCharacters ( $ string1 , $ string2 , $allowedDistance ){ $str1_len = strlen ( $ string1 ); $str2_len = strlen ( $ string2 ); $temp_string2 = $ string2 ; $commonCharacters = '' ; for ( $i = 0 ; $i < $str1_len ; $i ++ ){ $noMatch = Pravda ; // porovnejte, zda se znak shoduje uvnitř dané povolené vzdálenosti // a zda jej přidá do společných znaků pro ( $j = max ( 0 , $i - $allowedDistance ); $noMatch && $j < min ( $i + $allowedDistance + 1 , $str2_len ); $j ++ ){ if ( $temp_string2 [ $j ] == $string1 [ $i ] ){ $noMatch = False ; $commonCharacters .= $ string1 [ $i ]; $temp_string2 [ $j ] = '' ; } } } return $commonCharacters ; } function Jaro ( $string1 , $string2 ){ $str1_len = strlen ( $ string1 ); $str2_len = strlen ( $ string2 ); // teoretická vzdálenost $vzdálenost = podlaha ( min ( $str1_len , $str2_len ) / 2,0 ); // získání běžných znaků $commons1 = getCommonCharacters ( $ string1 , $ string2 , $vzdálenost ); $commons2 = getCommonCharacters ( $ string2 , $ string1 , $vzdálenost ); if ( ( $commons1_len = strlen ( $commons1 )) == 0 ) return 0 ; if ( ( $commons2_len = strlen ( $commons2 )) == 0 ) return 0 ; // výpočet transpozice $transpozice = 0 ; $upperBound = min ( $commons1_len , $commons2_len ); for ( $i = 0 ; $i < $upperBound ; $i ++ ){ if ( $commons1 [ $i ] != $commons2 [ $i ] ) $transpozice ++ ; } $transpozice /= 2,0 ; // return the Jaro distance return ( $commons1_len / ( $str1_len ) + $commons2_len / ( $str2_len ) + ( $commons1_len - $transpositions ) / ( $commons1_len )) / 3.0 ; } function getPrefixLength ( $ string1 , $ string2 , $MINPREFIXLENGTH = 4 ){ $n = min ( pole ( $MINPREFIXLENGTH , strlen ( $ string1 ), strlen ( $string2 ) ) ); for ( $i = 0 ; $i < $n ; $i ++ ){ if ( $string1 [ $i ] != $string2 [ $i ] ){ // návrat indexu prvního výskytu různých znaků return $i ; } } // prvních n znaků je stejných return $n ; } function JaroWinkler ( $ string1 , $ string2 , $PREFIXSCALE = 0,1 ){ $JaroDistance = Jaro ( $ string1 , $ string2 ); $prefixLength = getPrefixLength ( $ string1 , $ string2 ); return $JaroDistance + $prefixLength * $PREFIXSCALE * ( 1,0 - $JaroDistance ); } ?>

Poznámky

  1. Record Linkage Algorithms in F# - Extensions to Jaro-Winkler Distance (Part 3) . Získáno 21. března 2017. Archivováno z originálu 31. prosince 2019.
  2. Algoritmy pro přibližné porovnávání textu, část 2 . Získáno 21. března 2017. Archivováno z originálu dne 22. března 2017.
  3. Databáze PL/SQL balíčků a typů Reference . Získáno 21. března 2017. Archivováno z originálu 12. ledna 2017.
  4. Archivovaná kopie (odkaz není dostupný) . Získáno 23. února 2011. Archivováno z originálu 23. února 2011. 
  5. Distance de jaro-winkler Archived 22. března 2017 na Wayback Machine  (FR)
  6. [1  ]

Odkazy