Rechercher des ID dans un fichier qui ne se trouvent pas dans un autre

9

J'ai deux fichiers:

abc.txt

abcd
xyz
pqrs

mno.txt

zzon
mkno
abcd
  • Je veux vérifier si "abcd" est là dans le fichier mno.txt .
  • Il n'est pas nécessaire que si "abcd" soit d'abord dans abc.txt , il le sera aussi d'abord dans mno.txt .
  • Il y a des milliers de ces identifiants dans les deux fichiers.
  • Je veux également vérifier combien d'ID ne sont pas dans mno.txt qui sont dans abc.txt .

Comment puis-je faire ceci ?

Avani badheka
la source

Réponses:

19

Si votre objectif est de trouver des lignes communes ou inhabituelles, ce commserait ma commande préférée ici.

Il compare deux fichiers et affiche, sur trois colonnes, des lignes qui sont uniques au fichier 1, des lignes qui sont uniques au fichier 2 et des lignes qui apparaissent dans les deux fichiers, respectivement. Vous pouvez lui passer des drapeaux pour supprimer également l'une de ces sorties. Par exemple comm -1 file1 file2, supprimera la première colonne, les choses uniques à file1. comm -12 file1 file2ne montrerait que les choses dans les deux fichiers.

Il y a une grosse mise en garde: l'entrée doit être triée. Nous pouvons contourner cela.

Cela vous montrera tout en abc qui n'est pas en mno:

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

Et vous pouvez le canaliser wc -lpour obtenir un compte.


La raison pour laquelle j'y vais commest qu'une fois les fichiers triés, la comparaison côte à côte est très simple sur le plan des calculs. Si vous en traitez des millions, cela fera une différence.

Cela peut être démontré avec quelques fichiers fictifs. J'ai un ordinateur assez rapide, donc pour montrer la différence entre les approches, j'ai besoin d'un jeu d'échantillons assez gigantesque. Je suis passé à 10 millions de chaînes de 10 caractères par fichier.

$ 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

Le tri est ce qui prend la plupart du temps dans le mien. Si nous prétendons que abc.txt est statique, nous pouvons le pré-trier et cela rend les comparaisons futures beaucoup plus rapides:

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

Vous pouvez les regarder et considérer que quelques secondes ne sont pas pertinentes, mais je dois souligner qu'elles fonctionnent sur une machine haut de gamme. Si vous souhaitez le faire sur un (par exemple) Raspberry Pi 3, vous envisagerez des délais d'exécution beaucoup plus lents et la différence augmentera au point où cela compte vraiment.

Oli
la source
7

pour obtenir une liste:

grep -Fwf abc.txt mno.txt

cela vous donne quelque chose de similaire à:

abcd
abcd
zef

si vous voulez simplement obtenir une liste unique, utilisez-la comme:

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

et pour obtenir les comptes:

grep -Fcwv -f abc.txt mno.txt

  • -F signifie: interpréter PATTERN comme une liste de chaînes fixes au lieu d'expressions régulières.
  • -fobtenir des modèles de fichier qui vont être abc.txt.
  • nous recherchons mno.txtdes modèles
  • -c Comptez le nombre de correspondances
  • -wRecherchez uniquement les «mots entiers»: la sous-chaîne correspondante doit être soit au début de la ligne, soit précédée d'un caractère non-mot. De même, il doit être soit à la fin de la ligne, soit suivi d'un caractère non verbal. Les caractères constitutifs des mots sont les lettres, les chiffres et le trait de soulignement.
  • -v Inverser la recherche
Ravexina
la source
1
Si l'OP veut un nombre de non- correspondances, cela ne devrait-il pas ressembler davantage à cela grep -cxvFf abc.txt mno.txt?
steeldriver
Je viens de le voir: D ... vous êtes toujours là pour me sauver: D
Ravexina
Pour info fgrep, les egrepsuppléants sont censés être obsolètes (en faveur de grep -F, grep -E- bien que je ne sois pas sûr que quiconque pense qu'ils s'en iront un jour
conducteur de l'acier
Est-il nécessaire d'utiliser -xlors de l'utilisation -F?
Ravexina
1
Cela dépend de ce que l'OP veut compter exactement - par exemple, si mno.txt contient, cela abcdefdevrait-il compter comme une correspondance ou une non-correspondance avec abcd?
steeldriver
3

Nous pourrions utiliser awk pour faire le travail en passant deux fichiers, d'abord le fichier de signatures, puis le fichier que nous voulons vérifier. Lorsque nous lisons le premier fichier, nous le savons NR==FNRet à ce moment-là, nous pouvons lire les lignes dans le tableau. Lorsque NR!=FNRnous vérifions si le tableau pour une telle ligne est défini.

$ 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

Inversement, nous pouvons annuler le motif pour imprimer les lignes qui ne sont pas en abc.txt

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

Et si nous voulons imprimer le nombre de ceux que nous pouvons employer sortet wc:

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt | sort -u | wc -l         
2
Sergiy Kolodyazhnyy
la source
Je pense que vous vous trompez. Pour autant que je comprends la question, OP veut calculer la (taille de) la différence définie de abc.txt- mno.txtqui est {xyz, pqrs}.
David Foerster
2

Si l'une des listes de mots n'est pas triée, il serait plus rapide d'utiliser une structure de données d'ensemble efficace pour mémoriser les mots courants.

Python

#!/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

Usage:

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

Python (plus efficace)

Si vous souhaitez économiser un peu de mémoire pour le stockage intermédiaire et le temps d'exécution, vous pouvez utiliser ce programme légèrement plus difficile à comprendre:

#!/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

Performance

Étant donné abc.txtet mno.txtavec 1 mio de lignes non triées de 10 caractères ASCII aléatoires chacun (voir la réponse d'Oli pour la configuration):

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

contre.

$ 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

total: 23 secondes

David Foerster
la source