2009-08-02 20 views
7

Sto eseguendo una migrazione del sito Web che comporta l'estrazione di nome e cognome da nome completo. Dato che questi sono stati creati dall'utente finale, esistono tutti i tipi di permutazioni (anche se l'inglese e generalmente non troppo strano). Principalmente posso prendere la prima parola come firstname e l'ultima word come lastname, ma ho alcune eccezioni dal prefisso e suffisso occasionale. Passando attraverso i dati e cercando di capire le possibili eccezioni, mi sono reso conto che questo è un problema comune che è stato risolto almeno in parte molte volte in passato.Nome e cognome separati dalla stringa fullname in C#

Prima di reinventare la ruota, qualcuno ha delle espressioni regolari che hanno funzionato per loro o un codice utile? Le prestazioni non sono una considerazione in quanto questa è un'utilità occasionale.

Valori tipici per essere gestite:

Jason Briggs, JD Smith, John Y Cittadino, J Scott Myers, Bill Jackobson III, Mr. John Mills


Aggiornamento

: mentre un problema comune, la soluzione tipica sembra implicare la gestione della maggior parte dei casi e la pulizia manuale del resto.

(Data la frequenza questo problema deve essere vissuto mi è stato originariamente aspettavo di trovare una libreria di utilità là fuori, ma non è stato in grado di trovare uno io con Google)

+0

Quanti nomi avete nel database? –

+0

Circa 10K, quindi è ancora pratico fare il controllo manuale con una soluzione che gestisce il 95% dei casi. Usando la prima e l'ultima parola gestita circa l'85% dei nomi. – Stuart

risposta

11

La mia raccomandazione sarebbe il seguente:

  1. Split i nomi degli spazi.

  2. Verificare la lunghezza dell'array restituito. Se 2, facile divisione. Se di più, dopo.

  3. Confrontare il 1 ° valore per i prefissi (ad esempio Mr. Mrs. Ms. Dr.) ... in tal caso, rimuoverlo altrimenti passa a successivo.

  4. Confrontare il 1 ° valore per la lunghezza. Se è solo 1 carattere, combina i primi 2 elementi dell'array.

Non è ancora infallibile; tuttavia, dovrebbe riguardare almeno l'80% dei casi.

Spero che questo aiuti.

+0

Concordo con questo, se riesci a suddividere i dati in vari set di dati correttamente analizzati, potresti scoprire che i "casi di problemi" rimanenti sono abbastanza piccoli da avere un handle umano. – smercer

+0

James - grazie per le tue idee molto pratiche. Dato che i dati sono generalmente abbastanza buoni, penso che questo dovrebbe riguardare il ~ 95% dei casi. – Stuart

2

Se si tratta di un solo colpo affare quindi vorrei fortemente considera di pagare qualcun altro che è uno specialista per farlo per te.

Saranno esperti nel funzionamento con set di dati scarsamente strutturati.

Non ho alcuna affiliazione con loro ma lo standard Melissa Data fornisce un servizio su misura per questo tipo di cose.

6

Probabilmente è impossibile (affidabile).

Anche se è possibile farlo per alcuni nomi, a un certo punto si otterrà uno spagnolo che scriverà entrambi i nomi di famiglia. O alcune persone (ha dimenticato quale nazionalità è) che inserirà "cognome". O uno dei molti altre situazioni ...

Il meglio che puoi fare è probabilmente diviso 2 parole come nome e cognome, quindi passare per il resto manualmente (da soli, o assumere alcuni professionisti) ...

+0

Penso che tu abbia ragione. Le permutazioni linguistiche e la natura non strutturata lo rendono un problema indeterminato come ha sottolineato Apoorv. – Stuart

+0

E non dimenticare i cognomi come O'Neill e Van Der Spek e Van Eck e Hart-Mahon e de la Cruz ... –

2

Questo è un problema indeterminato (o un problema Oracle come mi piace chiamarlo) ed è irrisolvibile in modo affidabile. Ciò è dovuto all'esistenza di nomi che sono sia nomi di battesimo che cognomi, ad es. Stanley, Jackson ecc. Ma si può dare una prova. Devi scrivere un programma di apprendimento a cui verranno assegnati un insieme di nomi e cognomi e manterrà un dizionario di questi nomi, mappato sulla probabilità che si tratti di un nome.

Ora, passare tutti i valori da migrare e utilizzando queste probabilità è possibile ottenere una divisione ragionevole tra nome e cognome. Inoltre, se un nome particolare diventa ambiguo (totalmente su di te per definire ambiguo, ma lo definirei come il 30 percentile in basso di tutti i valori di probabilità che ho ottenuto), puoi contrassegnarlo per una revisione successiva.

Spero che questo aiuti.

Cheers!

+0

Inoltre, per casi come JD Smith, puoi principalmente considerare JD come primo nome e come ultimo Smith nome. –

1

Se solo pochi utenti (< 100k) vedono se è possibile convincere qualcuno a farlo manualmente e utilizzare il proprio tempo per qualcosa che valga la pena. Dal momento che è un lavoro di una sola volta il ROI fa schifo :-)

