2009-08-02 38 views
7

Estoy haciendo una migración de sitio web que implica la extracción de nombre y apellido de nombre completo. Dado que estos fueron creados por el usuario final, existen todo tipo de permutaciones (aunque en inglés y, en general, no demasiado extraño). Sobre todo puedo tomar la primera palabra como primer nombre y la última palabra como apellido, pero tengo algunas excepciones de los prefijos y sufijos ocasionales. Al revisar los datos y tratar de entender todas las posibles excepciones, me di cuenta de que este es un problema común que se ha resuelto al menos parcialmente muchas veces anteriormente.Separe el nombre y el apellido de la cadena de nombre completo en C#

Antes de reinventar la rueda, ¿alguien tiene alguna expresión regular que les haya funcionado o un código útil? El rendimiento no es una consideración ya que es una utilidad única.

Los valores típicos para ser manejados:

Jason Briggs, JD Smith, John Y ciudadana, J de Scott Myers, Bill Jackobson III, Sr. John Mills


Actualización: si bien es un problema común, la solución típica parece involucrar el manejo de la mayoría de los casos y limpiar manualmente el resto.

(Dada la frecuencia de este problema debe ser experimentado yo esperaba originalmente para encontrar una biblioteca de utilidades por ahí, pero no fue capaz de encontrar uno mismo con Google)

+0

¿Cuántos nombres tiene en la base de datos? –

+0

Aproximadamente 10K, por lo que todavía es práctico realizar una comprobación manual con una solución que maneja el 95% de los casos. Usando la primera y la última palabra manejó aproximadamente el 85% de los nombres. – Stuart

Respuesta

11

Mi recomendación sería la siguiente:

  1. Dividir los nombres en los espacios.

  2. Compruebe la longitud de la matriz devuelta. Si es 2, fácil de dividir. Si es más, el próximo.

  3. Compare el 1er valor para los prefijos (es decir, el Sr. Sra. Sra. Dr ..) ... en caso afirmativo, quítelo, pase al siguiente.

  4. Compare el 1er valor de longitud. Si solo tiene 1 carácter, combine los 2 primeros elementos en la matriz.

Todavía no es a prueba de tontos; sin embargo, debe abordar al menos el 80 por ciento de sus casos.

Espero que esto ayude.

+0

Estoy de acuerdo con esto, si puede dividir los datos en varios conjuntos de datos analizados de manera confiable, puede encontrar que los "casos problemáticos" restantes son lo suficientemente pequeños como para tener un control humano. – smercer

+0

James - gracias por tus ideas muy prácticas. Dado que los datos son generalmente bastante buenos, creo que esto debería abordar ~ 95% de los casos. – Stuart

2

Si esto es un asunto de peso entonces yo fuertemente considere pagar a alguien más que sea un especialista para que lo haga por usted.

Tendrán experiencia en el trabajo con conjuntos de datos mal estructurados.

No tengo ninguna afiliación con ellos, pero Melissa Data ofrece un servicio que parece adaptado a este tipo de cosas.

6

Probablemente sea imposible (de manera confiable).

Incluso si puede hacer eso para algunos nombres, obtendrá una persona española en algún momento, que escribirá los dos apellidos. O algunas personas (se olvidó de qué nacionalidad es) que pondrán "apellido nombre". O una de muchas otras situaciones ...

Lo mejor que puede hacer es probablemente dividir 2 palabras como nombre y apellido, y luego ir por el resto de forma manual (usted mismo, o contratar a algunos profesionales) ...

+0

Creo que tienes razón. Las permutaciones de lenguaje y la naturaleza no estructurada conducen a que esto sea un problema indeterminado como apuntó Apoorv. – Stuart

+0

Y no olvides apellidos como O'Neill y Van Der Spek y Van Eck y Hart-Mahon y de la Cruz ... –

2

Este es un problema indeterminado (o un problema de Oracle, como me gusta llamarlo) y no puede resolverse de manera confiable. Esto se debe a la existencia de nombres que son nombres y apellidos, por ejemplo, Stanley, Jackson, etc. Pero se puede intentar. Necesita escribir un programa de aprendizaje que recibirá un conjunto de nombres y apellidos y mantendrá un diccionario de estos nombres, correlacionado con la probabilidad de que sea un primer nombre.

