2つのファイルA
-nodes_to_delete
およびB
-nodes_to_keep
があります。各ファイルには、数値IDを持つ多数の行があります。
nodes_to_delete
にはあるがnodes_to_keep
にはない数値IDのリストが必要です。 。
PostgreSQLデータベース内でそれを行うことは、不当に遅いです。 Linux CLIツールを使用してbashでそれを行うための素敵な方法はありますか?
UPDATE:これはPythonicの仕事のように思えますが、ファイルは本当に、本当に大きいです。 uniq
、sort
、およびいくつかの集合理論手法を使用して、いくつかの同様の問題を解決しました。これは、同等のデータベースよりも約2〜3桁高速でした。
comm コマンドはそれを行います。
誰かが私に数ヶ月前にshでこれを正確に行う方法を教えてくれたのですが、しばらくそれを見つけることができませんでした...そして見ながらあなたの質問につまずきました。ここにあります :
set_union () {
sort $1 $2 | uniq
}
set_difference () {
sort $1 $2 $2 | uniq -u
}
set_symmetric_difference() {
sort $1 $2 | uniq -u
}
comm
を使用-ソートされた2つのファイルを行ごとに比較します。
このコマンドは、deleteNodesに固有の行を返しますが、keepNodesの行は返しません。
comm -1 -3 <(sort keepNodes) <(sort deleteNodes)
keepNodes
およびdeleteNodes
という名前のファイルを作成し、comm
コマンドのソートされていない入力として使用してみましょう。
$ cat > keepNodes <(echo bob; echo amber;)
$ cat > deleteNodes <(echo bob; echo ann;)
デフォルトでは、引数なしでcommを実行すると、次のレイアウトで3列が印刷されます。
lines_unique_to_FILE1
lines_unique_to_FILE2
lines_which_appear_in_both
上記のサンプルファイルを使用して、引数なしでcommを実行します。 3つの列に注意してください。
$ comm <(sort keepNodes) <(sort deleteNodes)
amber
ann
bob
-Nで列1、2、または3を抑制します。列が非表示になると、空白が縮小することに注意してください。
$ comm -1 <(sort keepNodes) <(sort deleteNodes)
ann
bob
$ comm -2 <(sort keepNodes) <(sort deleteNodes)
amber
bob
$ comm -3 <(sort keepNodes) <(sort deleteNodes)
amber
ann
$ comm -1 -3 <(sort keepNodes) <(sort deleteNodes)
ann
$ comm -2 -3 <(sort keepNodes) <(sort deleteNodes)
amber
$ comm -1 -2 <(sort keepNodes) <(sort deleteNodes)
bob
最初にファイルをソートせずにcommを実行すると、正常に失敗し、どのファイルがソートされていないかに関するメッセージが表示されます。
comm: file 1 is not in sorted order
comm
は、この種のユースケース用に特別に設計されましたが、ソートされた入力が必要です。
awk
は、セットの違いを見つけるのがかなり簡単であり、sort
を必要とせず、柔軟性が向上するため、おそらくこれに適したツールです。
awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete
おそらく、たとえば、負でない数を表す行の違いだけを見つけたいと思うでしょう:
awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r {
a[$0]
next
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete
Postgresでもっと良い方法が必要なのかもしれませんが、フラットファイルを使用してより高速な方法を見つけることはできないでしょう。単純な内部結合を実行でき、両方のid colにインデックスが付けられていると仮定すると、非常に高速になります。
したがって、これは他の回答とは少し異なります。 C++コンパイラがまさに「Linux CLIツール」であるとは言えませんが、g++ -O3 -march=native -o set_diff main.cpp
を実行しています(main.cpp
に以下のコードを使用すると、トリックを実行できます)。
#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>
using namespace std;
int main(int argc, char** argv) {
ifstream keep_file(argv[1]), del_file(argv[2]);
unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
string line;
while (getline(del_file, line)) {
init_lines.erase(line);
}
copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}
使用するには、set_diff B A
(notA B
、B
はnodes_to_keep
であるため)を実行するだけで、結果の差が標準出力に出力されます。
コードをシンプルにするために、C++のいくつかのベストプラクティスを無視していることに注意してください。
多くの追加の速度最適化を行うことができます(より多くのメモリを犠牲にして)。 mmap
は、大きなデータセットの場合にも特に役立ちますが、それによりコードがより複雑になります。
データセットが大きいと述べたので、メモリ消費を削減するには、一度に1行ずつnodes_to_delete
を読み取ることをお勧めします。 nodes_to_delete
に大量の重複がある場合、上記のコードで採用されているアプローチは特に効率的ではありません。また、順序は保持されません。
簡単にコピーしてbash
に貼り付ける(つまり、main.cpp
の作成をスキップする):
g++ -O3 -march=native -xc++ -o set_diff - <<EOF
#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>
using namespace std;
int main(int argc, char** argv) {
ifstream keep_file(argv[1]), del_file(argv[2]);
unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
string line;
while (getline(del_file, line)) {
init_lines.erase(line);
}
copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}
EOF