Trova gli ID in un file che non si trovano in un altro


9

Ho due file:

abc.txt

abcd
xyz
pqrs

mno.txt

zzon
mkno
abcd
  • Voglio verificare se "abcd" è presente nel file mno.txt .
  • Non è necessario che se "abcd" è il primo in abc.txt , lo sarà anche per primo in mno.txt .
  • Ci sono migliaia di tali ID in entrambi i file.
  • Voglio anche verificare quanti ID non sono presenti in mno.txt che sono in abc.txt .

Come posso fare questo ?

Risposte:


19

Se il tuo obiettivo è trovare linee comuni o non comuni, commsarebbe il mio comando preferito qui.

Confronta due file e mostra, in tre colonne, righe univoche per il file 1, righe uniche per il file 2 e righe visualizzate in entrambi i file, rispettivamente. È possibile passare flag per eliminare anche questo output. Ad esempio comm -1 file1 file2sopprimerà la prima colonna, le cose uniche di file1. comm -12 file1 file2mostrerebbe cose solo in entrambi i file.

C'è un grande avvertimento: l'input deve essere ordinato. Possiamo aggirare questo.

Questo ti mostrerà tutto in ABC che non è in MNO:

comm -23 <(sort abc.txt) <(sort mno.txt)

E puoi convogliarlo wc -lper ottenere un conteggio.


La ragione per cui vado commè che una volta ordinati i file, il confronto fianco a fianco è davvero semplice dal punto di vista computazionale. Se hai a che fare con milioni di questi, questo farà la differenza.

Questo può essere dimostrato con un paio di file finti. Ho un computer abbastanza veloce, quindi per mostrare la differenza tra gli approcci, ho bisogno di un set di campioni piuttosto mastodontico. Sono andato a 10 milioni di stringhe da 10 caratteri per file.

$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > abc.txt
$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > mno.txt

$ time comm -23 <(sort abc.txt) <(sort mno.txt) | wc -l
... 0m10.653s

$ time grep -Fcxv -f abc.txt mno.txt
... 0m23.920s

$ time grep -Fcwv -f abc.txt mno.txt
... 0m40.313s

$ time awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt | wc -l
... 0m12.161s

L'ordinamento è ciò che richiede la maggior parte del tempo nel mio. Se facciamo finta che abc.txt sia statico, possiamo preordinarlo e questo rende i confronti futuri molto più veloci:

$ sort abc.txt abc-sorted.txt
$ time comm -23 abc-sorted.txt <(sort mno.txt) | wc -l
... 0m7.426s

Potresti guardare questi e considerare alcuni secondi irrilevanti, ma devo evidenziare che questi sono in esecuzione su una macchina di fascia alta. Se vuoi farlo su un (ad es.) Raspberry Pi 3, vedrai turnaround molto più lenti e la differenza aumenterà fino al punto in cui conta davvero.


7

per ottenere un elenco:

grep -Fwf abc.txt mno.txt

ti dà qualcosa di simile a:

abcd
abcd
zef

se vuoi ottenere un elenco univoco, usalo come:

grep -Fwf abc.txt mno.txt | sort | uniq

e per ottenere i conteggi:

grep -Fcwv -f abc.txt mno.txt

  • -F significa: interpretare PATTERN come un elenco di stringhe fisse anziché espressioni regolari.
  • -fottenere modelli da FILE che saranno abc.txt.
  • cerchiamo mno.txtschemi
  • -c Conta il numero di partite
  • -wCerca solo "parole intere": la sottostringa corrispondente deve essere all'inizio della riga o preceduta da un carattere costitutivo non di parole. Allo stesso modo, deve essere alla fine della riga o seguito da un carattere costitutivo non di parole. I caratteri costituenti parole sono lettere, cifre e il trattino basso.
  • -v Invertire la ricerca

1
Se l'OP vuole un conteggio di non corrispondenze, non dovrebbe essere più simile grep -cxvFf abc.txt mno.txt?
Steeldriver,

L'ho appena visto: D ... sei sempre qui per salvarmi: D
Ravexina,

Cordiali saluti fgrep, i egrepsupplenti sono apparentemente deprecati (a favore di grep -F, grep -Eanche se non sono sicuro che qualcuno creda che se ne andranno mai
Steeldriver,

È necessario utilizzare -xquando si utilizza -F?
Ravexina,

1
Dipende da cosa l'OP vuole contare esattamente - ad es. Se contiene mno.txt abcdefdovrebbe essere considerato come una corrispondenza o una non corrispondenza abcd?
Steeldriver,

3

Potremmo usare awk per fare il lavoro passando due file, prima il file modello, quindi il file che vogliamo controllare. Quando leggiamo il primo file, lo sappiamo NR==FNRe in quel momento possiamo leggere le righe nell'array. Quando NR!=FNRcontrolliamo se è impostata la matrice per tale linea.

$ cat abc.txt                                                      
abcd
xyz
pqrs
$ cat mno.txt                                                      
zzon
xyz
mkno
abcd
$ awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt         
xyz
abcd

Al contrario, possiamo annullare il motivo per stampare quelle linee che non sono presenti abc.txt

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt       
zzon
mkno

E se vogliamo stampare il conteggio di quelli che possiamo impiegare sorte wc:

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt | sort -u | wc -l         
2

Penso che tu l'abbia sbagliato. Per quanto ho capito la domanda, OP vuole calcolare la (dimensione di) la differenza impostata di abc.txt- mno.txtche è {xyz, pqrs}.
David Foerster,

2

Se uno degli elenchi di parole non è ordinato, sarebbe più veloce utilizzare una struttura dati set efficiente per ricordare le parole comuni.

Pitone

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = frozenset(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = frozenset(map(str.rstrip, subtrahend_file))

difference = minuend - subtrahend
#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

Uso:

python3 set-difference.py abc.txt mno.txt

Python (più efficiente)

Se vuoi risparmiare un po 'di memoria per l'archiviazione intermedia e il tempo di esecuzione puoi utilizzare questo programma leggermente più difficile da capire:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = set(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = map(str.rstrip, subtrahend_file)
    minuend.difference_update(subtrahend)
    difference = minuend
    del minuend

#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

Prestazione

Dato abc.txte mno.txtcon 1 mio di righe non ordinate di 10 caratteri ASCII casuali ciascuno (vedi la risposta di Oli per il set-up):

$ time python3 set-difference.py abc.txt mno.txt
user    0m10.453s

vs.

$ export LC_COLLATE=C
$ time sort abc.txt > abc_sorted.txt
user    0m10.652s
$ time sort mno.txt > mno_sorted.txt
user    0m10.767s
$ time comm -23 abc_sorted.txt mno_sorted.txt | wc -l
9989882
user    0m1.600s

totale: 23 secondi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.