7

2 つのファイルの比較に問題があります。基本的に、私がやりたいことは、2 つのファイル間の UNIX のような diff です。たとえば、次のようになります。

$ diff -u 左ファイル 右ファイル

ただし、私の 2 つのファイルにはフロートが含まれています。また、これらのファイルは異なるアーキテクチャで生成された (ただし計算は同じ) ため、浮動小数点値はまったく同じではありません (たとえば、1e-10 だけ異なる場合があります)。しかし、ファイルを「比較」することで私が求めているのは、重要な違いと思われるものを見つけることです (たとえば、違いは 1e-4 を超えています)。UNIX コマンド diff を使用しているときに、浮動小数点値を含むほとんどすべての行が異なっています。それが私の問題です:「diff -u」が提供するような結果の差分を取得するにはどうすればよいですか?フロートの比較に関する制限は少なくなりますか?

私はそれを行うために Python のスクリプトを作成しようと考え、diff のような比較を提供するモジュール difflib を見つけました。しかし、私が見つけたドキュメンテーションは、それをそのまま(単一のメソッドを介して)使用する方法を説明し、内部オブジェクトを説明していますが、必要に応じてdifflibオブジェクトをカスタマイズする方法に関するものは何も見つかりません(比較メソッドのみを書き直すか、そのような)...解決策は、統一された違いを取得し、それを「手動で」解析して「誤った」違いを削除することであると思います。これはエレガントではありません。既存のフレームワークを使用したいと思います。

それで、私が求めていることができるように、このライブラリをカスタマイズする方法を知っている人はいますか? または、少なくとも私を正しい方向に向けてください... Pythonではない場合、シェルスクリプトで仕事ができるでしょうか?

どんな助けでも大歓迎です!ご回答ありがとうございます。

4

1 に答える 1

4

あなたのケースでは、一般的なケースに特化しています。difflib に物を渡す前に、フロートを含む行を検出して個別に処理する必要があります。これは基本的なアプローチです。デルタやコンテキスト行などを生成したい場合は、これに基づいて構築できます。float を文字列ではなく実際の float としてあいまい比較する方が簡単であることに注意してください (ただし、列ごとに異なるコードを記述して、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)

いくつかのテスト:

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

おまけとして、列の差分を強調するバージョンを次に示します。

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)
于 2011-07-03T01:07:15.680 に答える