Tenho dois arquivos contendo bilhões de nomes de sequências de DNA, o segundo é um subconjunto adequado do primeiro:
por exemplo,
1°:john mike anna paul laura ....
2°:john mike paul ...
todos os nomes ocupam uma única linha.
Minha dúvida é: como posso obter os nomes contidos no primeiro arquivo que não estão no segundo?
obrigado a todos!
Responder1
Isso é muito simples de fazer, mas sua vida será muito mais fácil se você tiver um nome por linha em vez de uma lista separada por espaços. Existem muitos utilitários excelentes para manipular arquivos de texto no Linux, é uma das coisas em que todos os *nixes se destacam, mas a maioria espera um item por linha. Portanto, a maioria das minhas soluções começará com a modificação dos arquivos de acordo.
Mude seus arquivos para terem um nome por linha:
sed 's/ /\n/g' file > newfile
ou, para modificar o arquivo original
sed -i 's/ /\n/g' file
Depois de fazer isso, qualquer um destes lhe dará o que você deseja:
grep
$ grep -vFwf file2 file1 anna laura
comm
oudiff
$ comm -23 <(sort file1) <(sort file2) anna laura $ diff file1 file2 | grep -Po '<\s+\K.*' anna laura
estranho
$ awk '(NR==FNR){a[$1]++; next}!($1 in a){print}' file2 file1 laura anna
Perl
$ perl -lne 'BEGIN{open(A,"file2"); while(<A>){chomp; $k{$_}++}} print unless $k{$_}' file2 file1 laura anna
ou
$ perl -lne '$k{$_}++; END{map{print unless $k{$_}>1}keys(%k)}' file2 file1 laura anna
Se você realmente não deseja alterar o formato do seu arquivo (mas realmente deveria), você pode fazer algo como
awk '{for (i=1;i<=NF;i++){a[$i]++}}END{for(n in a){if(a[n]<2){print n}}}' file2 file1
ou
perl -lane '$k{$_}++ for @F; END{map{print if $k{$_}<2} keys(%k)}' file1 file2
Responder2
Se eles estiverem classificados e separados por novas linhas, você poderá comm
mostrar as linhas exclusivas do arquivo1:
comm -23 file1 file2
Uma demonstração:
$ comm -23 <(echo -e 'john\nmike\nanna\npaul\nlaura'|sort) <(echo -e 'john\nmike\npaul'|sort)
anna
laura
Ou você poderia diff
fazer praticamente a mesma coisa (está grep
procurando exclusões de linha):
diff sorted-file-1 sorted-file-2 | grep -oP '(?<=< ).+'
Se você precisar evitar a classificação ou estiver lidando com números sérios, eu recorreria a uma linguagem adequada para fazer pesquisas baseadas em dicionário. Exemplo simples de python:
file2 = {}
with open("file2") as f:
for line in f:
file2[line] = 0
with open("file1") as f:
for line in f:
if not line in file2:
print line
Qualquer coisa maior que isso e você pode querer olhar um banco de dados real e um SQL simples. Eles são voltados para big data.
Responder3
E a opção python: independentemente de todas as palavras estarem em uma linha ou em linhas separadas:
#!/usr/bin/env python3
import sys
f1 = sys.argv[1]; f2 = sys.argv[2]
def read(f):
with open(f) as content:
return content.read().split()
for item in [w for w in read(f1) if not w in read(f2)]:
print(item)
Copie o script em um arquivo vazio, salve-o como showdiff.py
executável e execute-o com o comando:
/path/to/showdiff.py file1 file2
anna
laura
Observação
Não é a questão, mas há muita coisa relacionada para deixar de fora:
Se você precisar listar as diferençasmutuamente, (não apenas palavras file1
que não aparecem em file2
, mas também palavras file2
que não aparecem em file1
), O script abaixo deve ser usado:
#!/usr/bin/env python3
import sys
f1 = sys.argv[1]; f2 = sys.argv[2]
def read(f):
with open(f) as content:
return content.read().split()
wds1 = read(f1); wds2 = read(f2); allwords = wds1+wds2
for item in [w for w in allwords if (w in wds1, w in wds2).count(False) == 1]:
print(item)
Responder4
Se você optar pela opção python sugerida por Jacob Vlijm, vale a pena usar o 'set' (para mais informações, consultehttps://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset). Basicamente, depois de criar os dois conjuntos, você pode obter a matemática do conjunto (união, interseção, diferença,...).
Nesse caso, a diferença do conjunto é exatamente o que você precisa: um novo conjunto com todos os elementos que estão em um conjunto e não no outro.
O código de Jacob seria então:
#!/usr/bin/env python3
import sys
f1 = sys.argv[1]; f2 = sys.argv[2]
def read_set(f):
with open(f) as content:
return set(content.read().split())
for item in read_set(f1) - read_set(f2)]:
print(item)
É claro que, para bilhões de registros, vai demorar um pouco... `