Frage

Ich habe zwei Dateien A-nodes_to_delete und B-nodes_to_keep. Jede Datei hat eine viele Zeilen mit numerischen IDs.

Ich mag die Liste der numerischen IDs haben, die in nodes_to_delete sind, aber nicht in nodes_to_keep, z.B. alt text .

es in einer PostgreSQL-Datenbank zu tun, ist unzumutbar langsam. Jede ordentliche Weise es in der Bash mit Linux CLI-Tool zu tun?

UPDATE: Dies scheint ein Pythonic Job zu sein, aber die Dateien sind wirklich, wirklich groß. Ich habe einige ähnliche Probleme mit uniq, sort und einige Mengenlehre Techniken gelöst. Das war vor etwa zwei oder drei Größenordnungen schneller als die Datenbank-Äquivalente.

War es hilfreich?

Lösung

comm Befehl tut das.

Andere Tipps

Jemand hat mir gezeigt, wie vor genau dieses in sh ein paar Monate zu tun, und dann konnte ich es nicht für eine Weile finden ... und während ich auf der Suche stießen zufällig auf Ihre Frage. Hier ist sie:

set_union () {
   sort $1 $2 | uniq
}

set_difference () {
   sort $1 $2 $2 | uniq -u
}

set_symmetric_difference() {
   sort $1 $2 | uniq -u
}

Mit comm -. Es vergleicht zwei sortierte Dateien Zeile für Zeile

Die kurze Antwort auf Ihre Frage

Dieser Befehl Linien eindeutig deleteNodes zurück, aber nicht Linien in keepNodes.

comm -1 -3 <(sort keepNodes) <(sort deleteNodes)

Beispiel-Setup

Lassen Sie uns Erstellen Sie die Dateien mit dem Namen keepNodes und deleteNodes, und sie als unsortierter Eingang für den comm Befehl.

$ cat > keepNodes <(echo bob; echo amber;)
$ cat > deleteNodes <(echo bob; echo ann;)

Standardmäßig läuft comm ohne Argumente druckt 3 Spalten mit diesem Layout:

lines_unique_to_FILE1
    lines_unique_to_FILE2
        lines_which_appear_in_both

Mit unserer Beispieldatei oben, laufen comm ohne Argumente. Beachten Sie die drei Spalten.

$ comm <(sort keepNodes) <(sort deleteNodes)
amber
    ann
        bob

Unterdrückspaltenausgang

Suppress Spalte 1, 2 oder 3 mit -N; Beachten Sie, dass, wenn eine Spalte ausgeblendet ist, das Leerzeichen schrumpft auf.

$ 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

Sortierung ist wichtig!

Wenn Sie comm ausführen, ohne zuerst die Datei zu sortieren, es nicht ordnungsgemäß mit einer Meldung über die Datei sortiert ist es nicht.

comm: file 1 is not in sorted order

comm speziell für diese Art von Anwendungsfall entwickelt wurde, aber es erfordert sortierten Eingang.

awk ist wohl ein besseres Werkzeug für diese, da es ziemlich gerade ist vorwärts Einstelldifferenzdruck zu finden, erfordert keine sort und bietet zusätzliche Flexibilität.

awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete

Vielleicht zum Beispiel, würden Sie nur gerne den Unterschied in Linien finden, das nicht-negative Zahlen darstellen:

awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r {
    a[$0]
    next
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete

Vielleicht haben Sie einen besseren Weg, brauchen es in Postgres zu tun, kann ich so ziemlich Wette, dass Sie nicht einen schnelleren Weg finden, es zu tun flache Dateien. Sie sollten eine einfache innere Verknüpfung und unter der Annahme, dass beide ID cols indiziert sind, dass sollte sehr schnell sein.

tun können

Also, das ist etwas anders als die anderen Antworten. Ich kann nicht sagen, dass ein C ++ Compiler ist genau ein "Linux CLI-Tool", aber laufen g++ -O3 -march=native -o set_diff main.cpp (mit dem Code unten in main.cpp kann den Trick):

#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"));
}

zu bedienen, einfach laufen set_diff B A ( nicht A B, da B ist nodes_to_keep) und die daraus resultierende Differenz wird auf der Standardausgabe gedruckt werden.

Beachten Sie, dass ich habe entgangene ein paar C ++ Best Practices der Code einfacher zu halten.

Viele zusätzliche Geschwindigkeitsoptimierungen konnte (zu einem Preis von mehr Speicher) erfolgen. mmap wäre auch besonders nützlich sein für große Datenmengen, aber das würde den Code viel mehr beteiligt machen.

Da Sie erwähnt, dass die Datensätze groß sind, dachte ich, dass zu einem Zeitpunkt nodes_to_delete eine Linie zu lesen könnte eine gute Idee sein, den Speicherverbrauch zu reduzieren. Der Ansatz oben im Code ist nicht besonders effizient, wenn es viele Duplikate in Ihrem nodes_to_delete ist. Auch Auftrag wird nicht beibehalten.


Etwas leichter zu kopieren und in bash (d-Skipping Schaffung 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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top