Domanda

Ho due file A-nodes_to_delete E B-nodes_to_keep.Ogni file ha molte righe con ID numerici.

Voglio avere l'elenco degli ID numerici presenti in nodes_to_delete ma NON dentro nodes_to_keep, per esempio. alt text .

Farlo all'interno di un database PostgreSQL è irragionevolmente lento.Qualche modo accurato per farlo in bash usando gli strumenti CLI di Linux?

AGGIORNAMENTO: Sembrerebbe un lavoro Pythonic, ma i file sono davvero, davvero grandi.Ho risolto alcuni problemi simili utilizzando uniq, sort e alcune tecniche di teoria degli insiemi.Questo era circa due o tre ordini di grandezza più veloce degli equivalenti del database.

È stato utile?

Soluzione

Il href="http://unstableme.blogspot.com/2009/08/linux-comm-command-brief-tutorial.html" rel="noreferrer"> comm comando

Altri suggerimenti

Qualcuno mi ha mostrato come fare esattamente questo sh un paio di mesi fa, e poi non sono riuscito a trovare per un po '... e guardando mi sono imbattuto su sua domanda. Eccolo:

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

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

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

Usa comm - si confronterà due ordinato file riga per riga

.

La breve risposta alla tua domanda

Questo comando restituirà linee uniche per deleteNodes, ma non le linee in keepNodes.

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

Esempio di configurazione

Creiamo i file denominati keepNodes e deleteNodes, e li usano come input per il comando indifferenziato comm.

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

Per impostazione predefinita, in esecuzione comm senza argomenti stamperà 3 colonne con questo layout:

lines_unique_to_FILE1
    lines_unique_to_FILE2
        lines_which_appear_in_both

Usando il nostro esempio i file di cui sopra, eseguire comm senza argomenti. Notare le tre colonne.

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

Soppressione di uscita colonna

Elimina colonna 1, 2 o 3 con -N; notare che quando una colonna è nascosta, gli spazi si restringe fino.

$ 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

L'ordinamento è importante!

Se si esegue comm senza prima l'ordinamento del file, non riesce con grazia con un messaggio circa cui file non è ordinato.

comm: file 1 is not in sorted order

comm è stato specificamente progettato per questo tipo di caso d'uso, ma richiede input ordinato.

awk è senza dubbio uno strumento migliore per questo in quanto è abbastanza semplice da trovare impostare la differenza, non richiede sort, e offre una maggiore flessibilità.

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

Forse, per esempio, si desidera trovare solo la differenza nelle linee che rappresentano i numeri non negativi:

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

Forse hai bisogno di un modo migliore per farlo in Postgres, posso praticamente scommettere che non troverai un modo più veloce per farlo usando file flat.Dovresti essere in grado di eseguire un semplice inner join e supponendo che entrambi gli ID siano indicizzati, dovrebbe essere molto veloce.

Quindi, questo è un po 'diversa dalle altre risposte. Non posso dire che un compilatore C ++ è esattamente uno "strumento Linux CLI", ma in esecuzione g++ -O3 -march=native -o set_diff main.cpp (con il codice qui sotto in main.cpp può fare il trucco):

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

Per usare, basta eseguire set_diff B A ( non A B, dal momento che è B nodes_to_keep) e la differenza risultante verrà stampato sullo standard output.

Si noti che ho rinunciato migliori pratiche pochi C ++ per mantenere il codice più semplice.

Molte ottimizzazioni di velocità aggiuntivi potrebbero essere fatti (al prezzo di più memoria). mmap sarebbe anche particolarmente utile per grandi insiemi di dati, ma che sarebbe rendere il codice molto più coinvolto.

Dato che lei ha detto che i set di dati sono grandi, ho pensato che la lettura nodes_to_delete una linea alla volta potrebbe essere una buona idea per ridurre il consumo di memoria. L'approccio adottato nel codice di cui sopra non è particolarmente efficiente se ci sono un sacco di doppioni nella vostra nodes_to_delete. Inoltre, l'ordine non viene mantenuto.


Qualcosa di più facile da copiare e incollare nel bash (vale a dire saltare creazione di 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
scroll top