Comparing file and printing the matches and differences – StackOverflow
このコメントは質問を誤読していると思う.単なるファイル比較やソートされたデータの行を比較したいのであれば,diffやcommがリーズナブルだけど,バーティカルラインで区切られたデータフレームの比較という話だとすると,diffやcommで処理するのは必ずしもリーズナブルではないし(欲しい結果,その為の過程(処理)が変わってくる筈だから),Awkで処理したいというのは理にかなっていると思う.結局の所,何を仮定するのかで,処理は変わってくるが,少なくとも「車輪の再発明」云々って話では無いのでは.
%%bash echo """a|b|c|d|e|f|g q|w|e|r||f|""" > file1
%%bash echo """a|b|c|d|e|f|g q|w|e|r|t|f|u""" > file2
Pandas(Python)で考えると,例えば行列間比較(index/columnの対応付け)の場合はpandas.DataFrame.isinがリーズナブルチョイスだし,列間比較の場合は関連同様に,時間効率を優先するならpandas.DataFrame.isin,簡便さや汎用性を考えるなら除外マージ(欲しい列をqueryで指定すれば良い)がリーズナブルチョイスになる.
import pandas as pd df1 = pd.read_csv('file1', sep='|', header=None) df2 = pd.read_csv('file2', sep='|', header=None) print(df1) print(df2) cond = ~df1.isin(df2).all(1) print(pd.concat((df1[cond], df2[cond]))) print(pd.concat((df1, df2)).groupby(level=0).apply(lambda x: x.drop_duplicates(keep=False))) print(df1.merge(df2, how='outer', indicator=True)) print(df1.merge(df2, how='outer', indicator=True).query('_merge!="both"').drop('_merge', 1))
0 1 2 3 4 5 6 0 a b c d e f g 1 q w e r NaN f NaN 0 1 2 3 4 5 6 0 a b c d e f g 1 q w e r t f u 0 1 2 3 4 5 6 1 q w e r NaN f NaN 1 q w e r t f u 0 1 2 3 4 5 6 1 1 q w e r NaN f NaN 1 q w e r t f u 0 1 2 3 4 5 6 _merge 0 a b c d e f g both 1 q w e r NaN f NaN left_only 2 q w e r t f u right_only 0 1 2 3 4 5 6 1 q w e r NaN f NaN 2 q w e r t f u
さて,これをBashで考えると,まずコメントにある様にcommを使いたくなるが,
%%bash time { comm file1 file2 }
a|b|c|d|e|f|g q|w|e|r||f| q|w|e|r|t|f|u real 0m0.003s user 0m0.001s sys 0m0.002s
左から,左オンリー,右オンリー,両方にある項目が表示されるので,「ファイルを比較して一致項と相違項の表示」という質問のタイトルは満たしている.ただ,そもそも求められている処理は,バーティカルラインで区切られたデータフレーム(構造化されたデータ)の比較(セルをみたい)と考えられるので,commの結果をみても,どうしようもない.セパレータで区切って,文字列を処理すると云えば,やはり簡便なのはAwkで,行列間比較,列間比較,行間比較,簡単に変えられる.今の場合であれば,
%%bash time { awk 'BEGIN{FS=OFS="|"}NR==FNR{a[$0]++;next}{a[$0]++}END{for(k in a)if(a[k]<2)print k}' file1 file2 }
q|w|e|r|t|f|u q|w|e|r||f| real 0m0.003s user 0m0.001s sys 0m0.003s
これは,Pandasで云えば,
pd.concat((df1, df2)).drop_duplicates(keep=False)
0 1 2 3 4 5 6 1 q w e r NaN f NaN 1 q w e r t f u
と同じで,ユニーク行を選択しているので,ファイル内に同じ行が複数行ある場合は,
%%bash time { awk 'BEGIN{FS=OFS="|"} NR==FNR{a[$0]=1;next} (a[$0]<2){a[$0]++} END{for(k in a)if(a[k]<2)print k}' file1 file2 }
q|w|e|r|t|f|u q|w|e|r||f| real 0m0.003s user 0m0.001s sys 0m0.002s
とちょっと変えるだけ.で,セル間比較であれば,
%%bash time { awk 'BEGIN{FS=OFS="|"} NR==FNR{for(i=1;i<=NF;i++)a[FNR,i]=$i;next} {for(i=1;i<=NF;i++)if($i!=a[FNR,i]){b[i];j++};if(j==0)print "All columns are matching"} END{s="Column";for(k in b)s=s" "k((l++<j-1) ? " and" : " are not matching");print s}' file1 file2 }
All columns are matching Column 5 and 7 are not matching real 0m0.004s user 0m0.001s sys 0m0.003s
関連:
pythonで2つのデータフレーム間の違い(差集合)をみつける方法 – Pandasで2つのデータフレーム(pandas.DataFrame)を比較して片方にしか無い値をピックアップ