Encuentra ID en un archivo que no está en otro

9

Tengo dos archivos:

abc.txt

abcd
xyz
pqrs

mno.txt

zzon
mkno
abcd
  • Quiero verificar si "abcd" está allí en el archivo mno.txt .
  • No es necesario que si "abcd" esté primero en abc.txt , también estará primero en mno.txt .
  • Hay miles de identificadores en ambos archivos.
  • También quiero verificar cuántos ID no están en mno.txt que están en abc.txt .

Cómo puedo hacer esto ?

Avani badheka
fuente

Respuestas:

19

Si su objetivo es encontrar líneas comunes o poco comunes, commsería mi comando ir aquí.

Compara dos archivos y muestra, en tres columnas, líneas exclusivas del archivo 1, líneas exclusivas del archivo 2 y líneas que aparecen en ambos archivos, respectivamente. Puede pasarle banderas para suprimir cualquiera de estos resultados también. Por ejemplo comm -1 file1 file2, suprimirá la primera columna, las cosas exclusivas de file1. comm -12 file1 file2solo mostraría cosas en ambos archivos.

Hay una gran advertencia: la entrada debe ser ordenada. Podemos evitar esto.

Esto le mostrará todo en abc que no está en mno:

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

Y puedes canalizar eso wc -lpara obtener un recuento.


La razón con la que voy commes que una vez que los archivos están ordenados, la comparación lado a lado es computacionalmente muy simple. Si está lidiando con millones de estos, eso hará la diferencia.

Esto se puede demostrar con un par de archivos simulados. Tengo una computadora bastante rápida, así que para mostrar la diferencia entre los enfoques, necesito un conjunto de muestras bastante gigantesco. He ido a 10 millones de cadenas de 10 caracteres por archivo.

$ 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

La clasificación es lo que lleva la mayor parte del tiempo en el mío. Si pretendemos que abc.txt es estático, podemos ordenarlo previamente y eso hace que las futuras comparaciones sean mucho más rápidas:

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

Puede mirar esto y considerar unos segundos irrelevantes, pero debo resaltar que se están ejecutando en una máquina de gama alta. Si quisieras hacer esto en un (por ejemplo) Raspberry Pi 3, verás cambios mucho más lentos y la diferencia aumentará a un punto que realmente importa.

Oli
fuente
7

para obtener una lista:

grep -Fwf abc.txt mno.txt

te da algo similar a:

abcd
abcd
zef

si solo quieres obtener una lista única, úsala como:

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

y para obtener los recuentos:

grep -Fcwv -f abc.txt mno.txt

  • -F significa: interpretar PATTERN como una lista de cadenas fijas en lugar de expresiones regulares.
  • -fobtener patrones de ARCHIVO que va a ser abc.txt.
  • buscamos mno.txtpatrones
  • -c Cuenta el número de partidos
  • -wSolo busque "palabras completas": la subcadena correspondiente debe estar al principio de la línea o precedida por un carácter constituyente que no sea una palabra. Del mismo modo, debe estar al final de la línea o seguido de un carácter constituyente que no sea una palabra. Los caracteres que constituyen las palabras son letras, dígitos y guiones bajos.
  • -v Invierte la búsqueda
Ravexina
fuente
1
Si el OP quiere un recuento de no coincidencias, ¿no debería ser así grep -cxvFf abc.txt mno.txt?
steeldriver
Lo
acabo de
Para su información fgrep, las egrepalternativas supuestamente están en desuso (a favor de grep -F, grep -Eaunque no estoy seguro de que alguien crea que alguna vez se irán)
Steeldriver
¿Es necesario usar -xcuando se usa -F?
Ravexina
1
Depende de lo que el OP quiera contar exactamente, por ejemplo, si mno.txt contiene, ¿ abcdefdebería contar eso como una coincidencia o no abcd?
steeldriver
3

Podríamos usar awk para hacer el trabajo pasando dos archivos, primero el archivo de patrones y luego el archivo que queremos verificar. Cuando estamos leyendo el primer archivo, sabemos eso NR==FNRy en ese momento podemos leer líneas en una matriz. Cuando NR!=FNRverificamos si se establece la matriz para dicha línea.

$ 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

Por el contrario, podemos negar el patrón para imprimir aquellas líneas que no están en abc.txt

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

Y si queremos imprimir el recuento de aquellos que podemos emplear sorty wc:

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt | sort -u | wc -l         
2
Sergiy Kolodyazhnyy
fuente
Creo que lo tienes al revés. Hasta donde entiendo la pregunta, OP quiere calcular el (tamaño de) la diferencia establecida de abc.txt, mno.txtque es {xyz, pqrs}.
David Foerster
2

Si alguna de las listas de palabras no está ordenada, sería más rápido usar una estructura de datos de conjunto eficiente para recordar las palabras comunes.

Pitón

#!/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 (más eficiente)

Si desea guardar un poco de memoria para el almacenamiento intermedio y el tiempo de ejecución, puede usar este programa un poco más difícil de entender:

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

Actuación

Dado abc.txty mno.txtcon 1 millón de líneas sin clasificar de 10 caracteres de dígitos ASCII aleatorios cada una (consulte la respuesta de Oli para la configuración):

$ 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

total: 23 segundos

David Foerster
fuente