Wie kann ich die in der ersten Datei enthaltenen Namen abrufen, die nicht i sind?

Wie kann ich die in der ersten Datei enthaltenen Namen abrufen, die nicht i sind?

Ich habe zwei Dateien mit Milliarden von DNA-Sequenznamen, die zweite ist eine echte Teilmenge der ersten:

Zum Beispiel,

1°:john mike anna paul laura .... 

2°:john mike paul ...

Alle Namen belegen eine einzelne Zeile.

Meine Frage ist nun: Wie kann ich die Namen abrufen, die in der ersten Datei enthalten sind und in der zweiten nicht?

danke euch allen!

Antwort1

Das ist ganz einfach, aber Ihr Leben wird viel einfacher, wenn Sie einen Namen pro Zeile haben, statt einer durch Leerzeichen getrennten Liste. Es gibt viele hervorragende Dienstprogramme zum Bearbeiten von Textdateien in Linux. Dies ist eine der Sachen, die alle *nixes hervorragend können, aber die meisten erwarten ein Element pro Zeile. Daher beginnen die meisten meiner Lösungen damit, die Dateien entsprechend zu ändern.

Ändern Sie Ihre Dateien so, dass sie pro Zeile einen Namen haben:

sed 's/ /\n/g' file > newfile

oder, um die Originaldatei zu ändern

sed -i 's/ /\n/g' file

Sobald Sie das getan haben, erhalten Sie mit jeder dieser Optionen das gewünschte Ergebnis:

  1. grep

    $ grep -vFwf file2 file1
    anna
    laura
    
  2. commoderdiff

    $ comm -23 <(sort file1) <(sort file2)
    anna
    laura
    
    
    $ diff file1 file2 | grep -Po '<\s+\K.*'
    anna
    laura
    
  3. awk

    $ awk '(NR==FNR){a[$1]++; next}!($1 in a){print}' file2 file1 
    laura
    anna
    
  4. Perl

    $ perl -lne 'BEGIN{open(A,"file2"); while(<A>){chomp; $k{$_}++}} print unless $k{$_}' file2 file1
    laura
    anna
    

    oder

    $ perl -lne '$k{$_}++; END{map{print unless $k{$_}>1}keys(%k)}' file2 file1
    laura
    anna
    

Wenn Sie das Format Ihrer Datei wirklich nicht ändern möchten (aber das sollten Sie wirklich), können Sie Folgendes tun:

awk '{for (i=1;i<=NF;i++){a[$i]++}}END{for(n in a){if(a[n]<2){print n}}}' file2 file1

oder

perl -lane '$k{$_}++ for @F; END{map{print if $k{$_}<2} keys(%k)}' file1 file2

Antwort2

Wenn sie sortiert und durch Zeilenumbrüche getrennt sind, können Sie commdie Zeilen anzeigen, die für Datei1 eindeutig sind:

comm -23 file1 file2

Eine Demonstration:

$ comm -23 <(echo -e 'john\nmike\nanna\npaul\nlaura'|sort) <(echo -e 'john\nmike\npaul'|sort)
anna
laura

Oder Sie könnten diffso ziemlich dasselbe tun (es grepwird nach Zeilenlöschungen gesucht):

diff sorted-file-1 sorted-file-2 | grep -oP '(?<=< ).+'

Wenn Sie das Sortieren vermeiden müssen oder mit großen Zahlen arbeiten, würde ich für wörterbuchbasierte Nachschlagevorgänge auf eine geeignete Sprache zurückgreifen. Einfaches Python-Beispiel:

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

Bei allem, was größer ist, sollten Sie sich eine echte Datenbank und einfaches SQL ansehen. Diese sind auf große Datenmengen ausgerichtet.

Antwort3

Und die Python-Option: unabhängig davon, ob alle Wörter in einer Zeile oder auf separaten Zeilen stehen:

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

Kopieren Sie das Skript in eine leere Datei, speichern Sie es, showdiff.pymachen Sie es ausführbar und führen Sie es mit dem folgenden Befehl aus:

/path/to/showdiff.py file1 file2

anna
laura

Notiz

Das ist zwar nicht die Frage, aber es hängt zu viel damit zusammen, um es wegzulassen:

Wenn Sie die Unterschiede auflisten möchtengegenseitig, (nicht nur Wörter in , file1die nicht in vorkommen file2, sondern auch Wörter in file2, die nicht in vorkommen file1), Das folgende Skript sollte verwendet werden:

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

Antwort4

Wenn Sie die Python-Option wählen, wie von Jacob Vlijm vorgeschlagen, lohnt es sich, das 'set' zu verwenden (weitere Informationen finden Sie unterhttps://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset). Sobald Sie die beiden Mengen erstellt haben, können Sie grundsätzlich Mengenmathematik (Vereinigung, Schnittmenge, Differenz usw.) durchführen.
In diesem Fall ist die Differenzmenge genau das, was Sie benötigen: eine neue Menge mit allen Elementen, die in einer Menge enthalten sind und nicht in der anderen.
Der Code von Jacob wäre dann:

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

Natürlich wird es bei Milliarden von Datensätzen eine Weile dauern …“

verwandte Informationen