sábado, 8 de marzo de 2008

SOUNDEX en español

SOUNDEX, función que ha estado presente en Oracle desde tiempos remotos, es un clásico algoritmo fonético utilizado para indexar nombres propios, asociando palabras que suenen igual a pesar de ocasionales diferencias en su escritura.

El algoritmo SOUNDEX fue concebido teniendo en cuenta la fonética del idioma inglés, por lo que hace inadecuado su uso en el castellano. Realizando adaptaciones propias del idioma español, implementé una versión en PL/SQL llamada SOUNDESP, la cual respeta la esencia del algoritmo original.

Los pasos básicos son:

  1. Retener la primera letra de la cadena. Tener en cuenta las letras dobles como CH y LL.
  2. Remover todas las ocurrencias de las letras siguientes a partir de la segunda posición: a, e, i, o, u, h, w, y (cuando suena como vocal i )
  3. Asignar números a las siguientes letras (luego de la primera):
    • b, f, p, v = 1
    • c, g, j, k, q, s, x, z = 2
    • d, t = 3
    • l = 4
    • m, n = 5
    • r = 6
    • ll, y, ch = 7
  4. Si hay números consecutivos, dejar solamente uno en la serie.
  5. Retornar los cuatro primeros caracteres, si son menos de cuatro completar con ceros.

SOUNDESP es un proyecto abierto y es bienvenido cualquier comentario para mejorar su implementación.
Ultima versión: 1.8 (04-MAR-2010)


NOTA: Para descargar el código correctamente, haga click derecho y elija Guardar destino

Ver también:
Wikipedia: Soundex

44 comentarios:

Roberto Santana dijo...

Hola,

antes de nada dar las gracias por este package, muy útil, pero le veo un par de bugs así de primeras:

1. Falla si la cadena de entrada es de más de 30 caracteres.

2. Prueba la palabra "Bahía" verás que falla porque elimina la H y las vocales y nos quedamos con una palabra de una letra que falla en la función eli_ady

Espero sea de utilidada

Roberto Santana dijo...

¿Lo vas a solventar?

Gracias

lfer dijo...

Roberto, ya fueron solucionados esos bugs, la nueva versión ya está disponible.

Muchas gracias y cualquier otro comentario será más que bienvenido!

Saludos

Roberto Santana dijo...

Gracias por la rapidez.

Se me olvidó comentar un fallo que corregí sobre la marcha, he tenido que modificar la función eli_acc para que funcione con algunos casos por esta:

FUNCTION eli_acc(p_pal IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN TRANSLATE(LTRIM(UPPER(p_pal),'H'),'ÑñáéíóúÁÉÍÓÚ','NNAEIOUAEIOU');
END eli_acc;

Espero sea de utilidad.

Saludos

Roberto Santana dijo...

Aunque lo más correcto sería esto:

FUNCTION eli_acc(p_pal IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN TRANSLATE(LTRIM(UPPER(p_pal),'H'),'ÑÁÉÍÓÚ','NAEIOU');
END eli_acc;

ya que está en mayúsculas el convertir las minúsculas carece de sentido.

Saludos

Roberto Santana dijo...

Creo que esto no acaba de ir del todo bien, estoy probandolo con diversas cadenas y me da resultados de lo más extraños. Fijate:

Aldeyuso|A437
Aguilar de Campos|AG46
Aguilar de Campos|AG46
La Abadía|L 13
Abrojo|A167
Los Álamos|L2 4
Los Álamos|L2 4
Aceñas de Zofraguillas|A252
Las Aceñas|L2 2

Se supone que los resultados deberían tener el formato \w\d{3}

Roberto Santana dijo...

Estoy un poco pesadito, pero gracias a esto podemos conseguir un package muy sólido.

Te comento otra cosa, cuando pasamos una cadena de 30 caracteres o más sin ningún tipo de acento como por ejemplo:

'Esto es una prueba de mas de 30 caracteres'

Todo va bien, pero surge un problema cuando uno o varios de esos caracteres en los primeros 30 está acentuado porque ese caracter ocupa 2 bytes, por ejemplo:

'Esto es una prueba de más de 30 caracteres'

He acentuado la 'a' de más y claro ahora al cortar y quedarlo en 30:

'Esto es una prueba de más de 3'

Internamente están contando 31 y falla, por lo cual habría que hacer una de estas opciones:

a) Quitar los acentos antes de cortar la cadena.

b) Tener en cuenta que la cadena medirá 30 + número de caracteres especiales.

Espero que sirva.

Saludos

lfer dijo...

Roberto, muy valiosos comentarios,
hay una nueva versión disponible que corrige lo siguiente:

- Tomo tu sugerencia sobre TRANSLATE en eli_acc y además elimino espacios para evitar resultados como los que reportaste
- Elimino caracteres multibyte antes de cortar a 30 la cadena de caracteres tal como sugieres

Gracias y saludos!

Anónimo dijo...

Amigos no se si será por mi desconocimiento de la fonética de nuestro idioma pero me parece que en la función
cnv_dos se debería remplazar la LL por Y de esta forma seria lo mismo YANDER que LLANDER.

corríjanme si me equivoco.

lfer dijo...

Es muy correcto, y quedó corregido ese detalle. Ahora YANDER y LLANDER retornan el mismo valor.

Muchas gracias!
Saludos

Anónimo dijo...

Quisiera que me explicaran cual es la idea de retener la primera letra y luego concatenársela a la cadena final,que se logra con esto??.

Luis dijo...

Hola chicos, lamento decirles que no funciona para el siguiente listado de palabras:
abakuá,avakuá,abacuá,avacuá,
habakuá,havakuá,habacuá,havacuá

en todos los casos el resultado rebería ser el mismo porque foneticamente las palabras son !exactas!

Luis dijo...

Quisiera que me explicaran cual es la idea de retener la primera letra y luego concatenársela a la cadena final,que se logra con esto??.

La verdad que no tiene importancia. Recuerda que este algoritmo surgió para censar nombres, así que me imagino que era una forma de tener las tarjetas perforadas ordenadas por la letra inicial.

Igual sucede con la cantidad de letras que produce el cod. originalmente 4 (A567) es debido al promedio de la palabra anglosajona y de las cantidad de letras que se eliminan, para el caso del español podría ser mayor.

lfer dijo...

abakuá,avakuá,abacuá,avacuá,
habakuá,havakuá,habacuá,havacuá


Está arreglado el bug, v y b no estaban asociados según el algoritmo.
Ahora todas las cadenas retornan A120.

Gracias!
Saludos

Rodrigo Muñoz dijo...

Excelente proyecto!
Lo he migrado a PHP de forma muy básica.
Comparto con ustedes el link para descargarlo: http://rapidshare.com/files/170388402/SoundEsp.class.php.zip.html

Saludos,

Rodrigo Muñoz.-

Domenec dijo...

Gracias por el trabajo.

En la version on-line de test obtengo errores al introducir Barcèlona o María.

Sugiero que trabaje con algunas tildes como à è ò que no son del español pero si usadas en territorio español en otras lenguas.

Tambien esta la ele "geminada" "l·l" que en la practica suena como una ele (el punto es el que sale con Shift-3 en un teclado español, aunque muchas veces se escribe como punto normal)

Domenec dijo...

Ops, y la "ny" que suena como una eñe, puestos a pedir ;-)

lfer dijo...

Se arregló el problema de los acentos de la applicación en test, ahora está funcionando correctamente.

Domenec:

De forma que este proyecto sea útil a la mayoría de las personas, hemos establecido un marco que contempla los caracteres y pronunciación neutra del castellano únicamente.

No es mi objetivo abarcar todas las variaciones regionales ni influencias de otros idiomas, sino lograr un código de base.

El paquete está disponible para que cualquiera pueda modificarlo a su gusto, por ejemplo para incluir los acentos que mencionas, o adaptarlo a la pronunciación de su territorio.

Como compensación, te acepto la traducción del sonido ny como ni, por no ofrecer conflicto en la mayoría de los países de habla hispana. :)

Gracias por tu aporte!!

Saludos

Anónimo dijo...

Excelente trabajo me han salvado la vida... si me sale este negocio quiero hacerles una donacion a los creadores.... jejejje
Una consulta:
Alguien de ustedes conoce una funcion similar para analisis de palabras que tienen errores tipograficos basados en las posiciones de las teclas en el teclado... es parecido a la funcion que crearon.. por ejemplo:

tierra = rierra por que la R esta al lado de la letra T en el teclado.

carcasco dijo...

Muchisimas gracias por tu paquete!

Yo lo implementé para buscar una frase con varios nombres en otra frase con varios nombres.

Ej. "Juan Perez" en "Juan Antonio Perez Lopez"

Analicé cada palabra de la primera frase en cada palabra de la segunda frase. Dependiendo del porcentaje de palabras de la primera frase que estuvieran en la segunda devolvia exito.

saludos!

elkami dijo...