Ahora, pase todos sus valores para migrar y usando estas probabilidades puede obtener una división razonable entre los nombres y apellidos. Además, si un nombre particular se vuelve ambiguo (totalmente sobre usted para definir ambiguo, pero lo definiría como el percentil inferior de todos los valores de probabilidad que he obtenido), puede marcarlo para su posterior revisión.

Espero que esto ayude.

¡Salud!

+0

Además, para casos como JD Smith, puedes tratar principalmente a JD como primer nombre y a Smith como último nombre. –

1

Si solo tiene unos pocos usuarios (< 100k), entonces vea si puede conseguir que alguien lo haga manualmente, y use su tiempo en algo que valga la pena. Dado que es un trabajo de una sola vez, el ROI es una mierda :-)

+0

Hay alrededor de 10 mil usuarios, por lo que probablemente tengas razón, es la compulsión irracional del programador pasar 5 horas tratando de resolver la mitad de los casos extremos que tomarían una hora de limpieza "manual" por parte de un interno. – Stuart

+0

exactamente ;-) Lo sé muy bien – Kasper

6

Lo más rápido que se puede hacer es un enfoque híbrido de algoritmo humano. No quiere perder tiempo armando un sistema que funcione el 99.99% del tiempo porque el último 5-10% de optimización lo matará. Además, no desea simplemente volcar todo el trabajo en una persona porque la mayoría de los casos (supongo) son bastante sencillos.

Por lo tanto, genere rápidamente algo parecido a lo sugerido por JamesEgger, pero observe todos los casos que parecen inusuales o que no se ajustan a sus conversiones predefinidas. Luego, simplemente revisa esos casos manualmente (no debería ser demasiados).

Usted podría ir a través de esos casos por sí mismo o subcontratar a otros usuarios mediante la creación de hits en Mechanical Turk:

http://aws.amazon.com/mturk/

(Asumiendo 500 casos en $ 0,05 (alta recompensa) el coste total debe ser $ 25 en la mayoría)

+0

Creo que tienes el equilibrio justo aquí. – Stuart

1

desenterré una muy simple (80% probablemente) de expresiones regulares que tenía en perl y añadido algunas felices C# nombres de grupo:

(?<title>(mr|ms|mrs|miss|dr|hon)\.?\s+)?(?<firstandmiddle>.+)\s+(?<last>((van|de|von)\s+)?\S+)(?<junior>\s+(jr|sr|ii|iii|iv)\.?)

¡Estoy publicando como wiki, así que cualquier persona puede agregar cosas que piensen que podrían ser útiles!

+0

Gracias Mike, veré cómo va mi información. – Stuart

1

Como otros señalaron, no existe una solución que funcione en todos los casos. Una razón para esto es que hay nombres que se pueden usar como nombre inicial y como apellido.

Puede utilizar una base de datos de con los nombres y averiguar qué parte del nombre son posibles nombres de pila. Si también conoce el país de la persona con un nombre en particular, puede aumentar mucho la precisión.

Para una base de datos libre de primeros nombres, ver this answer.

1

si su universo de datos es < nombres de 10k y es una operación de una sola vez implementar uno de los escenarios divididos descritos por otros carteles en un archivo intermedio, luego pasar manualmente y mirar y actualizar donde sea necesario (se sorprendería de cómo poco tiempo se necesita para examinar 10k nombres). Le tomará menos tiempo que tratar de encontrar y/o construir el algoritmo perfectamente implementado. Una vez que su universo de nombres> 100k vale la pena tratar de programar su salida y girar un archivo para la revisión manual y la modificación de todos los nombres que no le dan un nombre perfecto, apellido dividido.

