2009-12-16 13 views
7

Posso eseguire espressioni regolari di base, ma questo è leggermente diverso, ovvero non so quale sarà il modello.Riconoscimento/compressione del modello di stringa di Python

Ad esempio, ho un elenco di stringhe simili:

lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt'] 

In questo caso il modello comune è di due segmenti di testo comune: 'sometxt' e 'moretxt', partendo e separati da qualcos'altro che è di lunghezza variabile .

La stringa comune e la stringa variabile possono naturalmente verificarsi in qualsiasi ordine e in qualsiasi numero di occasioni.

Quale sarebbe un buon modo per condensare/comprimere l'elenco di stringhe nelle parti comuni e nelle singole varianti?

Un'uscita esempio potrebbe essere:

c = ['sometxt', 'moretxt'] 

v = [('a','0'), ('b','1'), ('aa','10'), ('zz','999')] 
+2

Sembra difficile. Potrebbe essere d'aiuto se tu fornissi più background, come dire perché devi usare esattamente questo schema di compressione. – unwind

+1

aspetta, non sai cosa sono 'sometxt' e' moretxt' e dove sono? sarebbe un bel trucco quindi – SilentGhost

+2

Abbiamo bisogno di più casi di test! :) – Constantin

risposta

-1

Come circa subbing il testo noto, e quindi la scissione?

import re 
[re.sub('(sometxt|moretxt)', ',', x).split(',') for x in lst] 
# results in 
[['a', '0', ''], ['b', '1', ''], ['aa', '10', ''], ['zz', '999', '']] 
+3

L'OP non sa cosa 'sometxt' e' moretxt' sono – SilentGhost

+0

@SilentGhost Non sono sicuro che lo faccia o no, a dire il vero. La domanda lascia abbastanza poco chiaro. – mavnn

+0

@mavnn: leggiamolo di nuovo: * ovvero non so quale sarà il pattern. * – SilentGhost

1

Immagino che dovresti iniziare identificando sottostringhe (schemi) che si verificano frequentemente nelle stringhe. Dal momento che contare ingenuamente sottostringhe in un set di stringhe è piuttosto dispendioso dal punto di vista computazionale, dovrai inventare qualcosa di intelligente.

Ho eseguito la sottostringa contando su una grande quantità di dati utilizzando generalized suffix trees(example here). Una volta che conosci le sottostringhe/pattern più frequenti nei dati, puoi prenderli da lì.

3

Ecco uno spaventoso per far rotolare la palla.

>>> import re 
>>> makere = lambda n: ''.join(['(.*?)(.+)(.*?)(.+)(.*?)'] + ['(.*)(\\2)(.*)(\\4)(.*)'] * (n - 1)) 
>>> inp = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt'] 
>>> re.match(makere(len(inp)), ''.join(inp)).groups() 
('a', 'sometxt', '0', 'moretxt', '', 'b', 'sometxt', '1', 'moretxt', 'aa', '', 'sometxt', '10', 'moretxt', 'zz', '', 'sometxt', '999', 'moretxt', '') 

spero sua pura bruttezza ispirerà soluzioni migliori :)

+0

+1: molto elegante – codeape

+0

Bella soluzione, un po 'lento, ma posso aspettare. La bruttezza non mi infastidisce, un buon codice vale più dell'oro. Mai pensato di usare regex in questo modo, ma questo è ben oltre la mia conoscenza base delle espressioni regolari. Sto cercando di capire come separare l'output in una lista separata di costanti e variabili. – iCy

+2

@iCy, funziona molto più velocemente se c'è un carattere che non viene mai incontrato nei dati di input, come '\ 0'. In tal caso, sostituire entrambi ".join con" \ 0 ". Dà anche un risultato più bello. – Constantin

4

Questa soluzione trova i due sottostringhe più lunga comuni e li utilizza per delimitare le stringhe di input:

