Błąd pamięci podczas korzystania z pand read_csv

79

Próbuję zrobić coś dość prostego, wczytując duży plik csv do ramki danych pandy.

data = pandas.read_csv(filepath, header = 0, sep = DELIMITER,skiprows = 2)

Kod albo kończy się niepowodzeniem MemoryError, albo po prostu nigdy się nie kończy.

Użycie pamięci w menedżerze zadań zatrzymało się na 506 Mb i po 5 minutach braku zmian i braku aktywności procesora w procesie zatrzymałem je.

Używam pand w wersji 0.11.0.

Wiem, że kiedyś występował problem z pamięcią w parserze plików, ale według http://wesmckinney.com/blog/?p=543 powinno to zostać naprawione.

Plik, który próbuję odczytać ma 366 Mb, powyższy kod działa, jeśli skrócę plik do czegoś krótkiego (25 Mb).

Zdarzyło się również, że wyskoczyło mi wyskakujące okienko z informacją, że nie może napisać na adres 0x1e0baf93 ...

Ślad stosu:

Traceback (most recent call last):
  File "F:\QA ALM\Python\new WIM data\new WIM data\new_WIM_data.py", line 25, in
 <module>
    wimdata = pandas.read_csv(filepath, header = 0, sep = DELIMITER,skiprows = 2
)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\io\parsers.py"
, line 401, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\io\parsers.py"
, line 216, in _read
    return parser.read()
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\io\parsers.py"
, line 643, in read
    df = DataFrame(col_dict, columns=columns, index=index)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\frame.py"
, line 394, in __init__
    mgr = self._init_dict(data, index, columns, dtype=dtype)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\frame.py"
, line 525, in _init_dict
    dtype=dtype)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\frame.py"
, line 5338, in _arrays_to_mgr
    return create_block_manager_from_arrays(arrays, arr_names, axes)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1820, in create_block_manager_from_arrays
    blocks = form_blocks(arrays, names, axes)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1872, in form_blocks
    float_blocks = _multi_blockify(float_items, items)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1930, in _multi_blockify
    block_items, values = _stack_arrays(list(tup_block), ref_items, dtype)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1962, in _stack_arrays
    stacked = np.empty(shape, dtype=dtype)
MemoryError
Press any key to continue . . .

Trochę tła - próbuję przekonać ludzi, że Python może zrobić to samo co R. W tym celu próbuję powielić skrypt R, który to robi

data <- read.table(paste(INPUTDIR,config[i,]$TOEXTRACT,sep=""), HASHEADER, DELIMITER,skip=2,fill=TRUE)

R nie tylko udaje się dobrze odczytać powyższy plik, ale nawet czyta kilka z tych plików w pętli for (a następnie robi coś z danymi). Jeśli Python ma problem z plikami o takim rozmiarze, być może toczę przegraną bitwę ...

Anne
źródło
1
Zdecydowanie pandy nie powinny mieć problemów z plikami CSV tego rozmiaru. Czy możesz opublikować ten plik online?
Andy Hayden
1
Możesz także spróbować przejść nrows=something smalldo, read_csvaby upewnić się, że nie jest to rozmiar pliku powodującego problemy, co, jak powiedział Andy, nie powinno mieć miejsca.
TomAugspurger
1
może to być coś związanego z „Visual Studio, używając Anacondy i PTVS” ... może spróbuj też w zwykłym Pythonie
Andy Hayden
3
Znalazłem następujące rozwiązania, aby rozwiązać problem: Przeczytaj plik csv jako fragmenty csv_chunks = pandas.read_csv(filepath, sep = DELIMITER,skiprows = 1, chunksize = 10000), a następnie połącz fragmenty df = pandas.concat(chunk for chunk in csv_chunks). Nadal interesuje mnie, dlaczego czytanie go za jednym razem nie działa, dla mnie wygląda to na problem z czytnikiem csv.
Anne,
11
Jeśli ktoś nadal to śledzi, mam trochę aktualizacji. Doszedłem do wniosku, że parser csv jest w porządku (i bardzo szybki), ale podczas tworzenia ramek danych występuje pewien problem z pamięcią. Powód, dla którego w to wierzę: kiedy używam chunksize=1000hacka do odczytywania csv, a następnie próbuję połączyć wszystkie fragmenty w dużą ramkę danych, w tym momencie pamięć wysadza się, z około 3-4 razy większą ilością pamięci w porównaniu do rozmiaru oryginalnego pliku. Czy ktoś ma pojęcie, dlaczego ramka danych może wybuchnąć?
Anne,

Odpowiedzi:

32

Ograniczenie pamięci systemu Windows

Błędy pamięci zdarzają się często w Pythonie podczas korzystania z wersji 32-bitowej w systemie Windows. Dzieje się tak, ponieważ procesy 32-bitowe domyślnie otrzymują tylko 2 GB pamięci do gry .

Sztuczki zmniejszające zużycie pamięci

