¿Qué algoritmo usarías mejor para la similitud de cadenas?

23

Estoy diseñando un complemento para identificar de forma única el contenido en varias páginas web, según las direcciones.

Entonces puedo tener una dirección que se parece a:

1 someawesome street, anytown, F100 211

más tarde puedo encontrar esta dirección en un formato ligeramente diferente.

1 someawesome street, F100 211,

o tal vez tan vago como

someawesome street F100

Estas son técnicamente la misma dirección, pero con un nivel de similitud. Me gustaría a) generar un identificador único para cada dirección para realizar búsquedas, yb) averiguar cuándo aparece una dirección muy similar.

¿Qué algoritmos / técnicas / métricas de cadena debo mirar? La distancia de Levenshtein parece una opción obvia, pero es curioso si hay otros enfoques que se presten aquí.

Squiggs
fuente
La "distancia de Levenshtein" no es un algoritmo.
gnasher729
A menos que introduzca un análisis básico, la distancia cruda de Levenstein no será tan agradable. Debe intentar al menos identificar palabras que podrían ser calles, nombres de ciudades, etc. y aquellas que podrían ser números de calles o códigos postales. Entonces, tal vez aplique Levenstein en estos con alguna coincidencia difusa estadística alimentada por lugares reales / nombres de calles. No es una cosa fácil :)
77
@gnasher: Pero una función que calcula la distancia de Levenshtein es un algoritmo. Sin esa función, la distancia de Levenshtein es meramente una curiosidad intelectual.
Robert Harvey
Encontré una explicación muy práctica con ejemplos aquí: comparación de algoritmos . En conclusión, recomiendan usar la similitud de Jaro-Winkler ya que el algoritmo de Levenstein depende de la longitud de la cadena, por lo que no es útil comparar.
Sandra Meneses
Por favor no escriba respuestas de solo enlace .
Jan Doggen

Respuestas:

14

El algoritmo de Levenstein se basa en el número de inserciones, eliminaciones y sustituciones en cadenas.

Desafortunadamente, no tiene en cuenta un error ortográfico común, que es la transposición de 2 caracteres (por ejemplo, someawesome vs someaewsome). Entonces preferiría el algoritmo Damerau-Levenstein más robusto .

No creo que sea una buena idea aplicar la distancia en cadenas enteras porque el tiempo aumenta abruptamente con la longitud de las cadenas comparadas. Pero aún peor, cuando se eliminan componentes de dirección, como ZIP, direcciones completamente diferentes pueden coincidir mejor (medido usando la calculadora de Levenshtein en línea ):

1 someawesome street, anytown, F100 211       (reference) 
1 someawesome st.,anytown                     (difference of 15, same address)     
1 otherplaces street,anytown,F100211          (difference of 13, different ddress) 
1 sameawesome street, othertown, CA98200      (difference of 13, different ddress)
anytown, 1 someawesome street                 (28 different same address)
anytown, F100 211, 1 someawesome street       (37 different same address)

Estos efectos tienden a empeorar para nombres de calles más cortos.

Así que será mejor que uses algoritmos más inteligentes. Por ejemplo, Arthur Ratz publicó en CodeProject un algoritmo para la comparación de texto inteligente. El algoritmo no imprime una distancia (ciertamente puede enriquecerse en consecuencia), pero identifica algunas cosas difíciles como mover bloques de texto (por ejemplo, el intercambio entre la ciudad y la calle entre mi primer ejemplo y mi último ejemplo).

Si dicho algoritmo es demasiado general para su caso, entonces debería realmente trabajar por componentes y comparar solo componentes comparables. Esto no es algo fácil si desea analizar cualquier formato de dirección en el mundo. Pero si el objetivo es más específico, por ejemplo, es ciertamente factible. Por ejemplo, "street", "st.", "Place", "plazza" y sus errores ortográficos habituales podrían revelar la parte de la calle de la dirección, cuya parte principal sería, en principio, el número. El código postal ayudaría a localizar la ciudad, o alternativamente es probablemente el último elemento de la dirección, o si no le gusta adivinar, puede buscar una lista de nombres de ciudades (por ejemplo, descargar una base de datos de código postal gratuita). Luego puede aplicar Damerau-Levenshtein solo en los componentes relevantes.

Christophe
fuente
¿Qué hay de ordenar ambas cadenas de comparación antes de la comparación? Descubrí que esto puede ayudar con la transposición.
openwonk
2

La distancia de Levenshtein es mejor para las palabras.

Si las palabras se escriben (principalmente) correctamente, mire la bolsa de palabras . Puede parecer una muerte excesiva, pero TF-IDF y la similitud del coseno .

O podrías usar Lucene gratis. Creo que hacen similitud coseno.

paparazzo
fuente
1

