ファイルを比較して一致項と相違項の表示

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)を比較して片方にしか無い値をピックアップ

広告
カテゴリー: 未分類 パーマリンク

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください