Encontre IDs em um arquivo que não está em outro


9

Eu tenho dois arquivos:

abc.txt

abcd
xyz
pqrs

mno.txt

zzon
mkno
abcd
  • Quero verificar se "abcd" existe no arquivo mno.txt .
  • Não é necessário que, se "abcd" for o primeiro no abc.txt , ele também seja o primeiro no mno.txt .
  • Existem milhares desses IDs nos dois arquivos.
  • Também quero verificar quantos IDs não estão no mno.txt que estão no abc.txt .

Como posso fazer isso ?

Respostas:


19

Se seu objetivo é encontrar linhas comuns ou incomuns, commseria meu comando aqui.

Ele compara dois arquivos e mostra - em três colunas - linhas exclusivas para o arquivo 1, linhas exclusivas para o arquivo 2 e linhas que aparecem nos dois arquivos, respectivamente. Você pode passar sinalizadores para suprimir qualquer uma dessas saídas também. Por exemplo comm -1 file1 file2, suprimirá a primeira coluna, as coisas exclusivas do arquivo1. comm -12 file1 file2mostraria apenas coisas nos dois arquivos.

Há uma grande ressalva: a entrada deve ser classificada. Nós podemos contornar isso.

Isso mostra tudo no abc que não está no mno:

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

E você pode canalizar isso wc -lpara obter uma contagem.


O motivo commé que, uma vez que os arquivos são classificados, a comparação lado a lado é computacionalmente muito simples. Se você está lidando com milhões deles, isso fará a diferença.

Isso pode ser demonstrado com alguns arquivos simulados. Eu tenho um computador bastante rápido, para mostrar a diferença entre abordagens, preciso de um conjunto de amostras gigantesco. Eu fui para 10 milhões de strings de 10 caracteres por arquivo.

$ 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

A classificação é o que leva a maior parte do tempo na minha. Se fingirmos que o abc.txt é estático, podemos pré-classificá-lo e isso torna as comparações futuras muito mais rápidas:

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

Você pode olhar para eles e considerar alguns segundos irrelevantes, mas devo destacar que eles estão sendo executados em uma máquina de ponta. Se você quiser fazer isso em um (por exemplo) Raspberry Pi 3, verá mudanças muito mais lentas e a diferença aumentará a um ponto que realmente importa.


7

para obter uma lista:

grep -Fwf abc.txt mno.txt

fornece algo semelhante a:

abcd
abcd
zef

se você quiser apenas obter uma lista exclusiva, use-a como:

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

e para obter as contagens:

grep -Fcwv -f abc.txt mno.txt

  • -F significa: interpretar PATTERN como uma lista de cadeias de caracteres fixas em vez de expressões regulares.
  • -fobter padrões de FILE que serão abc.txt.
  • procuramos mno.txtpadrões
  • -c Contar o número de correspondências
  • -wProcure apenas "palavras inteiras": a substring correspondente deve estar no início da linha ou precedida por um caractere que não seja de palavra. Da mesma forma, ele deve estar no final da linha ou ser seguido por um caractere que não seja de palavra. Os caracteres constituintes da palavra são letras, dígitos e o sublinhado.
  • -v Inverter a pesquisa

11
Se o OP deseja uma contagem de não correspondências, não deveria ser mais assim grep -cxvFf abc.txt mno.txt?
steeldriver

Só vi: D ... você está sempre aqui para me salvar: D
Ravexina 26/06

FYI o fgrep, egrepsuplentes são supostamente obsoleta (em favor de grep -F, grep -E- embora eu não tenho certeza se alguém acredita que eles nunca vão embora
steeldriver

É necessário usar -xao usar -F?
Ravexina

11
Depende do que o OP deseja contar exatamente - por exemplo, se mno.txt contiver abcdef, isso deve ser considerado como uma correspondência ou não abcd?
Steeldiver

3

Poderíamos usar o awk para fazer o trabalho passando dois arquivos, primeiro o arquivo padrão, depois o arquivo que queremos verificar. Quando estamos lendo o primeiro arquivo, sabemos que NR==FNRe nesse momento podemos ler linhas na matriz. Quando NR!=FNRverificamos se o array para essa linha está definido.

$ 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 outro lado, podemos negar o padrão para imprimir as linhas que não estão em abc.txt

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

E se quisermos imprimir a contagem daqueles que podemos empregar sorte wc:

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

Eu acho que você entendeu errado. Tanto quanto eu entendo a pergunta, o OP quer calcular o (tamanho de) a diferença definida de abc.txt- mno.txtqual é {xyz, pqrs}.
David Foerster

2

Se uma das listas de palavras não estiver classificada, seria mais rápido usar uma estrutura de dados de conjunto eficiente para lembrar as palavras comuns.

Pitão

#!/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 (mais eficiente)

Se você deseja economizar um pouco de memória para armazenamento intermediário e tempo de execução, pode usar este programa um pouco mais 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

atuação

Dado abc.txte mno.txtcom 1 milhão de linhas não ordenadas de 10 caracteres aleatórios de dígitos ASCII cada (consulte a resposta de Oli para a configuração):

$ 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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.