En primer lugar, tendrías que analizar las direcciones de la página web, RegEx es uno que debes tomar, sin embargo, puede ser muy difícil analizar direcciones usando RegEx. Probablemente termines teniendo que revisar una lista de posibles formatos de direccionamiento y una o más expresiones geniales que coincidan. No estoy muy familiarizado con el análisis de direcciones, pero recomendaría echar un vistazo a esta pregunta que sigue una línea de pensamiento similar: Analizador de direcciones generales para texto de forma libre.

La distancia de Levenshtein es útil, pero solo después de separar la dirección en sus partes. Considere las siguientes direcciones. 123 someawesome st.y 124 someawesome st.Estas direcciones son ubicaciones totalmente diferentes, pero su distancia de Levenshtein es de solo 1. Esto también se puede aplicar a algo así 8th st.y 9th st.los nombres de calles similares no suelen aparecer en la misma página web, pero no es desconocida. La página web de una escuela puede tener la dirección de la biblioteca al otro lado de la calle, por ejemplo, o la iglesia a pocas cuadras. Esto significa que los únicos datos que la distancia de Levenshtein es fácilmente utilizable es la distancia entre 2 puntos de datos, como la distancia entre la calle y la ciudad.

En cuanto a descubrir cómo separar los diferentes campos, es bastante simple una vez que obtenemos las direcciones. Afortunadamente, la mayoría de las direcciones vienen en formatos muy específicos, con un poco de magia RegEx debería ser posible separarlas en diferentes campos de datos. Incluso si la dirección no está bien formateada, todavía hay alguna esperanza. Las direcciones siempre (casi) siguen el orden de magnitud. Su dirección debe estar en algún lugar de una cuadrícula lineal como esta, dependiendo de la cantidad de información que se proporcione y de qué se trata:

StreetNumber < Street < City < State < Country

Ocurre raramente, si es que la dirección salta de un campo a otro no adyacente. No va a ver una calle, luego país, o número de calle, luego ciudad, muy a menudo.

Ucenna
fuente
2
Excepto que las direcciones de calles no son regulares y no se pueden analizar de manera confiable mediante expresiones regulares. Ciertamente, no pueden identificarse con precisión si solo están incrustados en texto libre. Por supuesto, puede escribir algunas expresiones regulares diferentes para que coincidan con diferentes formatos comunes, si ya sabe dónde está buscando.
Inútil
@ Inútil Eso es cierto. Es factible en teoría, pero subestimé la cantidad de trabajo necesario para realizarlo. Especialmente cuando hay opciones potencialmente mejores disponibles. He modificado mi respuesta para reflejar esto.
Ucenna
1

Pregunta acerca de los algoritmos de similitud de cadenas pero sus cadenas son direcciones. Enviaría las direcciones a una API de ubicación como Google Place Search y las usaría formatted_addresscomo punto de comparación. Ese parece ser el enfoque más preciso.

Para las cadenas de direcciones que no pueden ubicarse a través de una API, puede recurrir a algoritmos de similitud.

Dan Wilson
fuente
1
+1 Externalícelo para que obtenga el poder de los expertos para hacer el trabajo por usted. No tiene que ser Google, ya que existen algunos proveedores de servicios. No pierda su tiempo haciendo esto a menos que la correspondencia de direcciones sea su actividad principal.
LoztInSpace
0

Un algoritmo genial que es útil pero requiere una base de datos preestablecida de respuestas anteriores se llama: Distancia de edición de línea.

La distancia de edición de línea, como función, puede devolver un "cuán diferentes son esas dos palabras".

Una palabra como "dogma" y "perro", obtendrá un valor de 3 (por 3 caracteres adicionales).

O "gato" y "sombrero", recupera un valor de 1 (para un personaje diferente).

(Fuente: https://en.wikipedia.org/wiki/Edit_distance )

John Greene
fuente
2
¿Cuál es la ventaja sobre Levensthtein mencionado por OP?
Christophe
-1

De hecho, usar alguna función de distancia parece un buen enfoque. Pero el problema es encontrar la cadena más cercana de una dirección dada, lo que está lejos de ser trivial.

Estás describiendo una amplia categoría de algoritmos aquí. Echa un vistazo a la búsqueda de vecinos más cercanos

Como se menciona en un comentario, si encuentra una manera de separar los componentes de la dirección (nombre de la calle, número, etc.), la tarea será mucho más fácil.

kjaquier
fuente
-1

LongestCommonSubsequence (de Apache commons-text) puede ser otro enfoque para probar con direcciones. Si define la similitud de dos como la proporción de " longitud de subsecuencia común / máx. (Longitudes de dirección) ", puede aplicar un umbral de tolerancia, por ejemplo, 0.8 que definirá coincidencia / no coincidencia. De esta forma le permitirá hacer coincidir direcciones como " 1 someawesome st., Anytown " y " 1 someawesome street., Anytown ".

No es un algoritmo súper rápido, por lo que es posible que desee aplicar una recuperación rápida para minimizar las comparaciones. El ejemplo sería: evite la comparación si los códigos postales no coinciden o si la secuencia de solo dígitos extraídos es diferente.

Altair7852
fuente