2012-03-08 3 views
5

Sto scrivendo uno script ETL in Python che ottiene i dati nei file CSV, convalida e disinfetta i dati oltre a classificare o classificare ciascuna riga in base ad alcune regole, e infine lo carica in un database postgresql.Come classificare/categorizzare le stringhe in base alle regole di espressione regolare in Python

I dati si presenta così (semplificato):

 
ColA, ColB, Timestamp, Timestamp, Journaltext, AmountA, AmountB 

Ogni riga è una transazione finanziaria. Quello che voglio fare è categorizzare o classificare le transazioni sulla base di alcune regole. Le regole sono fondamentalmente espressioni regolari che corrispondono al testo nella colonna Journaltext.

Quindi quello che voglio fare è qualcosa di simile:

 
transactions = [] 
for row in rows: 
    t = Transaction(category=classify(row.journaltext)) 
    transactions.append(t) 

io non sono sicuro di come scrivere la funzione di classificare() in modo efficiente.

In questo modo le regole di classificazione lavori:

  • Ci sono una serie di categorie (più può e saranno aggiunti in seguito)
  • Ogni categoria ha un insieme di stringhe o espressioni regolari che, se Il testo ufficiale di una transazione corrisponde a questa espressione o contiene questa sottostringa, quindi questa transazione appartiene a questa categoria.
  • Una transazione può essere solo su una categoria
  • Se una categoria, FOO, ha sottostringhe 'foo' e 'Foo', e un'altra categoria BAR ha sottostringhe 'calcio', quindi una transazione con Journaltext = 'cibo' deve essere inserito nella categoria FOO, perché corrisponde solo a FOO, ma una transazione con Journaltext = 'footballs' deve essere inserita nella categoria BAR. Penso che questo significhi che devo mettere una priorità o simili per ogni categoria.
  • Se una transazione non corrisponde a nessuna delle espressioni, è None in category o verrà inserita in una categoria segnaposto denominata "UNKNOWN" o simile. Questo non importa molto.

Ok. Quindi, come rappresento queste categorie e le regole corrispondenti in Python?

Apprezzerei molto il vostro contributo. Anche se non è possibile fornire una soluzione completa. Tutto ciò che mi suggerisce di essere nella giusta direzione sarà fantastico. Grazie.

+1

Quanto grande è il vostro input (numero di categorie, i termini per categorie, numero di transazioni e dimensione media del testo)? –

risposta

2

senza alcun tipo di lanugine in più:

categories = [ 
    ('cat1', ['foo']), 
    ('cat2', ['football']), 
    ('cat3', ['abc', 'aba', 'bca']) 
] 

def classify(text): 
    for category, matches in categories: 
    if any(match in text for match in matches): 
     return category 
    return None 

In Python è possibile utilizzare l'operatore in per verificare sottoinsiemi di una stringa. È possibile aggiungere alcune cose come isinstance(match, str) per verificare se si sta utilizzando una semplice stringa o un oggetto di espressioni regolari. Quanto è avanzato diventa per te.

+0

Questo sembra elegante, tuttavia, non sembra funzionare se le categorie hanno più di una sottostringa. Come farlo? Dì, cat3 ha sottostringhe: 'aba', 'abe' e 'bca' – ervingsb

+0

@ervingsb - Dai un'occhiata a queste regolazioni - permettono più corrispondenze per categoria. La priorità è determinata dall'ordine in cui metti le cose nella lista principale 'categorie '. –

+1

@ervingsb: se si utilizzano già le espressioni regolari, è possibile anche adattare queste opzioni per utilizzare l'alternativa ('abc | abe | bca'), che semplifica il codice e * potrebbe * comportare prestazioni migliori (a seconda dell'implementazione di espressioni regolari). –

2

che dire di questa soluzione in pseudo pitone:

def classify(journaltext): 
    prio_list = ["FOO", "BAR", "UPS", ...] # "..." is a placeholder: you have to give the full list here. 
    # dictionary: 
    # - key is the name of the category, must match the name in the above prio_list 
    # - value is the regex that identifies the category 
    matchers = {"FOO": "the regex for FOO", "BAR": "the regex for BAR", "UPS":"...", ...} 
    for category in prio_list: 
     if re.match(matchers[category], journaltext): 
      return category 
    return "UNKOWN" # or you can "return None" 

Caratteristiche:

  • questo ha un prio_list, che è tutte le categorie in ordine decrescente.
  • cerca di far corrispondere l'ordine della lista.
  • Corrisponde a un'espressione regolare del dizionario corrispondente. Quindi i nomi delle categorie possono essere arbitrari.
  • la funzione restituisce il nome della categoria
  • se nulla corrisponde, quindi si ottiene il nome della categoria del segnaposto.

È anche possibile leggere l'elenco delle categorie di priorità e le regexs da un file di configurazione, ma questo è lasciato come esercizio per il lettore ...

+0

Come supporto più di una sottostringa/regex per la categoria FOO? Non riesco a inserire più di una chiave "foo" nel comando. – ervingsb

+0

è possibile inserire più di una sottostringa insieme in una singola espressione regolare: "(foo | bar)" corrisponde alle stringhe che contengono "foo" o "bar". E regexes cann be case insensitiv, vedi http://docs.python.org/howto/regex.html per un repox howto su python. –