Jeśli nie używasz 32-bitowego Pythona w systemie Windows, ale chcesz poprawić wydajność pamięci podczas czytania plików csv, jest pewna sztuczka.

Funkcja pandas.read_csv przyjmuje opcję o nazwie dtype. Dzięki temu pandy wiedzą, jakie typy istnieją w danych CSV.

Jak to działa

Domyślnie pandy będą próbowały odgadnąć, jakie dtypy ma twój plik csv. Jest to bardzo ciężka operacja, ponieważ podczas określania typu dtype musi przechowywać wszystkie surowe dane jako obiekty (ciągi znaków) w pamięci.

Przykład

Załóżmy, że Twój plik CSV wygląda tak:

name, age, birthday
Alice, 30, 1985-01-01
Bob, 35, 1980-01-01
Charlie, 25, 1990-01-01

Ten przykład nie jest oczywiście problemem wczytywania do pamięci, ale to tylko przykład.

Gdyby pandy miały odczytać powyższy plik csv bez żadnej opcji dtype, wiek byłby przechowywany jako ciągi w pamięci, dopóki pandy nie przeczytają wystarczającej liczby wierszy pliku csv, aby dokonać kwalifikowanego przypuszczenia.

Myślę, że domyślnie w pandach odczytuje się 1 000 000 wierszy przed odgadnięciem typu dtype.

Rozwiązanie

Podając dtype={'age':int}jako opcję do .read_csv()woli, pandy będą wiedzieć, że wiek powinien być interpretowany jako liczba. Oszczędza to dużo pamięci.

Problem z uszkodzonymi danymi

Jeśli jednak plik csv byłby uszkodzony, na przykład:

name, age, birthday
Alice, 30, 1985-01-01
Bob, 35, 1980-01-01
Charlie, 25, 1990-01-01
Dennis, 40+, None-Ur-Bz

Określenie dtype={'age':int}spowoduje przerwanie .read_csv()polecenia, ponieważ nie może ono rzutować "40+"na int. Dlatego dokładnie oczyść swoje dane!

Tutaj możesz zobaczyć, jak użycie pamięci przez ramkę danych pandy jest znacznie wyższe, gdy elementy zmiennoprzecinkowe są przechowywane jako ciągi:

Spróbuj sam

df = pd.DataFrame(pd.np.random.choice(['1.0', '0.6666667', '150000.1'],(100000, 10)))
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# 224544 (~224 MB)

df = pd.DataFrame(pd.np.random.choice([1.0, 0.6666667, 150000.1],(100000, 10)))
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# 79560 (~79 MB)
firelynx
źródło
Widzę, jak to może przyspieszyć odczyt danych, ale zmniejszyć pamięć? Z pewnością nie powinno być konieczne przechowywanie więcej niż kilku wartości łańcuchowych na kolumnę, aby odgadnąć typ danych? To znaczy, jeśli nie masz biliardów kolumn lub read_csvfunkcja robi coś niesamowicie funky, byłbym bardzo zaskoczony, gdyby zużycie pamięci było zauważalnie wyższe.
Hannes Ovrén
2
@ HannesOvrén Typu danych nie można odgadnąć, zanim nie przeczytasz znacznej części danych, w przeciwnym razie istnieje ryzyko konieczności wielokrotnej zmiany, co wiąże się z kosztami. Myślę, że pandy domyślnie odczytują pierwszy milion wierszy przed podjęciem decyzji. Zmniejszyłem profil pamięci naszego produktu opartego na pandach 50 razy, dodając dtypes do ładunków csv.
firelynx
1
Hmm, myśląc o tym, myślę, że może być problem z podjęciem decyzji, czy „3” będzie zmiennoprzecinkową, czy int, chyba że gdzieś zobaczysz „2,5”. Dziękuję za wyjaśnienie. Nie wiedziałem o tym.
Hannes Ovrén
To nie jest prawda. Z dtype jest i w pamięci droższe iz czasem wolniejsze. Testowano 6 razy z dtype w read_csv. Średnie to: ... pamięć nr dtype: 12,121,429,333333334 | pamięć z dtype: 12.124.160.0 ... W testach czasowych 13 razy średnie to: ... czas bez dtypów: 2.0494697460761437 | time with dtypes: 2.100334332539485 ... Użyto: import os import psutil process = psutil.Process (os.getpid ()) print (process.memory_info (). rss) ___Wiersze danych: 1,5 miliona z trzech oddzielnych zbiorów danych, cols 90% są typem obiektu. * Oczywiście float ma mniejszy rozmiar niż typ string
nikolaosmparoutis
@nikolaos_mparoutis Nie jestem pewien, jak otrzymałeś te wyniki. Może chcesz napisać własną odpowiedź, ponieważ trudno jest śledzić, czym jest kod i jaki jest komentarz w Twoim komentarzu. Moja odpowiedź jest dość stara, może coś się zmieniło.
firelynx
6

