7

Tengo un problema para comparar dos archivos. Básicamente, lo que quiero hacer es un diff UNIX entre dos archivos, por ejemplo:Generar una diferencia "difusa" de dos archivos en Python, con una comparación aproximada de flotantes

$ diff -u-archivo de la izquierda a la derecha en archivos

Sin embargo mis dos archivos contienen los flotadores; y debido a que estos archivos fueron generados en arquitecturas distintas (pero computando las mismas cosas), los valores flotantes no son exactamente los mismos (pueden diferir, digamos, 1e-10). Pero lo que busco al "diferir" los archivos es encontrar lo que considero diferencias significativas (por ejemplo, la diferencia es más de 1e-4); ¡Mientras utilizo el comando diff de UNIX, casi todas mis líneas que contienen los valores flotantes son diferentes! Ese es mi problema: ¿cómo puedo obtener un diff resultante como 'diff -u', pero con menos restricciones con respecto a la comparación de carrozas?

Pensé que escribiría una secuencia de comandos de Python para hacer eso, y descubrí el módulo difflib que proporciona una comparación tipo diff. Pero la documentación que encontré explica cómo usarla tal como está (a través de un único método) y explica los objetos internos, pero no puedo encontrar nada sobre cómo personalizar un objeto difflib para satisfacer mis necesidades (como reescribir solo el método de comparación o tal) ... Supongo que una solución podría ser recuperar la diferencia unificada y analizarla 'manualmente' para eliminar mis 'falsas' diferencias, por esto no es elegante; Preferiría usar el marco ya existente.

Entonces, ¿alguien sabe cómo personalizar esta lib para que pueda hacer lo que busco? O al menos apúntame en la dirección correcta ... Si no está en Python, ¿quizás un script de shell podría funcionar?

¡Cualquier ayuda sería muy apreciada! ¡Gracias de antemano por sus respuestas!

+1

Tal vez también les gusta: [módulos Buena Python para la comparación de cadenas difusa ?] (http://stackoverflow.com/questions/682367/good-python-modules-for-fuzzy-string-comparison) – miku

+0

una alternativa más simple sería preprocesar archivos y formato flotantes correctamente a la precisión deseada –

+0

¿Desea publicar un par de líneas correspondientes de los archivos de entrada de muestra? – smci

Respuesta

4

En su caso, nos especializamos en general case: antes de pasar cosas a difflib, necesitamos detectar y manejar por separado las líneas que contienen flotadores. Aquí hay un enfoque básico, si desea generar los deltas, líneas de contexto, etc., puede basarse en esto. Tenga en cuenta que es más fácil confundir-floats como flotadores reales en lugar de cadenas (aunque podría codificar una columna por columna, e ignorar los caracteres después de 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) 

Algunas pruebas:

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

y como un bono, aquí está una versión que pone de relieve la columna-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) 
Cuestiones relacionadas