+0

Ci sono circa 10.000 utenti, quindi probabilmente hai ragione: è la compulsione del programmatore irrazionale a dedicare 5 ore a cercare di risolvere metà dei casi limite che richiederebbero un'ora di pulizia manuale da parte di un tirocinante. – Stuart

+0

esattamente ;-) Lo so solo per bene – Kasper

6

La cosa più veloce da fare è un approccio algoritmo ibrido-umano. Non vuoi perdere tempo a mettere insieme un sistema che funziona il 99,99% delle volte perché l'ultimo 5-10% dell'ottimizzazione ti ucciderà. Inoltre, non vuoi semplicemente scaricare tutto il lavoro su una persona perché la maggior parte dei casi (immagino) sono abbastanza semplici.

Quindi, crea rapidamente qualcosa di simile a quello suggerito da JamesEggers, ma cattura tutti i casi che sembrano insoliti o che non si adattano alle tue conversioni predefinite. Quindi, passa semplicemente attraverso quei casi manualmente (non dovrebbe essere troppo).

Si potrebbe passare attraverso quei casi da soli o in outsourcing ad altri utenti attraverso la creazione di colpi in Mechanical Turk:

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

(Assumendo 500 casi a $ 0.05 (alto rendimento) il costo totale dovrebbe essere $ 25 più)

+0

Penso che tu abbia il saldo giusto qui. – Stuart

1

ho scavato molto semplice (80% probabilmente) regex che avevo in perl e ha aggiunto alcuni nomi C# Buon gruppo:

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

Sto postando come wiki, quindi chiunque si senta libero di aggiungere cose che pensano possano aiutare!

+0

Grazie Mike, vedrò come va sui miei dati. – Stuart

1

Come altri hanno sottolineato, non esiste una soluzione che funzioni in tutti i casi. Una ragione per questo è che ci sono nomi che possono essere usati sia come prima sia come cognome.

È possibile utilizzare un database e individuare quale parta del nome è possibile per i nomi. Se conosci anche il paese della persona con un nome particolare, puoi aumentare molto la precisione.

Per un database gratuito di primi nomi vedere this answer.

1

se l'universo dati è < 10k nomi e il suo accordo one time implementare uno degli scenari suddivisi descritti da altri poster in un file intermedio quindi passare manualmente e guardare e aggiornare dove necessario (saresti sorpreso di come poco tempo necessario per controllare i nomi 10k). Ti ci vorrà meno tempo di cercare e creare l'algoritmo perfettamente implementato. Una volta che il tuo universo di nomi> 100k vale la pena tentare di programmare la tua via d'uscita e girare un file per la revisione manuale e la modifica di tutti i nomi che non ti danno un nome perfetto, la suddivisione del cognome.

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; }
Problemi correlati