Potrzebuję uzyskać liczbę wierszy dużego pliku (setki tysięcy linii) w pythonie. Jaki jest najbardziej efektywny sposób pod względem pamięci i czasu?
W tej chwili robię:
def file_len(fname):
with open(fname) as f:
for i, l in enumerate(f):
pass
return i + 1
czy można zrobić coś lepszego?
python
text-files
line-count
SilentGhost
źródło
źródło
enumerate(f, 1)
i porzucići + 1
?Odpowiedzi:
Nie ma nic lepszego niż to.
W końcu każde rozwiązanie będzie musiało przeczytać cały plik, dowiedzieć się, ile
\n
masz i zwrócić ten wynik.Czy masz lepszy sposób, aby to zrobić bez czytania całego pliku? Nie jestem pewien ... Najlepszym rozwiązaniem zawsze będzie I / O, najlepiej, abyś nie używał niepotrzebnej pamięci, ale wygląda na to, że ją masz.
źródło
Jedna linia, prawdopodobnie dość szybka:
źródło
Wierzę, że plik odwzorowany w pamięci będzie najszybszym rozwiązaniem. Próbowałem czterech funkcji: funkcja wysłana przez OP (
opcount
); prosta iteracja po liniach w pliku (simplecount
); readline z plikiem zmapowanym w pamięci (mmap) (mapcount
); oraz rozwiązanie odczytu bufora oferowane przez Mykola Kharechko (bufcount
).Uruchomiłem każdą funkcję pięć razy i obliczyłem średni czas działania dla pliku tekstowego o wielkości 1,2 miliona wierszy.
Windows XP, Python 2.5, 2 GB pamięci RAM, procesor AMD 2 GHz
Oto moje wyniki:
Edycja : liczby dla Python 2.6:
Tak więc strategia odczytu buforów wydaje się być najszybsza dla Windows / Python 2.6
Oto kod:
źródło
wccount()
jest to najszybszy gist.github.com/0ac760859e614cd03652Musiałem napisać to na podobne pytanie, dopóki moja ocena reputacji nieco nie podskoczyła (dzięki temu, kto mnie uderzył!).
Wszystkie te rozwiązania ignorują jeden ze sposobów na przyspieszenie tego działania, a mianowicie użycie niebuforowanego (surowego) interfejsu, używanie bajtów i tworzenie własnego bufora. (Dotyczy to tylko Pythona 3. W Pythonie 2 domyślny interfejs może, ale nie musi być używany, ale w Pythonie 3 domyślnie zostanie ustawiony Unicode.)
Korzystając ze zmodyfikowanej wersji narzędzia do pomiaru czasu, uważam, że następujący kod jest szybszy (i nieznacznie bardziej pythoniczny) niż którekolwiek z oferowanych rozwiązań:
Korzystanie z osobnej funkcji generatora przyspiesza smidge:
Można to zrobić całkowicie za pomocą wyrażeń generatorów wbudowanych za pomocą itertools, ale robi się dość dziwnie:
Oto moje czasy:
źródło
wccount
w tej tabeli dlawc
narzędzia powłoki podprocesowej ?rawincount
rozwiązanie będzie mniej dziwne, używającbufgen = iter(partial(f.raw.read, 1024*1024), b'')
zamiast łączeniatakewhile
irepeat
.Możesz wykonać podproces i uruchomić
wc -l filename
źródło
Oto program w języku Python, który wykorzystuje bibliotekę wieloprocesową do dystrybucji liczenia linii między maszynami / rdzeniami. Mój test poprawia liczenie 20-milionowego pliku linii od 26 sekund do 7 sekund przy użyciu 8-rdzeniowego serwera Windows 64. Uwaga: nieużywanie mapowania pamięci znacznie spowalnia działanie.
źródło
Jednowierszowe rozwiązanie bashowe podobne do tej odpowiedzi , wykorzystujące nowoczesną
subprocess.check_output
funkcję:źródło
wc -l
ponad 100 sekund, podczas gdy wywołanie podprocesu zajmuje ~ 5 sekund.shell=True
jest szkodliwy dla bezpieczeństwa, lepiej go unikać.Chciałbym użyć metody obiektu pliku Pythona w
readlines
następujący sposób:To otwiera plik, tworzy listę linii w pliku, liczy długość listy, zapisuje ją w zmiennej i ponownie zamyka plik.
źródło
xreadlines
są przestarzałe od wersji 2.3, ponieważ zwraca tylko iterator.for line in file
jest podanym zamiennikiem. Zobacz: docs.python.org/2/library/stdtypes.html#file.xreadlinesźródło
Oto, czego używam, wydaje się całkiem czysty:
AKTUALIZACJA: Jest to nieznacznie szybsze niż używanie czystego Pythona, ale kosztem użycia pamięci. Podproces rozwidli nowy proces z takim samym obszarem pamięci, jak proces nadrzędny podczas wykonywania polecenia.
źródło
:-)
Jest to najszybsza rzecz, jaką znalazłem, używając czystego pytona. Możesz użyć dowolnej ilości pamięci, ustawiając bufor, chociaż 2 ** 16 wydaje się być dobrym miejscem na moim komputerze.
Znalazłem odpowiedź tutaj Dlaczego czytanie linii ze standardowego wejścia w C ++ jest znacznie wolniejsze niż w Pythonie? i trochę poprawiłem. To bardzo dobra lektura, aby zrozumieć, jak szybko liczyć wiersze, choć
wc -l
nadal jest o 75% szybsza niż cokolwiek innego.źródło
Uzyskałem niewielką (4-8%) poprawę w tej wersji, która ponownie wykorzystuje stały bufor, więc powinna unikać jakiegokolwiek narzutu pamięci lub GC:
Możesz bawić się z rozmiarem bufora i być może zauważyć niewielką poprawę.
źródło
Odpowiedź Kyle'a
jest prawdopodobnie najlepsza, alternatywą jest
Oto porównanie wydajności obu
źródło
Rozwiązanie jednoliniowe:
Mój fragment:
źródło
os.system()
zmiennej i przetworzyć go w żaden sposób.Aby wykonać powyższe metody, wypróbowałem wariant z modułem fileinput:
I przekazałem plik linii 60 mil do wszystkich wyżej wymienionych metod:
To dla mnie małe zaskoczenie, że dane wejściowe są tak złe i skalują się znacznie gorzej niż wszystkie inne metody ...
źródło
Jak dla mnie ten wariant będzie najszybszy:
Powody: buforowanie jest szybsze niż czytanie linia po linii, a
string.count
także bardzo szybkieźródło
Ten kod jest krótszy i bardziej przejrzysty. To chyba najlepszy sposób:
źródło
Zmodyfikowałem przypadek bufora w następujący sposób:
Teraz liczone są także puste pliki i ostatni wiersz (bez \ n).
źródło
A co z tym
źródło
count = max(enumerate(open(filename)))[0]
źródło
enumerate()
jest liczenie początkowe zgodnie z docs.python.org/2/library/functions.html#enumerateźródło
źródło
Jeśli chcesz taniej uzyskać liczbę wierszy w Pythonie w Linuksie, polecam tę metodę:
ścieżka_pliku może być zarówno ścieżką abstrakcyjną, jak i ścieżką względną. Mam nadzieję, że to może pomóc.
źródło
Co powiesz na to?
źródło
Co powiesz na ten liniowiec:
Zastosowanie tej metody zajmuje 0,003 s, aby zmierzyć czas w pliku linii 3900
źródło
źródło
Prosta metoda:
1)
2)
3)
źródło
wynikiem otwarcia pliku jest iterator, który można przekonwertować na sekwencję o długości:
jest to bardziej zwięzłe niż twoja wyraźna pętla i pozwala uniknąć
enumerate
.źródło
Możesz użyć
os.path
modułu w następujący sposób:, gdzie
Filename
jest bezwzględna ścieżka do pliku.źródło
os.path
?Jeśli plik zmieści się w pamięci, to
źródło