hola pues primero te felicito
muy buen aporte, lo iba a pasa ra mysql pero no tengo mucha experiencia escribiendo funciones en mysql, de momento lo pase a ruby y parece estar funcionando igual, me genero algunas dudas la parte de
traslate(p_pal, '@AEIOUHWY','@')
que no entendi bien lo de la @ y termine cambiando las arrobas por '' y los ' ' por '' y parece ir ahora todo bien.
si alguien le interesa el codigo me lo hace saber.
no esta tan bien pensado hacia ruby solo me dedique a transcribirlo pero funciona.
les dejo mi correo si alguien les interesa.
omar.omarseb@gmail.com

Xibalba' dijo...

Que tal tengo una duda, he estado probando el algoritmo desde la pagina y por ejemplo si ingreso Vasquez o Bazques me da el mismo resultado B200 eso esta bien, pero si ingreso Valle y Baye me da el mismo resultado B200 tambien, no se que puedo hacer para que esta parte me retorne resultados distintos, o si ese era el funcionamiento esperado. Gracias.

lfer dijo...

Hola,

El caso de Valle y Baye es sutil y depende un poco de la región. En el Rio de la Plata, el Caribe o España, esa sílaba puede tener diferente sonido. En ese caso, es preferible dar el mismo valor equivalente.
De todas formas, si deseas personalizar el código puedes alterar la funcion cnv_dos dentro del paquete, y asignar una letra diferente a la sílaba YE.

Espero que te sirva.

Un abrazo

Xibalba' dijo...

Gracias por la pronta respuesta solo que creo que expuse mal mi pregunta. Mi dura era que parte del código podría modificar para que Basquez y Valle me den resultados distintos, porque intente con lo que me dijiste de modificar la silaba “YE” pero creo que no he entendido el funcionamiento de la rutina cnv_dos, perdón por tanta pregunta.

lfer dijo...

Hola,

Gracias por tu valioso aporte, he subido otra versión corrigiendo un error de conversión. Puede ser que solucione tu problema.

Saludos

Xibalba' dijo...

Gracias lo he estado testeando y si esta perfecto, gracias por la ayuda.

Ricardo de la Vega Cotarelo dijo...

La tabla de conversión expuesta en el paso básico número 3 no está actualizada conforme al código vigente de la aplicación.
Por lo demás, me ha sido muy útil para mi propia rutina (en ASP VbScript, para aplicarla próximamente en la rutina de búsqueda interna de mi sitio web).
Muchas gracias.

Akinet dijo...

Hola lfer.
Antes que nada gracias por tu blog y en especial por este package que espero me sea útil (Estoy evaluando todavía si este es el camino por el que quiero que vaya mi proyecto).

He detectado que con ciertas combinaciones con la letra G el resultado que devuelve no está en el formato A000. Te dejo unos ejemplos: MAGUFO - MG10, MANGANTE - M5G5, SUBMAGO - S15G.
No he mirado mucho pero creo que tiene que ver con la funcion map_num que no asigna equivalencia numérica a la G.

Gracias!!

lfer dijo...

Akinet, he corregido el bug, muchas gracias por el aporte! Acertaste en acusar a la funcion map_num :)

Ya está disponible para descargar en el link arriba.

Saludos

Tabrisius dijo...

Basarse en el codigo en ingles SOUNDEX, me parece que ya esa arrastar un erro de concepcion fonetica, por ejemplo segun tu codigo "Alicante" es igual que "Aliciente", con el codigo A425, cuando una sonaria "alikante" y la otra sonaria "alisiente", considero que el codigo debe ser realizado desde cero, y no basandose en el codigo anglosajón, porque asignar a la c un solo valor cuando puede sonar como S o como K es un error grave

lfer dijo...

Agradezco tu comentario y no comparto tu ejemplo. No pasa por ser inglés o castellano, el propio algoritmo SOUNDEX iguala los fonemas 'S' y 'C' (o K), eso es independiente del idioma. Tanto 'acronym' como 'asteroid' asignan un valor 2 en su segunda posición.

Bien es sabido que SOUNDEX tiene sus limitaciones y no es perfecto, pero no era mi intención arreglarlo sino implementar precisamente, SOUNDEX.

Anónimo dijo...

Hola que tal

Creo que encontré otro bug que tiene que ver con letras iguales al principio. por ejemplo vaca y vvaca, juan y jjuan. Cada pareja debería arrojarme el mismo resultado.

saludos y gracias por el paquete

lfer dijo...

Una observación: el algoritmo Soundex en su definición más conocida no indica un tratamiento de errores tipográficos de las cadenas de entrada, tales como consonantes dobles, espacios, o símbolos no alfanuméricos.

