piw*_*iwi 7 python floating-point fuzzy-comparison inexact-arithmetic
我有比较两个文件的问题.基本上,我想要做的是两个文件之间类似于UNIX的差异,例如:
$ diff -u左文件右文件
但是我的两个文件包含浮点数; 并且因为这些文件是在不同的体系结构上生成的(但是计算相同的东西),浮动值并不完全相同(它们可能因1e-10而不同).但我通过'差异'文件寻找的是找到我认为是显着差异的东西(例如差异大于1e-4); 使用UNIX命令diff时,几乎所有包含浮动值的行都不同!这是我的问题:我如何得到像'diff -u'提供的结果差异,但对浮点数比较的限制较少?
我以为我会写一个Python的脚本来做到这一点,并找到了模块difflib,它提供了类似diff的比较.但是我发现的文档解释了如何按原样使用它(通过单一方法),并解释了内部对象,但我找不到任何关于如何自定义difflib对象以满足我的需求(比如仅重写比较方法或...)我想解决方案可能是检索统一的差异,然后"手动"解析它以消除我的"错误"差异,这不是优雅的; 我更愿意使用现有的框架.
那么,有没有人知道如何自定义这个lib,以便我可以做我想要的东西?或者至少指出我正确的方向......如果不是在Python中,也许shell脚本可以完成工作?
任何帮助将不胜感激!提前感谢您的回答!
对于您的情况,我们专门研究一般情况:在将内容传递到 difflib 之前,我们需要检测并单独处理包含浮点数的行。这是一个基本方法,如果您想生成增量、上下文行等,您可以在此基础上进行构建。请注意,将浮点数模糊比较为实际浮点数而不是字符串更容易(尽管您可以逐列编码不同,并忽略 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)
Run Code Online (Sandbox Code Playgroud)
一些测试:
fuzzydiffer('text: 558.113509766 +23477547.6407 -0.867086648057 0.009291785451',
'text: 558.11351 +23477547.6406 -0.86708665 0.009292000001')
Run Code Online (Sandbox Code Playgroud)
作为奖励,这里有一个突出显示列差异的版本:
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)
Run Code Online (Sandbox Code Playgroud)