7

Ho un problema per il confronto di due file. In sostanza, quello che voglio fare è un diff UNIX-like tra due file, ad esempio:Genera differenza "fuzzy" di due file in Python, con confronto approssimativo di float

$ diff -u destra-file-file di sinistra

Tuttavia i miei due file contengono galleggianti; e poiché questi file sono stati generati su architetture distinte (ma calcolano le stesse cose), i valori fluttuanti non sono esattamente gli stessi (possono essere diversi, ad esempio, 1e-10). Ma quello che cerco 'differenziando' i file è quello di trovare quelle che considero differenze significative (per esempio la differenza è maggiore di 1e-4); mentre utilizzo il comando UNIX diff, ottengo quasi tutte le mie righe contenenti i valori fluttuanti diversi! Questo è il mio problema: come posso ottenere un diff risultante come 'diff -u' fornisce, ma con meno restrizioni riguardo il confronto dei float?

Ho pensato di scrivere uno script di Python per farlo e ho scoperto il modulo difflib che fornisce un confronto simile a diff. Ma la documentazione che ho trovato spiega come usarlo così com'è (attraverso un singolo metodo) e spiega gli oggetti interni, ma non riesco a trovare nulla riguardo a come personalizzare un oggetto difflib per soddisfare le mie esigenze (come riscrivere solo il metodo di confronto o tale) ... Immagino che una soluzione potrebbe essere quella di recuperare la differenza unificata, e analizzarla 'manualmente' per rimuovere le mie differenze 'false', in quanto non è elegante; Preferirei usare la struttura già esistente.

Quindi, qualcuno sa come personalizzare questa libreria in modo da poter fare ciò che cerco? O almeno indicarmi la giusta direzione ... Se non in Python, forse uno script di shell potrebbe fare il lavoro?

Qualsiasi aiuto sarebbe molto apprezzato! Grazie in anticipo per le tue risposte!

+1

Forse piace anche: [moduli Buono Python per confronto di stringhe sfocata ?] (http://stackoverflow.com/questions/682367/good-python-modules-for-fuzzy-string-comparison) – miku

+0

un'alternativa più semplice sarebbe quella di pre-processare correttamente i file e i float con l'accuratezza desiderata –

+0

Si prega di inviare un paio di righe corrispondenti da file di input di esempio? – smci

risposta

4

Nel tuo caso specializziamo lo general case: prima di passare le cose in difflib, dobbiamo rilevare e gestire separatamente linee contenenti float. Ecco un approccio di base, se vuoi generare i delta, le linee di contesto ecc. Puoi costruire su questo. Nota che è più facile confrontare fuochi con float come float reali piuttosto che come stringhe (anche se potresti codificare una colonna per colonna e ignorare i caratteri dopo 1-e4).

import re 

float_pat = re.compile('([+-]?\d*\.\d*)') 
def fuzzydiffer(line1,line2): 
    """Perform fuzzy-diff on floats, else normal diff.""" 
    floats1 = float_pat.findall(line1) 
    if not floats1: 
     pass # run your usual diff() 
    else: 
     floats2 = float_pat.findall(line2) 
     for (f1,f2) in zip(floats1,floats2): 
      (col1,col2) = line1.index(f1),line2.index(f2) 
      if not fuzzy_float_cmp(f1,f2): 
       print "Lines mismatch at col %d", col1, line1, line2 
      continue 
    # or use a list comprehension like all(fuzzy_float_cmp(f1,f2) for f1,f2 in zip(float_pat.findall(line1),float_pat.findall(line2))) 
    #return match 

def fuzzy_float_cmp(f1,f2,epsilon=1e-4): 
    """Fuzzy-compare two strings representing floats.""" 
    float1,float2 = float(f1),float(f2) 
    return (abs(float1-float2) < epsilon) 

Alcuni test:

fuzzydiffer('text: 558.113509766 +23477547.6407 -0.867086648057 0.009291785451', 
'text: 558.11351 +23477547.6406 -0.86708665 0.009292000001') 

e come bonus, ecco una versione che mette in evidenza colonna-diff:

import re 

float_pat = re.compile('([+-]?\d*\.\d*)') 
def fuzzydiffer(line1,line2): 
    """Perform fuzzy-diff on floats, else normal diff.""" 
    floats1 = float_pat.findall(line1) 
    if not floats1: 
     pass # run your usual diff() 
    else: 
     match = True 
     coldiffs1 = ' '*len(line1) 
     coldiffs2 = ' '*len(line2) 
     floats2 = float_pat.findall(line2) 
     for (f1,f2) in zip(floats1,floats2): 
      (col1s,col2s) = line1.index(f1),line2.index(f2) 
      col1e = col1s + len(f1) 
      col2e = col2s + len(f2) 
      if not fuzzy_float_cmp(f1,f2): 
       match = False 
       #print 'Lines mismatch:' 
       coldiffs1 = coldiffs1[:col1s] + ('v'*len(f1)) + coldiffs1[col1e:] 
       coldiffs2 = coldiffs2[:col2s] + ('^'*len(f2)) + coldiffs2[col2e:] 
      #continue # if you only need to highlight first mismatch 
     if not match: 
      print 'Lines mismatch:' 
      print ' ', coldiffs1 
      print '< ', line1 
      print '> ', line2 
      print ' ', coldiffs2 
     # or use a list comprehension like 
     # all() 
     #return True 

def fuzzy_float_cmp(f1,f2,epsilon=1e-4): 
    """Fuzzy-compare two strings representing floats.""" 
    print "Comparing:", f1, f2 
    float1,float2 = float(f1),float(f2) 
    return (abs(float1-float2) < epsilon)