Similitudes Jaro-Winkler

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 22 de diciembre de 2019; las comprobaciones requieren 9 ediciones .

En el campo de la informática y las estadísticas , la similitud de Jaro-Winkler es una medida de similitud de cadenas para medir la distancia entre dos secuencias de caracteres. Esta es una variante propuesta por William E. Winkler en 1999 basada en la distancia Jaro (1989, Matthew A. Jaro). De manera informal, la distancia de Jaro entre dos palabras es el número mínimo de transformaciones de un solo carácter necesarias para cambiar una palabra por otra.

Cuanto menor sea la distancia Jaro-Winkler para dos cadenas, más similares serán estas cadenas entre sí. El resultado se normaliza, por lo que significa que no hay similitud y significa  coincidencia exacta. El parecido Jaro-Winkler es .

Definición

Distancia Jaro

La distancia de Jaro entre dos cadenas dadas y esto:

Dónde:

Se considera que dos caracteres de y respectivamente coinciden solo si son iguales y no más allá de .

Cada carácter de la cadena se compara con todos sus caracteres correspondientes en . El número de caracteres coincidentes (pero ordinales diferentes), que es divisible por 2, determina el número de transposiciones . Por ejemplo, al comparar la palabra CRATE con la palabra TRACE, solo 'R', 'A' y 'E' son caracteres coincidentes, es decir, m=3. Aunque 'C' y 'T' aparecen en ambas líneas, están más allá de 1, es decir, piso(5/2)-1=1. Por lo tanto, t=0. Al comparar DwAyNE con DuANE, las letras correspondientes ya están en el mismo orden DANE, por lo que no se necesitan permutaciones.

Distancia Jaro-Winkler

La distancia de Jaro-Winkler utiliza un factor de escala , que otorga calificaciones más favorables a las cadenas que coinciden entre sí desde el principio hasta una determinada longitud , denominada prefijo. Dadas dos cadenas y . Su distancia Jaro-Winkler es:

dónde:

Si bien la distancia de Jaro-Winkler a menudo se denomina métrica de distancia , no es una métrica en el sentido matemático de la palabra porque no obedece a la desigualdad del triángulo . Además, la distancia Jaro-Winkler no satisface el axioma que dice que [1] .

En algunas implementaciones del algoritmo de cálculo de distancia de Jaro-Winkler, se agrega una bonificación de prefijo solo si las cadenas comparadas tienen una distancia de Jaro por encima del "umbral de impulso" establecido . El umbral en la implementación de Winkler fue 0,7.

Ejemplos

Cabe señalar que el código C de Winkler difiere en al menos dos lugares del trabajo publicado sobre la métrica de Jaro-Winkler. El primero es el uso de la tabla de errata (adjwt), y el segundo son algunas condiciones adicionales para cadenas largas.

Ejemplo 1

Se dan las cadenas MARTHA y MARHTA. Representemos su intersección en forma tabular:

METRO A R T H A
METRO una 0 0 0 0 0
A 0 una 0 0 0 0
R 0 0 una 0 0 0
H 0 0 0 0 una 0
T 0 0 0 una 0 0
A 0 0 0 0 0 una

Aquí, la distancia máxima es 6/2 - 1 = 2. Las celdas amarillas en la tabla anterior indican unos cuando los caracteres son idénticos (hay una coincidencia) y ceros en caso contrario.

Resulta:

Distancia Jaró:

Para encontrar el resultado de Jaro-Winkler utilizando el peso estándar, seguimos buscando:

De este modo:

Ejemplo 2

Se dan las cadenas DWAYNE y DUANE. Resulta:

Distancia Jaró:

Para encontrar el resultado de Jaro-Winkler utilizando el peso estándar, seguimos buscando:

De este modo:

Ejemplo 3

Dadas las cadenas DIXON y DICKSONX . Resulta:

D yo X O norte
D una 0 0 0 0
yo 0 una 0 0 0
C 0 0 0 0 0
k 0 0 0 0 0
S 0 0 0 0 0
O 0 0 0 una 0
norte 0 0 0 0 una
X 0 0 0 0 0

Aquí, las celdas llenas son la ventana correspondiente para cada carácter. Una unidad en una celda indica una coincidencia. Tenga en cuenta que las dos x (X) no se consideran una coincidencia porque están fuera de la tercera ventana de coincidencia.

Distancia Jaró:

Para encontrar el resultado de Jaro-Winkler utilizando el peso estándar, seguimos buscando:

