Entfernen Sie doppelte Zeilen aus einer großen Datei in Python
Ich habe eine csv-Datei, die möchte ich entfernen Sie doppelte Zeilen aus, aber es ist zu groß, um passen in den Speicher. Ich fand einen Weg, um es getan, aber meine Vermutung ist, dass es nicht der beste Weg.
Jede Zeile enthält 15 Felder und mehrere hundert Zeichen, und alle Felder sind erforderlich, um Eindeutigkeit zu bestimmen. Anstelle des Vergleichs der gesamten Reihe zu finden, ein Duplikat, ich Vergleiche hash(row-as-a-string)
in einem Versuch, um Speicherplatz zu sparen. Ich einen filter setzen, der Partitionen die Daten in einer etwa gleichen Anzahl von Zeilen (z.B. Tage der Woche), und jede partition ist klein genug, dass eine lookup-Tabelle der hash-Werte für die partition wird in den Speicher passt. Passiere ich die Datei einmal für jede partition, die überprüfung für eindeutige Zeilen und schreiben Sie Sie heraus in eine zweite Datei (pseudo-code):
import csv
headers={'DayOfWeek':None, 'a':None, 'b':None}
outs=csv.DictWriter(open('c:\dedupedFile.csv','wb')
days=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
outs.writerows(headers)
for day in days:
htable={}
ins=csv.DictReader(open('c:\bigfile.csv','rb'),headers)
for line in ins:
hvalue=hash(reduce(lambda x,y:x+y,line.itervalues()))
if line['DayOfWeek']==day:
if hvalue in htable:
pass
else:
htable[hvalue]=None
outs.writerow(line)
Einer Weise, die ich dachte, um diese Fahrt ist von der Suche nach einem besseren filter zu verringern die Anzahl der Durchläufe notwendig. Vorausgesetzt, die Länge der Zeilen ist gleichmäßig verteilt, vielleicht anstelle von
for day in days:
und
if line['DayOfWeek']==day:
wir haben
for i in range(n):
und
if len(reduce(lambda x,y:x+y,line.itervalues())%n)==i:
wo 'n', die kleiner als Speicher zu erlauben. Das ist aber immer noch mit der gleichen Methode.
Wayne Werner eine gute praktische Lösung unten; ich war neugierig, ob es besser/schneller/einfacher Weg, dies zu tun von einem Algorithmus Perspektive.
P. S. ich bin beschränkt auf Python 2.5.
- Tun Sie Ihre Ausgabe-Zeilen müssen in der gleichen Reihenfolge, wie Sie auf die input-Datei? Erwarten Sie viele Wiederholungen, oder sollte die output-Datei-Größe halten mehr oder weniger die gleiche Größenordnung wie die der input-Datei (oder ist das nicht vorhersehbar)?
- Die Reihenfolge der Zeilen in der Ausgabe-Datei ist nicht wichtig. Für diesen speziellen Fall gibt es relativ wenige Dubletten. Denken Sie, dass die Anzahl der Duplikate hat Lager im Allgemeinen Fall?
- Es könnte, wenn, zum Beispiel, die eindeutige Zeilen passen in den Speicher (auch wenn die vollständige Datei mit der duplizierten, würde nicht). Ich muss für eine Weile verlassen, aber ich werde einen Vorschlag machen, später.
- Das edit bedeutet, dass Sie interessiert sind, Antworten, die nicht gut oder nicht praktikabel ist oder die Lösung nicht sein -- warum?
- Machin: ich meinte zu vermitteln, dass ich interessiert daran war, die Theorie, die hinter der Lösung. Ich nahm Wayne Werners Antwort sowieso, da es das problem lösen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie möchten, eine wirklich einfache Möglichkeit, dies zu tun, erstellen Sie einfach eine sqlite-Datenbank:
Dann würden Sie nicht haben, um sorgen über die Vergleich-Logik-sich - lassen sqlite übernehmen das für Sie. Es wird wahrscheinlich nicht viel schneller als die Vermischung der Saiten, aber es ist wahrscheinlich viel einfacher. Natürlich würden Sie ändern Sie den Typ in der Datenbank gespeichert, wenn Sie wollte, oder nicht, wie der Fall sein kann. Natürlich da hast du schon konvertieren der Daten in eine Zeichenfolge, die Sie könnte nur noch ein Feld statt. Viele Optionen hier.
cur.execute("insert into XXX values (?,?,?,?,?)", (1,2,3,4,5))
Sind Sie im Grunde tun ein merge-sort und das entfernen von duplizierten Einträgen.
Brechen Sie die Eingabe in den Speicher-große Stücke, die Sortierung jedes Stück, dann verschmelzen die Stücke beim entfernen der Duplikate ist ein sound-Idee im Allgemeinen.
Eigentlich, bis auf ein paar gigs ich würde das virtual memory system umgehen und nur schreiben:
Ihre aktuelle Methode ist nicht garantiert, um richtig zu arbeiten.
Erstens gibt es die geringe Wahrscheinlichkeit, dass zwei Linien, die sind tatsächlich anders produzieren können, die den gleichen hash-Wert.
hash(a) == hash(b)
bedeutet nicht immer, dassa == b
Zweitens -, Sie machen die Wahrscheinlichkeit höher mit Ihrem "reduzieren/lambda" caper:
BTW, wäre das nicht "".join(['foo', '1', '23']) etwas klarer?
BTW2, warum nicht ein
set
statt einerdict
fürhtable
?Hier eine praktische Lösung: den "core utils" - Paket von der GnuWin32 Website, und installieren Sie es. Dann:
c:\gnuwin32\bin\sort --unique -ooutfile.csv infile.csv
Für jeden der Schritte 1 & 3, können Sie ein Python-Skript, oder einige der anderen GnuWin32 utilities (head, tail, tee, Katze, ...).
Ihrem ursprünglichen Lösung etwas falsch ist: man kann verschiedene Linien-hashing auf den gleichen Wert (hash-Kollision), und der code verlassen, eine von Ihnen aus.
In Bezug auf die Algorithmische Komplexität, wenn Sie erwarten, dass relativ wenige Duplikate, ich denke die Schnellste Lösung wäre zum Scannen der Datei Zeile für Zeile, indem die hash der einzelnen Linie (wie Sie es getan haben), sondern auch die Speicherung der Position der Linie. Dann, wenn Sie auf einen doppelten hash, suchen Sie an den ursprünglichen Platz stellen Sie sicher, dass es ein Duplikat und nicht nur eine hash-Kollision, und wenn ja, versuchen Sie sich zurück und überspringen Sie die Linie.
Durch die Art und Weise, wenn die CSV-Werte sind normalisiert (d.h. die Datensätze als gleich gelten, wennn die entsprechenden CSV-Zeilen sind äquivalent byte-für-byte), müssen Sie nicht um die CSV-parsing hier überhaupt, lediglich mit plain-text-Zeilen.
Da ich angenommen, Sie haben auf einem etwas regelmäßig (oder man müsste gehackt eine einmal-over-Skript), und Sie haben erwähnt, Sie waren daran interessiert, eine theoretische Lösung, hier ist eine Möglichkeit.
Lesen Sie die input-Zeilen in B-Bäumen bestellt durch jeden Eingang hash-Wert, das schreiben auf die Festplatte, wenn der Speicher füllt. Wir kümmern uns um zu speichern, auf der B-Bäume, die ursprünglichen Linien angebracht, um die hash (als set, da wir nur über einzigartige Linien). Wenn wir Lesen, ein doppeltes element, überprüfen wir die Linien festgelegt, auf die gespeicherte element und fügen Sie es, wenn es eine neue Linie, die geschieht, um hash um den gleichen Wert.
Warum B-Bäume???? Sie erfordert weniger Festplatte liest, wenn Sie nur können (oder wollen) zu Lesen, teilen Sie Sie in den Speicher. Der Grad (Anzahl der Kinder) auf jedem Knoten hängt von den verfügbaren Speicher und die Anzahl der Linien, aber Sie wollen nicht zu viele Knoten.
Einmal haben wir diese B-Bäume auf der Festplatte, vergleichen wir die niedrigste element von jeder von Ihnen. Wir entfernen die niedrigsten von allen, von allem B-Bäume, die es haben. Wir verschmelzen Ihre Linien setzt, was bedeutet, dass wir keine Duplikate haben Links für diese Zeilen (und auch, dass wir keine Zeilen mehr, dass hash-Wert). Wir schreiben Sie dann die Zeilen aus diesem merge in der Ausgabe csv-Struktur.
Können wir die Trennung die Hälfte des Speichers für das Lesen der B-Bäume, und die Hälfte halten Sie die csv-Ausgabe im Speicher für einige Zeit. Wir Spülen das csv-Format auf die Festplatte, wenn die Hälfte voll ist, Anhängen, was bereits geschrieben wurde. Wie viel von jedem B-Baum Lesen wir auf jedem Schritt können Sie grob berechnen, indem (available_memory /2) /number_of_btrees, gerundet, so Lesen wir voller Knoten.
In pseudo-Python:
Wie über die Verwendung heapq Modul zu Lesen Stücke von Dateien, die bis zu memory limit und schreiben Sie Sie aus der sortierten Stücke (heapq hält die Dinge immer in sortierter Reihenfolge).
Oder Sie fangen konnte, das erste Wort in Zeile, und teilen Sie die Datei in Stücke von diesem. Dann Lesen Sie die Zeilen (vielleicht tun ' '.join(Zeile.split ()), um die Vereinheitlichung der Abstände/tabs in der Linie, wenn es ist OK, um zu ändern, Abstand) in Satz in alphabetischer Reihenfolge clearing-setzen zwischen die Stücke (set-entfernt Duplikate), um die Dinge in der Hälfte sortiert (set ist nicht in Ordnung, wenn Sie möchten, können Sie Lesen im heap und Schreibe Sie heraus zu bekommen, sortiert, letzten vorkommen im set ersetzen die alten Werte, wie Sie gehen.) Alternativ können Sie auch Sortieren Sie die Stück und entfernen Sie doppelte Linien, mit Joe Koberg ist groupby-Lösung. Schließlich können Sie join-Stücke wieder zusammen (können Sie natürlich tun, die schreiben, wie Sie gehen Stück für Stück der endgültigen Datei während der Sortierung der Stücke)