def an_answer_to_stackoverflow_question_1914394(lst): 
    """ 
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt'] 
    >>> an_answer_to_stackoverflow_question_1914394(lst) 
    (['sometxt', 'moretxt'], [('a', '0'), ('b', '1'), ('aa', '10'), ('zz', '999')]) 
    """ 
    delimiters = find_delimiters(lst) 
    return delimiters, list(split_strings(lst, delimiters)) 

find_delimiters e amici reperti i delimitatori:

import itertools 

def find_delimiters(lst): 
    """ 
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt'] 
    >>> find_delimiters(lst) 
    ['sometxt', 'moretxt'] 
    """ 
    candidates = list(itertools.islice(find_longest_common_substrings(lst), 3)) 
    if len(candidates) == 3 and len(candidates[1]) == len(candidates[2]): 
     raise ValueError("Unable to find useful delimiters") 
    if candidates[1] in candidates[0]: 
     raise ValueError("Unable to find useful delimiters") 
    return candidates[0:2] 

def find_longest_common_substrings(lst): 
    """ 
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt'] 
    >>> list(itertools.islice(find_longest_common_substrings(lst), 3)) 
    ['sometxt', 'moretxt', 'sometx'] 
    """ 
    for i in xrange(min_length(lst), 0, -1): 
     for substring in common_substrings(lst, i): 
      yield substring 


def min_length(lst): 
    return min(len(item) for item in lst) 

def common_substrings(lst, length): 
    """ 
    >>> list(common_substrings(["hello", "world"], 2)) 
    [] 
    >>> list(common_substrings(["aabbcc", "dbbrra"], 2)) 
    ['bb'] 
    """ 
    assert length <= min_length(lst) 
    returned = set() 
    for i, item in enumerate(lst): 
     for substring in all_substrings(item, length): 
      in_all_others = True 
      for j, other_item in enumerate(lst): 
       if j == i: 
        continue 
       if substring not in other_item: 
        in_all_others = False 
      if in_all_others: 
       if substring not in returned: 
        returned.add(substring) 
        yield substring 

def all_substrings(item, length): 
    """ 
    >>> list(all_substrings("hello", 2)) 
    ['he', 'el', 'll', 'lo'] 
    """ 
    for i in range(len(item) - length + 1): 
     yield item[i:i+length] 

split_strings divide le stringhe utilizzando i delimitatori:

import re 

def split_strings(lst, delimiters): 
    """ 
    >>> lst = ['asometxt0moretxt', 'bsometxt1moretxt', 'aasometxt10moretxt', 'zzsometxt999moretxt'] 
    >>> list(split_strings(lst, find_delimiters(lst))) 
    [('a', '0'), ('b', '1'), ('aa', '10'), ('zz', '999')] 
    """ 
    for item in lst: 
     parts = re.split("|".join(delimiters), item) 
     yield tuple(part for part in parts if part != '') 
+0

Perché il downvote? La mia risposta risolve almeno il problema. – codeape

+0

Avere un upvote per contrastare (e un po ') il driveby casuale. Ci sono alcune cose interessanti e utili nella tua risposta. – mavnn

+0

Grazie, molto apprezzato! – codeape

1

questo aspetto molto simile al LZW algoritmo per i dati (testo) di compressione. Ci dovrebbero essere implementazioni Python là fuori, che potresti essere in grado di adattarsi alle tue necessità.

Suppongo che tu non abbia una conoscenza a priori di queste stringhe che si ripetono spesso.

+0

è scritto "a priori" –

2

Questo sembra essere un esempio di longest common subsequence problem. Un modo potrebbe essere quello di vedere come vengono generati diffs. Il Hunt-McIlroy algorithm sembra essere stato il primo, ed è il più semplice, soprattutto perché apparentemente non è euristico.

Il primo collegamento contiene una discussione dettagliata e esempi di codice (pseudo). Supponendo, ovviamente, non sono completamente del brano qui.

+0

Sì, ho pensato alla differenza. pure, non ho idea di dove cominciare. – iCy

Problemi correlati