De este modo:

Relaciones con otras métricas de cambio de distancia

Hay otras medidas populares de cambio de distancia que se calculan utilizando un conjunto diferente de operaciones de edición válidas. Por ejemplo,

El cambio en la distancia generalmente se define como una métrica parametrizable que se calcula utilizando un determinado conjunto de operaciones de edición válidas, y a cada operación se le asigna un costo (quizás infinito). Esta es una generalización adicional de los algoritmos de alineación de secuencias genéticas , como el algoritmo de Smith-Waterman , que hacen que el costo de una operación dependa de dónde se aplique.

Aplicación práctica

Implementaciones del algoritmo en varios lenguajes de programación

Implementación del algoritmo en C [4] * strcmp95 . c Versión 2 */ /* La función strcmp95 devuelve un valor de doble precisión de 0,0 ( desacuerdo total) a 1,0 (acuerdo carácter por carácter). El valor devuelto es una medida de la similitud de las dos cadenas. */ /* Fecha de lanzamiento: enero. 26, 1994 */ /* Modificado: 24 de abril de 1994 Se corrigió el procesamiento de las cadenas de caracteres de longitud única. Autores: esta función se escribió usando la lógica del código escrito por Bill Winkler, George McLaughlin y Matt Jaro con modificaciones de Maureen Lynch. Comentario: Este es el comparador oficial de cadenas que se usará para la comparación durante el Censo de prueba de 1995. */ # incluir <ctipo.h> # incluir <cadena.h> # define NOTNUM(c) ((c>57) || (c<48)) # define INRANGE(c) ((c>0) && (c<91)) # define MAX_VAR_SIZE 61 # define NULL60 " " doble strcmp95 ( char * ying , char * yang , long y_length , int * ind_c []) { /* Argumentos: ying y yang son punteros a las 2 cadenas que se van a comparar. Las cadenas no necesitan ser cadenas terminadas en NUL porque se pasa la longitud. y_length es la longitud de las cadenas. ind_c es una matriz que se utiliza para determinar si se deben activar ciertas opciones. Un valor distinto de cero indica que la opción está desactivada. Las opciones son: ind_c[0] Aumenta la probabilidad de una coincidencia cuando el número de caracteres coincidentes es grande. Esta opción permite un poco más de tolerancia cuando las cadenas son grandes. No es una prueba adecuada cuando se comparan campos de longitud fija, como números de teléfono y de seguridad social. ind_c[1] Todos los caracteres en minúsculas se convierten a mayúsculas antes de la comparación. Deshabilitar esta función significa que la cadena en minúsculas "código" no se reconocerá como la misma cadena en mayúsculas "CÓDIGO". Además, la sección de ajuste de caracteres similares solo se aplica a los caracteres en mayúsculas. Los valores sugeridos son todos ceros para cadenas de caracteres como nombres. */ static int pass = 0 , adjwt [ 91 ][ 91 ]; sp de caracteres estáticos [ 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_mantener [ MAX_VAR_SIZE ], bandera_ying [ MAX_VAR_SIZE ], yang_bandera [ MAX_VAR_SIZE ]; peso doble , Num_sim ; _ minv largo , rango_de_búsqueda , límite_bajo , longitud_ying , hilim , N_trans , Num_com , yang_length ; int yl1 , yi_st , N_simi ; registro int i , j , k ; /* Inicializar la matriz adjwt en la primera llamada a la función solamente. La matriz adjwt se utiliza para dar crédito parcial a los caracteres que pueden ser errores debido a errores fonéticos o de reconocimiento de caracteres conocidos. Un ejemplo típico es hacer coincidir la letra "O" con el número "0" */ si ( ! pasar ) { pasar ++ ; for ( i = 0 ; i < 91 ; i ++ ) for ( j = 0 ; j < 91 ; j ++ ) adjwt [ i ][ j ] = 0 ; para ( yo = 0 ; yo < 36 ; yo ++ ) { adjwt [ sp [ i ][ 0 ]][ sp [ i ][ 1 ]] = 3 ; adjwt [ sp [ i ][ 1 ]][ sp [ i ][ 0 ]] = 3 ; } } /* Si cualquiera de las cadenas está en blanco - retorno - agregado en la Versión 2 */ if ( ! strncmp ( ying , NULL60 , y_length )) return ( 0.0 ); if ( ! strncmp ( yang , NULL60 , y_length )) return ( 0.0 ); /* Identifique las cadenas que se compararán eliminando todos los espacios iniciales y finales. */ k = y_longitud - 1 ; for ( j = 0 ;(( ying [ j ] == ' ' ) && ( j < k )); j ++ ); for ( i = k ;(( ying [ i ] == ' ' ) && ( i > 0 )); i -- ); longitud_ying = i + 1 - j ; yi_st = j ; for ( j = 0 ;(( yang [ j ] == ' ' ) && ( j < k )); j ++ ); for ( i = k ;(( yang [ i ] == ' ' ) && ( i > 0 )); i -- ); yang_longitud = yo + 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 ( longitud_ying > longitud_yang ) { rango_búsqueda = longitud_ying ; minv = yang_longitud ; } más { rango_búsqueda = longitud_yang ; minv = longitud_ying ; } /* Si cualquiera de las cadenas está en blanco, devuelve */ /* si (! minv) devuelve (0.0); eliminado en la versión 2 */ /* Borrar las banderas */ bandera_ying [ 0 ] = bandera_ying [ 0 ] = 0 ; strncat ( ying_flag , NULL60 , search_range ); strncat ( yang_flag , NULL60 , search_range ); rango_busqueda = ( rango_busqueda / 2 ) - 1 ; if ( rango_de_búsqueda < 0 ) rango_de_búsqueda = 0 ; /* añadido en la versión 2 */ /* Convierte todos los caracteres en minúsculas a mayúsculas. */ si ( ! ind_c [ 1 ]) { for ( i = 0 ; i < ying_length ; i ++ ) if ( islower ( ying_hold [ i ])) ying_hold [ i ] -= 32 ; for ( j = 0 ; j < yang_longitud ; j ++ ) if ( islower ( yang_hold [ j ])) yang_hold [ j ] -= 32 ; } /* Mirando solo dentro del rango de búsqueda, cuente y marque los pares coincidentes. */ num_com = 0 ; yl1 = yang_longitud - 1 ; for ( i = 0 ; i < longitud_ying ; i ++ ) { lowlim = ( i >= search_range ) ? i - search_range : 0 ; hilim = (( i + search_range ) <= yl1 ) ? ( i + rango_búsqueda ) : yl1 ; for ( j = límite bajo ; j <= hilo ; j ++ ) { if (( yang_flag [ j ] != '1' ) && ( yang_hold [ j ] == ying_hold [ i ])) { bandera_yang [ j ] = '1' ; bandera_ying [ i ] = '1' ; Num_com ++ ; romper ; } } } /* Si no hay caracteres en común - retorno */ si ( ! Num_com ) devuelve ( 0.0 ); /* Cuenta el numero de transposiciones */ k = N_trans = 0 ; for ( i = 0 ; i < longitud_ying ; i ++ ) { if ( bandera_ying [ i ] == '1' ) { para ( j = k ; j < longitud_yang ; j ++ ) { if ( yang_flag [ j ] == '1' ) { k = j + 1 ; romper ; } } if ( ying_hold [ i ] != yang_hold [ j ]) N_trans ++ ; } } N_trans = N_trans / 2 ; /* ajustar las similitudes en los caracteres que no coinciden */ N_sim = 0 ; if ( minv > Num_com ) { for ( i = 0 ; i < longitud_ying ; 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 ]]; bandera_yang [ j ] = '2' ; romper ; } } } } } } Num_sim = (( doble ) N_simi ) / 10.0 + Num_com ; /* Cálculo del peso principal. */ peso = Num_sim / (( doble ) longitud_ying ) + Num_sim / (( doble ) longitud_yang ) + (( doble ) ( Num_com - N_trans )) / (( doble ) Num_com ); peso = peso / 3.0 ; /* Continuar aumentando el peso si las cadenas son similares */ si ( peso > 0.7 ) { /* Ajuste por tener hasta los primeros 4 caracteres en común */ j = ( minv >= 4 ) ? 4 : minv ; for ( i = 0 ;(( i < j ) && ( ying_hold [ i ] == yang_hold [ i ]) && ( NOTNUM ( ying_hold [ i ]))); i ++ ); si ( i ) peso += i * 0.1 * ( 1.0 - peso ); /* Opcionalmente ajuste para cadenas largas. */ /* Después de acordar los caracteres iniciales, al menos dos más deben coincidir y los caracteres coincidentes deben ser > .5 de los caracteres restantes. */ if (( ! ind_c [ 0 ]) && ( minv > 4 ) && ( Num_com > i + 1 ) && ( 2 * Num_com >= minv + i )) if ( NOTNUM ( ying_hold [ 0 ])) peso += ( doble ) ( 1.0 - peso ) * (( doble ) ( Num_com - i -1 ) / (( doble ) ( longitud_ying + longitud_yang - i * 2 + 2 ))); } retorno ( peso ); } /* strcmp95 */ Implementación del algoritmo en Delphi [5] función JaroWinkler ( prmT1 , prmT2 : Cadena ; p : Doble = 0.1 ) : Doble ; Var ecartMax , l1 , l2 , compteMatching , compteTransposition , longueurPrefix , i , j : entero ; c1 , c2 , t1Coincidencia , t2Coincidencia : cadena ; b1 , b2 : matriz de valores booleanos ; distanciaJaro : Doble ; etiqueta endfor , exitfor2 ; function TrouverMatches ( prmTextInitial : string ; b1 : array of Boolean ) : string ; var i : entero ; res : cadena _ begin // Calcule el nombre de las características que coinciden con i := 1 to Length ( prmTextInitial ) do begin if b1 [ i ] then //prmTextMatche[i]='_' then begin res := res + prmTextInitial [ i ] ; fin ; fin ; TrouverCoincidencias := res ; fin ; comenzar ecartMax := round ( Max ( Longitud ( prmT1 ) , Longitud ( prmT2 )) / 2 ) - 1 ; si (( prmT1 = '' ) o ( prmT2 = '' )) entonces empieza JaroWinkler := 0 ; salida ; fin ; compteMatching := 0 ; compteTransposition := 0 ; l1 := Longitud ( prmT1 ) ; l2 := Longitud ( prmT2 ) ; conjuntolongitud ( b1 , l1 + 1 ) ; Establecer longitud ( b2 , l2 + 1 ) ; para i := 0 a l1 empieza b1 [ i ] : = false ; fin ; para i := 0 a l2 empieza b2 [ i ] : = false ; fin ; for i := 1 to l1 do begin c1 := prmT1 [ i ] ; si ( i <= l2 ) entonces c2 := prmT2 [ i ] else c2 := '' ; para j := Max ( i - ecartMax , 1 ) a Min ( i + ecartMax , l2 ) comience c2 : = prmT2 [ j ] ; if c1 = c2 entonces //compteMatching avec transposition begin b1 [ i ] := true ; b2 [ j ] := verdadero ; // Le caractère a été matché, il n'est plus disponible Inc ( compteMatching ) ; romper ; fin ; fin ; fin ; si ( compteMatching = 0 ) luego comience JaroWinkler := 0 ; salida ; fin ; //Dans les caractères matchés, compte ceux qui ne matchent pas exactement t1Matche := TrouverMatches ( prmT1 , b1 ) ; t2Coincidencia := TrouverCoincidencias ( prmT2 , b2 ) ; si t1Matche <> t2Matche entonces comienza para i : = 1 hasta la longitud ( t1Matche ) comienza si t1Matche [ i ] <> t2Matche [ i ] luego Inc ( compleTransposition ) end ; end else begin compteTransposition := 0 ; fin ; distanciaJaro := 1 / 3 * (( compteMatching / l1 ) + ( compteMatching / l2 ) + (( compteMatching - Int ( compteTransposition / 2 )) / compteMatching )) ; // Calcular la distancia Winkler // Calcular el prefijo entre los 4 primeros coches aux max longueurPrefix := 0 ; for i := 1 to min ( 4 , min ( l1 , l2 )) do begin c1 := prmT1 [ i ] ; c2 := prmT2 [ i ] ; si c1 = c2 entonces inc ( prefijolongitudinal ) de lo contrario romper ; fin ; //Valor constante definido por el algoritmo JaroWinkler := distanciaJaro + ( longueurPrefix * p * ( 1 - distanciaJaro )) ; fin ; Implementación del algoritmo en PHP [6] <?php /* versión 1.2 Copyright (c) 2005-2010 Ivo Ugrina <[email protected]> Una biblioteca PHP que implementa la distancia Jaro y Jaro-Winkler , midiendo la similitud entre cadenas. El material teórico se puede encontrar en: Winkler, W.E. (1999). "El estado de la vinculación de registros y los problemas de investigación actuales". Estadísticas de la División de Ingresos, Servicio de Rentas Internas Publicación R99/04. http://www.census.gov/srd/papers/pdf/rr99-04.pdf. Este programa es software libre; puede redistribuirlo y/o modificarlo según los términos de la Licencia Pública General de GNU publicada por la Free Software Foundation; ya sea la versión 3 de la Licencia o (a su elección) cualquier versión posterior. Este programa se distribuye con la esperanza de que sea útil, pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Consulte la Licencia pública general de GNU para obtener más detalles. Debería haber recibido una copia de la Licencia Pública General de GNU junto con este programa. Si no, consulte <http://www.gnu.org/licenses/>. === Muchas gracias a Pierre Senellart <[email protected]> por encontrar un pequeño error en el código. */ función getCommonCharacters ( $ string1 , $ string2 , $allowedDistance ){ $str1_len = strlen ( $string1 ); $str2_len = strlen ( $string2 ); $temp_cadena2 = $cadena2 ; $caracterescomunes = '' ; para ( $i = 0 ; $i < $str1_len ; $i ++ ){ $noCoincidencia = Verdadero ; // comparar si el carácter coincide dentro de la distancia permitida dada // y si lo hace, agréguelo a caracteres comunes para ( $j = max ( 0 , $i - $distancia permitida ); $noCoincidencia && $j < min ( $i + $distancia permitida + 1 , $str2_len ); $j ++ ){ if ( $temp_string2 [ $j ] == $string1 [ $i ] ){ $noMatch = False ; $caracterescomunes .= $cadena1 [ $i ]; $temp_string2 [ $j ] = '' ; } } } devuelve $caracterescomunes ; } function Jaro ( $cadena1 , $cadena2 ){ $str1_len = strlen ( $string1 ); $str2_len = strlen ( $string2 ); // distancia teórica $distancia = piso ( min ( $str1_len , $str2_len ) / 2.0 ); // obtener caracteres comunes $commons1 = getCommonCharacters ( $string1 , $string2 , $distance ); $comunes2 = obtenerCaracteresComunes ( $cadena2 , $cadena1 , $distancia ); if ( ( $commons1_len = strlen ( $commons1 )) == 0 ) return 0 ; if ( ( $commons2_len = strlen ( $commons2 )) == 0 ) devuelve 0 ; // calcular transposiciones $ transposiciones = 0 ; $límitesuperior = min ( $comunes1_len , $comunes2_len ); for ( $i = 0 ; $i < $superiorBound ; $i ++ ){ if ( $comunes1 [ $i ] != $comunes2 [ $i ] ) $transposiciones ++ ; } $transposiciones /= 2.0 ; // devuelve el retorno de distancia Jaro ( $ commons1_len / ( $str1_len ) + $commons2_len / ( $str2_len ) + ( $commons1_len - $transpositions ) / ( $commons1_len )) / 3.0 ; } función getPrefixLength ( $cadena1 , $cadena2 , $MINPREFIXLENGTH = 4 ){ $n = min ( arreglo ( $MINPREFIXLENGTH , strlen ( $string1 ), strlen ( $ string2 ) )) ); for ( $i = 0 ; $i < $n ; $i ++ ){ if ( $cadena1 [ $i ] != $cadena2 [ $i ] ){ // devuelve el índice de la primera aparición de diferentes caracteres devuelve $i ; } } // los primeros n caracteres son iguales return $n ; } función JaroWinkler ( $cadena1 , $cadena2 , $PREFIXSCALE = 0.1 ){ $JaroDistancia = Jaro ( $cadena1 , $cadena2 ); $prefixLength = getPrefixLength ( $cadena1 , $cadena2 ); return $JaroDistance + $prefixLength * $PREFIXSCALE * ( 1.0 - $JaroDistance ); } ?>

Notas

  1. Algoritmos de vinculación de registros en F# - Extensiones a la distancia Jaro-Winkler (Parte 3) . Consultado el 21 de marzo de 2017. Archivado desde el original el 31 de diciembre de 2019.
  2. Algoritmos de comparación de texto aproximados, Parte 2 . Consultado el 21 de marzo de 2017. Archivado desde el original el 22 de marzo de 2017.
  3. Referencia de tipos y paquetes de PL/SQL de base de datos . Consultado el 21 de marzo de 2017. Archivado desde el original el 12 de enero de 2017.
  4. Copia archivada (enlace no disponible) . Consultado el 23 de febrero de 2011. Archivado desde el original el 23 de febrero de 2011. 
  5. Distance de jaro-winkler Archivado el 22 de marzo de 2017 en Wayback Machine  (FR)
  6. [1  ]

Enlaces