1

 
static void CheckSuffix(ref string[] sArrName) 
     { 
      // Initialize suffixes 
      List<string> Suffixes = new List<string>(); 
      Suffixes.Add("jr"); 
      Suffixes.Add("sr"); 
      Suffixes.Add("esq"); 
      Suffixes.Add("ii"); 
      Suffixes.Add("iii"); 
      Suffixes.Add("iv"); 
      Suffixes.Add("v"); 
      Suffixes.Add("2nd"); 
      Suffixes.Add("3rd"); 
      Suffixes.Add("4th"); 
      Suffixes.Add("5th");

int i = 0; string suffix = string.Empty; foreach (string s in sArrName) { string[] schk = s.ToLower().Split(new char[] { ' ' }); foreach (string sverifiy in schk) { if (Suffixes.Contains(sverifiy)) { suffix = sverifiy; sArrName[i] = sArrName[i].Replace(sverifiy.ToUpper(), string.Empty).Trim(); }; } i += 1; } sArrName[2] = string.Format("{0}{1}", sArrName[2], (!string.IsNullOrEmpty(suffix) ? " " + suffix.ToUpper() + "." : string.Empty)); } public static string[] ExtractFullname(string name) { string[] sArr = { "", "", ""}; string[] sName = name.Split(new char[] { ' ', ',', '.' }, StringSplitOptions.RemoveEmptyEntries); int chkinitial = -1; for (int i = 0; i < sName.Length; i++) { if (sName[i].Length == 1) chkinitial = i; } switch (sName.Length) { case 1: sArr[0] = name; break; case 2: { int idx = name.IndexOf(','); if (idx != -1 && idx < name.Length) { sArr[0] = sName[1]; sArr[2] = sName[0]; } /* last, first */ else { idx = name.IndexOf(' '); if (idx != -1 && idx < name.Length) { sArr[0] = sName[0]; sArr[2] = sName[1]; } /* first last */ } } break; case 3: if (chkinitial == 1) { sArr[0] = sName[0]; sArr[1] = sName[1]; sArr[2] = sName[2]; } /* first middle last */ else if (chkinitial == 2) { sArr[0] = sName[1]; sArr[1] = sName[2]; sArr[2] = sName[0]; } /* last first middle */ else if (chkinitial == -1) { int idx = name.IndexOf(','); if (idx != -1) { if (idx == (sName[0].Length + sName[1].Length + 1)) { sArr[0] = sName[2]; sArr[2] = string.Format("{0} {1}", sName); } else { sArr[0] = string.Format("{1} {2}", sName); sArr[2] = sName[0]; } } else { sArr[0] = name; } } break; case 4: if (chkinitial == 1) { sArr[0] = sName[0]; sArr[1] = sName[1]; sArr[2] = string.Format("{2} {3}", sName); } /* first middle last */ else if (chkinitial == 2) { sArr[0] = string.Format("{0} {1}", sName); sArr[1] = sName[2]; sArr[2] = sName[3]; } /* last first middle */ else if (chkinitial == 3) { int idx = name.IndexOf(','); if (idx != -1) { if (idx == (sName[0].Length + sName[1].Length + 1)) { sArr[0] = sName[2]; sArr[1] = sName[3]; sArr[2] = string.Format("{0} {1}", sName); } else { sArr[0] = string.Format("{1} {2}", sName); sArr[1] = sName[3]; sArr[2] = sName[0]; } } else { sArr[0] = name; } } else if (chkinitial == -1) { int idx = name.IndexOf(','); if (idx != -1) { if (idx == (sName[0].Length)) { sArr[0] = string.Format("{1} {2} {3}", sName); sArr[2] = sName[0]; } else if (idx == (sName[0].Length + sName[1].Length + 1)) { sArr[0] = string.Format("{2} {3}", sName); sArr[2] = string.Format("{0} {1}", sName); } else if (idx == (sName[0].Length + sName[1].Length + sName[2].Length + 1)) { sArr[0] = sName[3]; sArr[2] = string.Format("{0} {1} {2}", sName); } else { sArr[0] = name; } } else { sArr[0] = name; } } break; default: /* more than 3 item in array */ sArr[0] = name; break; } CheckSuffix(ref sArr); return sArr; }
Cuestiones relacionadas