Teniendo esto en cuenta, las palabras "vvaca" y "vaca" deberían tener diferente codificación si seguimos las reglas basicas, ya que la segunda consonante luego de la primera es 'v'(=1) en el primer caso y 'c'(=0) en el segundo.

La implementación de Oracle aparentemente tiene un tratamiento de la cadena para este tipo de casos, pero sería una característica extra algoritmo.

Me parece buena idea agregar ese "accesorio" para acercarnos lo más posible a la función de Oracle.

Próximamente estaré disponibilizando una nueva versión.

Gracias por la contribución!

Saludos

Anónimo dijo...

Hola, buenos aportes pero quisiera saber porq sale q es igual 'cemento' y 'zenda'

Juan dijo...

Si yo también probé Cemento con Zenda y los resultados indican que son iguales.

Alejandro dijo...

Hola,
muchas gracias por tu código. Ha sido de gran ayuda.

Creo que ayudaría en la búsqueda fonética, convertir los números a palabras:

1 = UNO
2 = DOS
...
22= VEINTIDOS O VEINTE Y DOS
...

Saludos,

Gustavo Andrés Arriola O. dijo...

Hola!

Antes que nada, felicitaciones por la publicación, es muy interesante, de hecho, lo he adaptado a un proyecto en otro lenguaje.

Sin embargo, he detectado que para algunas palabras produce el mismo resultado, por ejemplo:
"Nacional" y "Nacionalidad" lo mismo ocurre con palabras cortas como "ley" y "la".

Un cordial saludo,

Anónimo dijo...

Estimado, buenas tardes: mi nombre es Luciano, trabajo buscando coincidencias de nombres, quería saber cómo se puede "ajustar" un poco más el parámetro de búsqueda de coincidencia. Por ejemplo, si comparo a JOSE LUIS GONZALEZ IBAÑEZ contra JOSE LUIS IBAÑEZ MIÑO que no haya coincidencia entre ellos porque son personas distintas. Otro ejemplo: MARIA DEL CARMEN GONZALEZ CONTRA MARIA DEL ROSARIO GONZALEZ. ¿se podría ajustar el parámetro o porcentaje de coincidencia?

Anónimo dijo...

Hola e implementado un algoritmo propio basandome en el soundesp y me esta funcionando bien. Tal vez me podrian ayudar a hacerlo mas robusto. donde prodria publicar el algoritmo (esta en MSQL) pero es facil de convertir a oracle.
mi correo es eampuerog@gmail.com

Kaminal Juyu dijo...

Gracias por el aporte. En Sql Server hay una función que se llama Difference, que devuelve un valor numérico de 0 a 4, el 4 significa que dos cadenas comparadas son iguales o casi iguales. Como aplico esto aquí. Como hago una comparación entre dos cadenas, como sé que son iguales o casi iguales??? Shuwer David L

Juan Carlos Delgado Loyola dijo...

Hola. El algoritmo funciona mejor. Necesito detalles sobre tí, para incluirlo como una cita de autor de la versión SOUNDESP en mi tesis de grado sobre coincidencia de nombres. Es especialmente bueno en el tratamiento que hace de las letras combinadas con el sonido de la CH y la LL. Te he enviado un correo al gmail. Gracias.

Delgado Juan C.

Javier Castillo dijo...
Este comentario ha sido eliminado por el autor.
Javier Castillo dijo...

@lfer: gracias por este gran aporte a la informática hispana.
@Domenec: has conseguido implementar el SOUNESP para el catalá?
Yo creo que como es una lengua que difiere de manera considerable al español (ya que el catalán no es una variante del español (si del latín), sino más bien una mezcla de 5 idiomas, español, francés, portugués, italiano y rumano.

Valdría la pena modificar SOUNDESP, para hacer un SOUNDCAT???

O una variante de SOUNDESP en donde pueda recibir un parametro de la cultura o variante, por ejemplo: CA, EU, GA (Catalán, Euskera, Gallego).

Que opinan?

Yo podría buscar implementar para catalán, pero no soy catalán, ni muy experto en PL-SQL, aunque no parece muy complicado el código. Soy mexicano, así que agradecería mucho alguna ayuda de un parlante catalán que conozca bien las reglas de pronunciación.

Saludos y nuevamente muchas gracias @lfer por este GRAN GRAN aporte.

Orlando Ruiz dijo...

Hola me he encontrado que morada, mareada, mirada, mierda, muerda dan el mismo resultado M630... no sé si asi debe ser. Gracias y saludos