Chcę iterować po każdej linii całego pliku. Jednym ze sposobów na to jest czytanie całego pliku, zapisywanie go na liście, a następnie przekraczanie linii zainteresowania. Ta metoda zużywa dużo pamięci, więc szukam alternatywy.
Mój kod do tej pory:
for each_line in fileinput.input(input_file):
do_something(each_line)
for each_line_again in fileinput.input(input_file):
do_something(each_line_again)
Wykonanie tego kodu daje komunikat o błędzie: device active
.
Jakieś sugestie?
Celem jest obliczenie podobieństwa ciągów parami, co oznacza, że dla każdej linii w pliku chcę obliczyć odległość Levenshteina z każdą inną linią.
Odpowiedzi:
Prawidłowy, w pełni Pythoński sposób odczytu pliku jest następujący:
Te
with
uchwyty komunikat otwierania i zamykania pliku, w tym, jeśli jest wyjątek w wewnętrznym bloku. Dofor line in f
traktuje obiekt Filef
jako iterable, który wykorzystuje automatycznie buforowane I / O i zarządzanie pamięcią, dzięki czemu nie trzeba się martwić o dużych plików.źródło
for line in f:
działa? Mam na myśli, w jaki sposób możliwe jest iterowanie obiektu pliku?__iter__
, która mówi mu, co robić. Obiekty plików definiują tę specjalną metodę zwracania iteratora po wierszach. (Z grubsza.)Dwa sposoby oszczędzania pamięci w kolejności uszeregowanej (pierwszy jest najlepszy) -
with
- obsługiwane z Python 2.5 i nowszychyield
jeśli naprawdę chcesz mieć kontrolę nad tym, ile czytać1. użycie
with
with
jest dobrym i wydajnym pythonowym sposobem czytania dużych plików. zalety - 1) obiekt pliku jest automatycznie zamykany po wyjściu zwith
bloku wykonawczego. 2) obsługa wyjątków wewnątrzwith
bloku. 3)for
Pętla pamięci przechodzi przezf
obiekt obiektu linia po linii. wewnętrznie buforuje operacje we / wy (zoptymalizowane pod kątem kosztownych operacji we / wy) i zarządzanie pamięcią.2. użycie
yield
Czasami ktoś może chcieć dokładniejszej kontroli nad tym, ile czytać w każdej iteracji. W takim wypadku należy iter i wydajnością . Uwaga: w przypadku tej metody należy wyraźnie zamknąć plik na końcu.
Pułapki i ze względu na kompletność - poniższe metody nie są tak dobre lub nie tak eleganckie do czytania dużych plików, ale proszę przeczytać, aby uzyskać ogólne zrozumienie.
W Pythonie najczęstszym sposobem odczytywania wierszy z pliku jest wykonanie następujących czynności:
Kiedy to się jednak stanie,
readlines()
funkcja (to samo dotyczyread()
funkcji) ładuje cały plik do pamięci, a następnie wykonuje iterację. Nieco lepsze podejście (pierwsze dwie wspomniane metody są najlepsze) dla dużych plików to użyciefileinput
modułu w następujący sposób:fileinput.input()
wezwanie czyta wiersze kolejno, ale nie przechowuje je w pamięci po ich czytać, a nawet po prostu tak to, ponieważfile
w Pythonie jest iterable.Bibliografia
źródło
for line in open(...).readlines(): <do stuff>
. Dlaczego miałbyś?! Właśnie straciłeś wszystkie zalety sprytnego IO buforowanego iteratora Pythona bez żadnej korzyści.readlines
i wyjaśniając, dlaczego nie jest to dobra rzecz (ponieważ odczytuje plik do pamięci), a następnie wyjaśniając, cofileinput
robi moduł i dlaczego może chcieć użyć go w porównaniu z innymi metodami, a następnie wyjaśnić, w jaki sposób dzielenie pliku poprawia IO i podać przykład funkcji dzielenia (ale wspominając, że Python robi to już za ciebie, więc nie musisz). Ale podanie pięciu sposobów rozwiązania prostego problemu, z których cztery są w tym przypadku błędne, nie jest dobre.Aby usunąć nowe linie:
Z powszechnego poparcia nowej linii wszystkie linie pliku tekstowego będzie wydają się być zakończona
'\n'
, co terminatory w pliku'\r'
,'\n'
lub'\r\n'
.EDYCJA - Aby określić uniwersalną obsługę nowej linii:
open(file_path, mode='rU')
- wymagany [dzięki @Dave ]open(file_path, mode='rU')
- opcjonalnyopen(file_path, newline=None)
- opcjonalnyTen
newline
parametr jest obsługiwany tylko w Pythonie 3 i domyślnie jest ustawiony naNone
. Wmode
domyślnie parametr, aby'r'
we wszystkich przypadkach.U
Jest przestarzałe w Pythonie 3. W Pythonie 2 na Windows jakiś inny mechanizm wydaje się przekładać\r\n
na\n
.Dokumenty:
Aby zachować terminatory linii rodzimej:
Tryb binarny może nadal przetwarzać plik na linie z
in
. Każda linia będzie zawierała dowolne terminatory w pliku.Dzięki @katrielalex „s odpowiedź , Pythona open () doc i ipython eksperymentów.
źródło
open(file_path, 'rU')
włączyć uniwersalne znaki nowej linii.jest to możliwy sposób odczytu pliku w pythonie:
nie przydziela pełnej listy. Iteruje po liniach.
źródło
with open(input_file) as f:
. To oszczędzaf.close()
i zapewnia, że nie zapomnisz przypadkowo go zamknąć. Zapobiega wyciekom pamięci i wszystkim, co jest bardzo ważne podczas odczytu plików.Pewien kontekst z góry, skąd pochodzę. Fragmenty kodu znajdują się na końcu.
Kiedy mogę, wolę używać narzędzia typu open source, takiego jak H2O, do robienia bardzo wysokiej wydajności równoległych odczytów plików CSV, ale to narzędzie ma ograniczony zestaw funkcji. W końcu piszę dużo kodu do tworzenia potoków nauki danych przed przekazaniem do klastra H2O w celu właściwego nadzorowanego uczenia się.
Czytam pliki takie jak 8 GB zestawu danych HIGGS z repozytorium UCI, a nawet 40 GB plików CSV do celów analizy danych znacznie szybciej, dodając dużo równoległości z obiektem puli i funkcją mapy biblioteki wieloprocesowej. Na przykład grupowanie z wyszukiwaniem najbliższych sąsiadów, a także algorytmy klastrowania DBSCAN i Markowa wymagają pewnej finezji programowania równoległego, aby ominąć niektóre poważnie trudne problemy z pamięcią i czasem zegara ściennego.
Zwykle lubię łamać plik wierszowo na części za pomocą narzędzi GNU, a następnie glob-filemaskować je wszystkie, aby znaleźć i odczytać je równolegle w programie python. Często używam ponad 1000 plików częściowych. Wykonanie tych sztuczek niezwykle pomaga w ograniczeniu prędkości przetwarzania i pamięci.
Panda dataframe.read_csv jest jednowątkowa, więc możesz wykonywać te sztuczki, aby pandy były znacznie szybsze, uruchamiając map () do równoległego wykonywania. Możesz użyć htop, aby zobaczyć, że przy zwykłych starych sekwencyjnych pandach dataframe.read_csv, 100% procesora na tylko jednym rdzeniu stanowi rzeczywiste wąskie gardło w pd.read_csv, a nie dysk.
Powinienem dodać, że używam dysku SSD na szybkiej magistrali karty graficznej, a nie obracającego się HD na szynie SATA6 oraz 16 rdzeni procesora.
Ponadto inną techniką, którą odkryłem, że działa świetnie w niektórych aplikacjach, jest równoległe odczytywanie pliku CSV w jednym gigantycznym pliku, rozpoczynanie każdego procesu roboczego z innym przesunięciem do pliku, zamiast wstępnego dzielenia jednego dużego pliku na wiele plików części. Użyj metody seek () i tell () pythona w każdym równoległym procesie roboczym, aby odczytać duży plik tekstowy w paskach, w różnych bajtach z przesunięciem bajtów w lokalizacjach początkowych i końcowych bajtów jednocześnie, jednocześnie. Możesz zrobić wyrażenie regularne na bajtach i zwrócić liczbę kanałów. To jest suma częściowa. Na koniec zsumuj sumy częściowe, aby uzyskać sumę globalną, gdy funkcja mapy powróci po zakończeniu procesu roboczego.
Oto kilka przykładowych testów porównawczych z wykorzystaniem sztuczki polegającej na przesunięciu bajtów równoległych:
Używam 2 plików: HIGGS.csv ma 8 GB. Pochodzi z repozytorium uczenia maszynowego UCI. all_bin .csv ma 40,4 GB i pochodzi z mojego obecnego projektu. Używam 2 programów: programu wc GNU, który jest dostarczany z Linuksem, i opracowanego przeze mnie czystego programu pytread fastread.py.
To około 4,5 GB / s, czyli 45 Gb / s, prędkość usuwania plików. To nie jest wirujący dysk twardy, przyjacielu. To właściwie dysk SSD Samsung Pro 950.
Poniżej znajduje się test porównawczy prędkości dla tego samego pliku liczonego w linii przez gnu wc, program skompilowany w czystym C.
Fajne jest to, że w tym przypadku mój czysty program w Pythonie zasadniczo pasował do prędkości skompilowanego programu C w GNU WC. Python jest interpretowany, ale C jest kompilowany, więc jest to dość interesujący wyczyn prędkości, myślę, że się zgodzisz. Oczywiście wc naprawdę trzeba zmienić na program równoległy, a wtedy naprawdę pobiłbym skarpety mojego programu python. Ale w obecnej formie gnu wc to tylko program sekwencyjny. Robisz, co możesz, a Python może dziś robić równolegle. Kompilacja Cython może mi pomóc (przez jakiś czas). Również pliki mapowane w pamięci nie zostały jeszcze zbadane.
Wniosek: Szybkość jest dobra dla czystego programu python w porównaniu do programu C. Jednak nie wystarczy używać czystego programu pythonowego nad programem C, przynajmniej do celów zliczania linii. Ogólnie technika ta może być używana do innego przetwarzania plików, więc ten kod Pythona jest nadal dobry.
Pytanie: Czy skompilowanie wyrażenia regularnego tylko raz i przekazanie go wszystkim pracownikom poprawi szybkość? Odpowiedź: Kompilacja wstępna Regex NIE pomaga w tej aplikacji. Podejrzewam, że powodem jest to, że dominują koszty związane z serializacją i tworzeniem procesów dla wszystkich pracowników.
Jeszcze jedna rzecz. Czy równoległe czytanie plików CSV w ogóle pomaga? Czy dysk jest wąskim gardłem, czy jest to procesor? Wiele tak zwanych najwyżej ocenianych odpowiedzi na stackoverflow zawiera powszechną mądrość programistyczną, że do odczytania pliku potrzebny jest tylko jeden wątek, najlepiej, jak można, mówią. Czy są jednak pewni?
Dowiedzmy Się:
O tak, tak. Równoległe czytanie plików działa całkiem dobrze. Cóż, proszę bardzo!
Ps. W przypadku, gdyby niektórzy z was chcieli wiedzieć, co by było, gdyby balanceFactor wynosił 2 podczas korzystania z jednego procesu roboczego? Cóż, to okropne:
Kluczowe części programu python fastread.py:
Def dla PartitionDataToWorkers to zwykły sekwencyjny kod. Zostawiłem to na wypadek, gdyby ktoś chciał poćwiczyć na programowaniu równoległym. Oddałem za darmo trudniejsze części: przetestowany i działający kod równoległy, dla twojej korzyści w nauce.
Dzięki: Projekt H2O typu open source autorstwa Arno i Cliffa oraz pracowników H2O za ich wspaniałe oprogramowanie i filmy instruktażowe, które dały mi inspirację do tego czysto pythonowego, równoległego czytnika offsetów bajtowych, jak pokazano powyżej. H2O dokonuje równoległego odczytu plików przy użyciu java, jest wywoływalny przez programy Python i R, i jest szalony szybki, szybszy niż cokolwiek na świecie przy czytaniu dużych plików CSV.
źródło
Katrielalex zapewnił sposób na otwarcie i odczytanie jednego pliku.
Jednak sposób, w jaki podąża Twój algorytm, odczytuje cały plik dla każdej linii pliku. Oznacza to, że całkowity odczyt pliku - i obliczenie odległości Levenshteina - zostanie wykonane N * N, jeśli N jest liczbą linii w pliku. Ponieważ martwisz się rozmiarem pliku i nie chcesz zachować go w pamięci, martwię się o wynikowy kwadratowy czas działania . Twój algorytm należy do klasy algorytmów O (n ^ 2), które często można ulepszyć dzięki specjalizacji.
Podejrzewam, że znasz już kompromis między pamięcią a środowiskiem uruchomieniowym, ale być może chciałbyś sprawdzić, czy istnieje skuteczny sposób na obliczenie wielu odległości Levenshteina równolegle. Jeśli tak, warto podzielić się tutaj swoim rozwiązaniem.
Ile linii mają twoje pliki i na jakim komputerze (mem i CPU) musi działać twój algorytm i jaki jest tolerowany czas działania?
Kod wyglądałby następująco:
Ale pytania dotyczą sposobu przechowywania odległości (macierzy?) I czy można uzyskać przewagę, przygotowując np. Linię_zewnętrzną do przetwarzania lub buforując niektóre wyniki pośrednie do ponownego wykorzystania.
źródło
Jeśli chcesz na przykład sprawdzić określoną linię dla długości większej niż 10, pracuj z tym, co już masz dostępne.
źródło
Z dokumentacji Pythona dla fileinput .input ():
ponadto definicją tej funkcji jest:
czytając między wierszami, to mówi mi, że
files
może to być lista, więc możesz mieć coś takiego:Zobacz tutaj, aby uzyskać więcej informacji
źródło
Zdecydowanie odradzam używanie domyślnego ładowania plików, ponieważ jest on strasznie powolny. Powinieneś przyjrzeć się funkcjom numpy i IOpro (np. Numpy.loadtxt ()).
http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html
https://store.continuum.io/cshop/iopro/
Następnie możesz podzielić operację parowania na części:
Prawie zawsze jest o wiele szybsze ładowanie danych w porcjach, a następnie wykonywanie na nich operacji macierzowych, niż robienie tego element po elemencie !!
źródło
Chcesz często czytać duży plik z ostatniego odczytu pozycji?
Stworzyłem skrypt używany do wycinania pliku access.log Apache kilka razy dziennie. Musiałem więc ustawić kursor pozycji na ostatniej linii parsowanej podczas ostatniego wykonania . W tym celu użyłem
file.seek()
ifile.seek()
metody, które pozwala na przechowywanie kursora w pliku.Mój kod:
źródło
Najlepszym sposobem na odczytanie dużego pliku, wiersz po wierszu, jest użycie funkcji wyliczającej w języku Python
źródło