Lies eine große Datei ein und erstelle ein Wörterbuch

Ich habe eine große Datei, die ich einlesen und aus der ich ein Wörterbuch machen muss. Ich möchte, dass das so schnell wie möglich geht. Mein Code in Python ist jedoch zu langsam. Hier ist ein minimales Beispiel, das das Problem zeigt.

Machen Sie zuerst einige gefälschte Daten

paste <(seq 20000000) <(seq 2 20000001)  > largefile.txt

Hier ist ein minimaler Python-Code zum Einlesen und Erstellen eines Wörterbuchs.

import sys
from collections import defaultdict
fin = open(sys.argv[1])

dict = defaultdict(list)

for line in fin:
    parts = line.split()
    dict[parts[0]].append(parts[1])

Timings:

time ./read.py largefile.txt
real    0m55.746s

Es ist jedoch nicht I / O-gebunden als:

time cut -f1 largefile.txt > /dev/null    
real    0m1.702s

Wenn ich das auskommentieredict Linie dauert es9 Sekunden. Es scheint, dass fast die ganze Zeit damit verbracht wirddict[parts[0]].append(parts[1]).

Gibt es eine Möglichkeit, dies zu beschleunigen? Es macht mir nichts aus, Cython oder sogar C zu verwenden, wenn das einen großen Unterschied machen wird. Oder können Pandas hier helfen?

Hier ist die Profilausgabe in einer Datei mit einer Größe von 10000000 Zeilen.

python -m cProfile read.py test.data         20000009 function calls in 42.494 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 bisect.py:1(<module>)
        1    0.000    0.000    0.001    0.001 collections.py:1(<module>)
        1    0.000    0.000    0.000    0.000 collections.py:25(OrderedDict)
        1    0.000    0.000    0.000    0.000 collections.py:386(Counter)
        1    0.000    0.000    0.000    0.000 heapq.py:31(<module>)
        1    0.000    0.000    0.000    0.000 keyword.py:11(<module>)
        1   30.727   30.727   42.494   42.494 read.py:2(<module>)
 10000000    4.855    0.000    4.855    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
 10000000    6.912    0.000    6.912    0.000 {method 'split of 'str' objects}
        1    0.000    0.000    0.000    0.000 {open}

Aktualisieren. Wir können annehmen, dass parts [1] eine ganze Zahl und parts [0] eine kurze Zeichenfolge mit fester Länge ist.

Meine gefälschten Daten sind nicht sehr gut, da Sie nur einen Wert pro Schlüssel erhalten. Hier ist eine bessere Version.

perl -E 'say int rand 1e7, $", int rand 1e4 for 1 .. 1e7' > largefile.txt

Die einzige Operation, die ich ausführen werde, ist das Abfragen eines Schlüssels, um die Liste der damit verbundenen Werte zurückzugeben.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage