Czytaj w dużym pliku i stwórz słownik

Mam duży plik, z którego muszę wczytać i utworzyć słownik. Chciałbym, żeby to było jak najszybciej. Jednak mój kod w Pythonie jest zbyt wolny. Oto minimalny przykład pokazujący problem.

Najpierw wymyśl fałszywe dane

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

Teraz jest to minimalny fragment kodu Pythona do odczytu i tworzenia słownika.

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])

Czas:

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

Jednak nie jest on związany z I / O jako:

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

Jeśli komentujędict linia bierze9 sekundy. Wydaje się, że prawie cały czas spędzadict[parts[0]].append(parts[1]).

Czy jest jakiś sposób, żeby to przyspieszyć? Nie mam nic przeciwko używaniu cythona, a nawet C, jeśli to ma duże znaczenie. A może pandy mogą tu pomóc?

Oto dane wyjściowe profilu w pliku o rozmiarze 10000000 linii.

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}

Aktualizacja. Możemy założyć, że części [1] to liczba całkowita, a części [0] to krótki łańcuch o stałej długości.

Moje fałszywe dane nie są zbyt dobre, ponieważ otrzymujesz tylko jedną wartość za klucz. Oto lepsza wersja.

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

Jedyną operacją, którą zrobię, jest zapytanie klucza, aby zwrócić listę powiązanych z nim wartości.

questionAnswers(4)

yourAnswerToTheQuestion