2009-12-21 8 views
5

Ho una tabella contenente 3 milioni di record di persone su cui voglio eseguire la corrispondenza fuzzy usando q-grammi (sul cognome per esempio). Ho creato una tabella di 2 grammi che collega a questo, ma le prestazioni di ricerca non sono eccezionali su questo volume di dati (circa 5 minuti).ottimizzazioni corrispondenti approssimative di q-grammi

Io fondamentalmente due domande: (1) si può suggerire dei modi per migliorare le prestazioni per evitare una scansione di tabella (cioè dover contare comuni q-grammi tra la stringa di ricerca e 3 milioni di cognomi) (2) Con q-grammi, se A è simile a B e C è simile a B, implica C è simile ad A?

Cordiali saluti

Peter

risposta

6

Ho cercato nella stringa sfocata corrispondenza ultimamente, quindi anche a rischio di rispondere a una domanda abbandonata, qui va. Spero che tu lo trovi utile

Suppongo che ti interessano solo le stringhe per le quali la distanza di modifica è inferiore a un dato valore. E i tuoi q-grammi (o n-grammi) simile a questa

2-grams for "foobar": {"fo","oo","ob","ba","ar"} 
  1. Si potrebbe utilizzare posizionali q-grammi:

    "foobar": {("fo",1),("oo",2),("ob",3),("ba",4),("ar",5)} 
    

    L'informazione posizionale può essere utilizzato per determinare se un q-gram corrispondente è davvero una "buona corrispondenza".

    Per esempio, se siete alla ricerca di "foobar" con la massima edit distance di 2, questo significa che sei solo interessati a parole dove

    2-gram "fo" exists in with position from 1 to 3 or 
    2-gram "oo" exists in with position from 2 to 4 or 
    ... and so on 
    

    String "barfoo" doesn' t ottenere qualsiasi incontri perché le posizioni del altrimenti corrispondenti 2-grammi differiscono da 3.

  2. Inoltre, esso potrebbe essere utile u se la relazione tra modifica distanza e il conteggio dei q-grammi corrispondenti. L'Intution è che poiché

    una stringa s ha len (s) -q + ​​1 q-grammi

    e

    una singola operazione di modifica può influenzare massimo q q-grammi,

    si può dedurre che

    stringhe s1 e s2 all'interno modificare distanza d hanno almeno max (len (s1), len (s2)) - q + 1-qk che combina i q-grammi non posizionali.

    Se siete alla ricerca di "foobar" con una distanza massima di modifica di 2, un stringa di 7 caratteri corrispondenti (come "fotocar") dovrebbe contenere almeno due comuni 2-grammi.

  3. Infine, la cosa ovvia da fare è il filtro a per la lunghezza. La modifica della distanza tra due stringhe è a almeno la differenza delle lunghezze delle stringhe. Ad esempio se la soglia è 2 e si ricerca "foobar", "foobarbar" non può corrispondere a .

Vedere http://pages.stern.nyu.edu/~panos/publications/deb-dec2001.pdf per ulteriori e alcuni pseudo SQL.

2

carta interessante circa l'indicizzazione DNA q-grammi in modo da non dover eseguire la scansione l'intera tabella:

www.comp.nus.edu.sg/~atung/publication/qgram_edit.pdf

4

Hai sicuramente visto le ricerche di testo fuzzy ovunque. Ad esempio si digita "stck" ma in realtà si intende "stack"! Vi siete mai chiesti come funziona questa roba?

Ci sono un sacco di algoritmi per fare la corrispondenza del testo fuzzy, ognuno con i suoi pro e contro. I più famosi sono edit distance e qgram. Voglio concentrarmi su qgram oggi e implementare un campione.

In pratica i qgram sono l'algoritmo di corrispondenza stringa fuzzy più adatto per i database relazionali. È piuttosto semplice "q" in qgram verrà sostituito con un numero come 2-gram o 3-gram o anche 4-gram.

2 grammi significa che ogni parola è suddivisa in un gruppo di due caratteri grammi. "Stack" sarà suddiviso in un insieme di {"st", "ta", "ac", "ck"} o "database" sarà suddiviso in {"da", "in", "ta", "ba ", "come", "se"}.

Una volta che le parole sono suddivise in 2 grammi, possiamo cercare nel database un insieme di valori anziché una stringa. Ad esempio, se l'utente ha digitato "stck", qualsiasi ricerca per "stck" non corrisponde a "stack" perché manca "a", ma il set di 2 grammi "" st "," tc "," ck "} ha 2 righe in comune con il set di stack da 2 grammi! Bingo abbiamo trovato una partita molto simile. Non ha nulla in comune con il set di database da 2 grammi e solo 1 in comune con il set da 2 grammi di "stat", quindi possiamo facilmente suggerire all'utente che intendeva scrivere: primo "stack" o secondo "stella ".

Ora implementarlo utilizzando Sql Server: presuppone un set di dati di parole ipotetiche. È necessario avere una relazione molti a molti tra 2 grammi e parole.

CREATE TABLE Grams(twog char(2), wordId int, PRIMARY KEY (twog, wordId)) 

La tabella dei grammi deve essere raggruppata nel primo twog, quindi nella parola ID per le prestazioni. Quando si interroga una parola (ad esempio una pila), si inseriscono i grammi in una tabella temporanea. Per prima cosa creiamo alcuni milioni di record fittizi.

--make millions of 2grams 
DECLARE @i int =0 
WHILE (@i<5000000) 
BEGIN 
-- a random 2gram 
declare @rnum1 char = CHAR(CAST(RAND()*28 AS INT)+97) 
declare @rnum2 char = CHAR(CAST(RAND()*28 AS INT)+97) 
INS... INTO Grams (twog, wordId) VALUES (@rnum1 + @rnum2, CAST(RAND()*100000 AS int)) 
END 

Ora consente di interrogare la parola "stack", che sarà rotto per: { 'st', 'ta', 'AC', 'ck'} due grammi.

DECLARE @word TABLE(twog char(2)) -- 'stack' 
INS... INTO @word VALUES ('st'), ('ta'), ('ac'), ('ck') 

select wordId, count(*) from @word w inner join Grams g ON w.twog = g.twog 
GROUP BY wordId 

È necessario assicurarsi che SQL Server utilizza un mazzo di indice cluster cerca (o loockups) per l'esecuzione di questa query.Dovrebbe essere la scelta naturale ma a volte le statistiche potrebbero essere danneggiate o non aggiornate e SqlServer potrebbe decidere che una scansione completa è più economica. Questo di solito accade se non conosce la cardinalità della tabella di sinistra, ad esempio SqlServer può presumere che la tabella @word sia enorme e che milioni di loockup siano più costosi di una scansione dell'indice completa.

0

Ho un semplice miglioramento che non elimina la scansione, ma la velocizza se si utilizzano solo 2 grammi o solo 3 grammi: sostituire le lettere con i numeri. La maggior parte dei motori SQL funziona molto più velocemente confrontando i numeri.

Esempio: la nostra tabella di origine contiene voci di testo in una colonna. Creiamo una tabella temporanea in cui abbiamo diviso i nomi in 2-grammi utilizzando un

SELECT SUBSTRING (column, 1,2) as gram, 1 as position FROM sourcetable 
UNION 
SELECT SUBSTRING (column, 2,2) as gram, 2 as position FROM sourcetable 
UNION 
SELECT SUBSTRING (column, 3,2) as gram, 3 as position FROM sourcetable 

etc. 

Questo dovrebbe essere eseguito in un ciclo in cui i = 0 e j = la dimensione massima di una voce di origine.

Quindi prepariamo una tabella di mappatura che contiene tutti i possibili grammi di 2 lettere e include una colonna IDENTITY (1,1) chiamata gram_id. Possiamo ordinare i grammi per frequenza nel dizionario inglese ed eliminare i grammi più rari (come 'kk' o 'wq') - questo ordinamento potrebbe richiedere un po 'di tempo e ricerche, ma assegnerà i numeri più piccoli ai grammi più frequenti, che quindi miglioreremo le prestazioni se possiamo limitare il numero di grammi a 255 perché allora possiamo usare una colonna tinyint per gram_id.

Quindi ricostruiamo un altro tavolo temporaneo dal primo, dove usiamo il gram_id invece del grammo. Questo diventa il tavolo principale. Creiamo un indice nella colonna gram_id e nella colonna position.

Quindi, quando dobbiamo confrontare una stringa di testo con la tabella principale, prima divideremo la stringa di testo dividerla in 2 grammi, quindi sostituire i 2 grammi con il loro gram_id (usando la tabella di mappatura) e confrontarli a quello della tabella master

Questo fa molti confronti, ma la maggior parte di essi sono numeri interi a 2 cifre, il che è molto veloce.