Miałem ten sam problem z pamięcią po prostym odczytaniu pliku tekstowego rozdzielanego tabulatorami o rozmiarze około 1 GB (ponad 5,5 miliona rekordów) i to rozwiązało problem z pamięcią :

df = pd.read_csv(myfile,sep='\t') # didn't work, memory error
df = pd.read_csv(myfile,sep='\t',low_memory=False) # worked fine and in less than 30 seconds

Spyder 3.2.3 Python 2.7.13 64 bity

mooseman
źródło
7
Jest sprzeczne z intuicją, że low_memory=Falsepowinno zużywać mniej pamięci…
guillefix
2

Używam Pand na moim Linuksie i napotkałem wiele wycieków pamięci, które zostały rozwiązane dopiero po uaktualnieniu Pandy do najnowszej wersji po sklonowaniu go z github.

Tarik
źródło
1

Napotkałem ten problem również, gdy pracowałem na maszynie wirtualnej lub w innym miejscu, w którym pamięć jest ściśle ograniczona. Nie ma to nic wspólnego z pandami, numpy czy csv, ale zawsze się wydarzy, jeśli spróbujesz użyć większej ilości pamięci, której możesz używać, nawet nie tylko w Pythonie.

Jedyną szansą, jaką masz, jest to, czego już próbowałeś, spróbuj rozłożyć to duże na mniejsze kawałki, które pasują do pamięci.

Jeśli kiedykolwiek zadałeś sobie pytanie, o co chodzi w MapReduce, dowiedziałeś się sam ... MapReduce spróbowałoby rozprowadzić porcje na wielu maszynach, spróbowałbyś przetworzyć porcje na jednym komputerze jeden po drugim.

To, czego dowiedziałeś się z konkatenacją plików porcji, może rzeczywiście stanowić problem, być może potrzebna jest kopia w tej operacji ... ale ostatecznie może to uratować Cię w obecnej sytuacji, ale jeśli twój plik csv stanie się trochę większy możesz znowu biec pod tą ścianę ...

Możliwe też, że pandy są tak inteligentne, że w rzeczywistości ładują poszczególne fragmenty danych do pamięci tylko wtedy, gdy coś z nimi zrobisz, na przykład konkatenację z dużym df?

Możesz spróbować kilku rzeczy:

  • Nie ładuj wszystkich danych naraz, ale podziel je na części
  • O ile wiem, hdf5 jest w stanie wykonać te fragmenty automatycznie i wczytuje tylko część, na której obecnie działa twój program
  • Sprawdź, czy typy są w porządku, łańcuch „0.111111” wymaga więcej pamięci niż liczba zmiennoprzecinkowa
  • Czego właściwie potrzebujesz, jeśli adres jest ciągiem znaków, możesz go nie potrzebować do analizy numerycznej ...
  • Baza danych może pomóc w dostępie i ładowaniu tylko części, których faktycznie potrzebujesz (np. Tylko 1% aktywnych użytkowników)
SebastianNeubauer
źródło
1

Nie ma błędu dla Pandas 0.12.0 i NumPy 1.8.0.

Udało mi się stworzyć dużą ramkę DataFrame i zapisać ją w pliku csv, a następnie pomyślnie ją przeczytać. Zobacz przykład tutaj . Rozmiar pliku to 554 Mb (działał nawet dla pliku 1,1 Gb, trwało to dłużej, aby wygenerować plik 1,1 Gb z częstotliwością wykorzystania 30 sekund). Chociaż mam dostępne 4 GB pamięci RAM.

Moja sugestia to spróbuj zaktualizować Pandy. Inną rzeczą, która może się przydać, jest próba uruchomienia skryptu z wiersza poleceń, ponieważ dla R nie używasz Visual Studio (zostało to już zasugerowane w komentarzach do twojego pytania), stąd ma więcej dostępnych zasobów.

Oleksandr
źródło
1

Próbowałem chunksizeczytając duży plik CSV

reader = pd.read_csv(filePath,chunksize=1000000,low_memory=False,header=0)

Odczyt jest teraz listą. Możemy iterować readeri zapisywać / dołączać do nowego csv lub możemy wykonać dowolną operację

for chunk in reader:
    print(newChunk.columns)
    print("Chunk -> File process")
    with open(destination, 'a') as f:
        newChunk.to_csv(f, header=False,sep='\t',index=False)
        print("Chunk appended to the file")
muTheTechie
źródło
0

Dodaj następujące: ratingi = pd.read_csv (..., low_memory = False, memory_map = True )

Moja pamięć z tymi dwoma: # 319.082.496 Bez tych dwóch: # 349.110.272

nikolaosmparoutis
źródło
-1

Chociaż jest to obejście, a nie tylko poprawka, spróbuję przekonwertować ten plik CSV na JSON (powinno być trywialne) i read_jsonzamiast tego użyć metody - piszę i czytam duże ramki JSON / dataframe (100s MB) w Pandas to sposób bez problemu.

LetMeSOThat4U
źródło