タブで区切られた2つの大きなファイル(> 10 GB)があり、並べ替えるとその内容が同じであることがわかります。
Source
ただし、同じ「キー」(ここでキーは列に基づいてグループ化されたLocation
行として定義されています)を共有するときに、行の順序と置き換えられた行のインデックスに興味があります。
つまり、これら2つのファイル間の行は、同じグループに属している場合にのみ(つまり、同じソースと場所を共有する場合)、互いに比較する必要があります。
たとえば、次の例では、行4、5、6をfile1.tsv
次の行4、5、6と比較する必要があります。file2.tsv
注:ファイルは通常のTSVです。より見やすくするために、ここに余分なスペースを追加し、列を中央に揃えて右に揃えます。このスペースは元のファイルの一部ではありません。
ファイル1.tsv
Identifier Position Source Location
AY1:2301 87 ch1 14
BC1U:4010 105 ch1 14
AC44:1230 90 ch1 15
AJC:93410 83 ch1 16
ABYY:0001 101 ch1 16
ABC:01 42 ch1 16
HH:A9CX 413 ch1 17
LK:9310 2 ch1 17
JFNE:3410 132 ch1 18
MKASDL:11 14 ch1 18
MKDFA:9401 18 ch1 18
MKASDL1:011 184 ch2 50
LKOC:AMC02 18 ch2 50
POI:1100 900 ch2 53
MCJE:09HA 11 ch2 53
ABYCI:1123 15 ch2 53
MNKA:410 1 ch2 53
ファイル2.tsv
Identifier Position Source Location
AY1:2301 87 ch1 14
BC1U:4010 105 ch1 14
AC44:1230 90 ch1 15
ABC:01 42 ch1 16
ABYY:0001 101 ch1 16
AJC:93410 83 ch1 16
HH:A9CX 413 ch1 17
LK:9310 2 ch1 17
MKASDL:11 14 ch1 18
JFNE:3410 132 ch1 18
MKDFA:9401 18 ch1 18
MKASDL1:011 184 ch2 50
LKOC:AMC02 18 ch2 50
MNKA:410 1 ch2 53
POI:1100 900 ch2 53
ABYCI:1123 15 ch2 53
MCJE:09HA 11 ch2 53
「diff」に似ていますが、「グループ」レベルで実行したいです(行がSource
同じ合計を共有している場合にのみ比較されますLocation
)。
抽出したいオリジナル同じ「ソース/位置」内で行の順序が「交換」されると、「行番号」グループ「(またはキー)。
行全体の内容が一致する必要があります。
しかし、どうすればいいのかわかりません。私が考えることができるのはforループを書くことだけです。これは、元のデータセットに数百万の行がある場合、非常に非効率的です。
予想される結果:
Group_Source:Location df1.index df2.index
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 15
ch2:53 15 17
ch2:53 17 14
仮定:
- 両方のデータフレームは同じ数の行を持ちます。
- 両方のデータフレームは同じです(行の順序のみが変わるため、両方ともソース基準、位置基準、位置基準、識別子基準でソートされている場合はまったく同じです)。
- 「交換された」行は、常にすべての列の内容と正確に一致します。
答え1
入力ファイルのサイズのため、これは私が使用できるまれなケースの1つなので、getline
> 10Gの代わりに一度に数行だけメモリに保存します。
$ cat tst.awk
BEGIN {
OFS = "\t"
print "Group_Source:Location", "df1.index", "df2.index"
}
NR != FNR { exit }
{ srcLoc = $3 ":" $4 }
srcLoc != prevSrcLoc {
if ( NR > 1 ) {
diff()
}
prevSrcLoc = srcLoc
}
{
file1[$1,$2] = FNR - 1
if ( (getline < ARGV[2]) > 0 ) {
file2[$1,$2] = FNR - 1
}
}
END { diff() }
function diff( idPos) {
for ( idPos in file1 ) {
if ( file1[idPos] != file2[idPos] ) {
print prevSrcLoc, file1[idPos], file2[idPos]
}
}
delete file1
delete file2
}
$ awk -f tst.awk file1.tsv file2.tsv
Group_Source:Location df1.index df2.index
ch1:16 6 4
ch1:16 4 6
ch1:18 10 9
ch1:18 9 10
ch2:53 17 14
ch2:53 15 17
ch2:53 14 15
もっと情報がgetline
欲しいなら読んでくださいhttp://awk.freeshell.org/AllAboutGetline。
Identifier
上記のコードは、2つのファイル間の4つのフィールドをすべて比較するため、入力中および/または繰り返される場合にも機能します。Position
例の入力に示すように、ソースとロケーションの値が2つのファイル間で同じ順序であるとします。
答え2
たとえば、次のようになりますawk
。
$ awk '{
if(FNR==1){
next
}
else if(FNR==NR){
a[$1]=FNR-1;
}
else if ( a[$1] != FNR-1 ){
print $3":"$4, FNR-1, a[$1]
}
}' file1.tsv file2.tsv
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 17
ch2:53 15 14
ch2:53 17 15
説明する
if(FNR==1){ next }
:FNR
現在読んでいるファイルの行番号(レコード番号)を保存します。したがって、これが入力ファイルの最初の行である場合は、ヘッダーを処理したくないので、スキップしてください。else if(FNR==NR){ ... }
:NR
どのファイルを読んでいるかに関係なく、現在の入力行番号を保存します。したがって、FNR
同じNR
場合、最初のファイルを読んでいるという意味です。a[$1]=FNR-1
: したがって、これが最初のファイルの場合は、最初のフィールドを連想配列のインデックス(キー)として保存し、その値は現在のファイルの行番号(FNR
)になりますが、計算を望まないので1を減算します。ヘッダー。else if ( a[$1] != FNR-1 ){
:これはelse if
以前のファイルに関連しているため、FNR
このファイルが同じではない場合にのみ入力されるため、NR
2番目のファイルを読み取る場合にのみ入力されます。だから私たちが2番目のファイルを読むならそしてこの行の最初のフィールドの配列に格納されている値は、a
現在のファイルの行番号から1を引いたものと等しくないため、印刷しようとしています。print $3":"$4, FNR-1, a[$1]
:したがって、3番目のフィールドa:
と4番目のフィールドを印刷し、FNRマイナス1とa
最初のフィールドの配列に格納されている値を印刷します。
最後に、パディングとヘッダーを使用してきれいに印刷するには、次のようにします。
$ awk 'BEGIN{
printf "%-26s%-12s%-12s\n", \
"Group_Source:Location","df1.index","df2.index"
}
{
if(FNR==1){ next }
else if(FNR==NR){ a[$1]=FNR-1 }
else if ( a[$1] != FNR-1){
printf "%-26s%-12s%-12s\n", $3":"$4, FNR-1, a[$1]
}
}' file1.tsv file2.tsv
Group_Source:Location df1.index df2.index
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 17
ch2:53 15 14
ch2:53 17 15
重要:この方法を使用するには、最初のファイルの各行(ヘッダーを除く)ごとに少量のデータをメモリに保持する必要があります。これは大容量ファイルの場合に問題になる可能性がありますが、これらのタスクが実行される可能性のあるほとんどのコンピュータではそうでない可能性があります。これが問題だったらおすすめしたいエドの答えこれははるかに高速で、メモリの問題もないはずです。