Odkrywam przejście na Pythona i pandy jako wieloletni użytkownik SAS.
Jednak podczas dzisiejszego przeprowadzania niektórych testów byłem zaskoczony, że Pythonowi zabrakło pamięci podczas próby pandas.read_csv()
pliku CSV o wielkości 128 MB. Miał około 200 000 wierszy i 200 kolumn zawierających głównie dane liczbowe.
Dzięki SAS mogę zaimportować plik csv do zestawu danych SAS i może on mieć rozmiar odpowiadający rozmiarowi mojego dysku twardego.
Czy jest w tym coś analogicznego pandas
?
Regularnie pracuję z dużymi plikami i nie mam dostępu do rozproszonej sieci komputerowej.
Odpowiedzi:
Zasadniczo nie powinno zabraknąć pamięci, ale obecnie występują problemy z pamięcią przy
read_csv
dużych plikach spowodowane przez złożone problemy wewnętrzne Pythona (jest to niejasne, ale znane jest od dawna: http://github.com/pydata / pandy / Issues / 407 ).W tej chwili nie ma idealnego rozwiązania (tutaj jest nudne: możesz transkrybować plik wiersz po wierszu na wstępnie przydzieloną tablicę NumPy lub plik mapowany w pamięci -
np.mmap
), ale nad tym będę pracować w najbliższej przyszłości. Innym rozwiązaniem jest odczytanie pliku w mniejszych fragmentach (użycieiterator=True, chunksize=1000
), a następnie połączenie zpd.concat
. Problem pojawia się, gdy przeciągniesz cały plik tekstowy do pamięci jednym wielkim łykiem.źródło
Wes ma oczywiście rację! Wbijam się tylko w trochę bardziej kompletny przykładowy kod. Miałem ten sam problem z plikiem 129 Mb, który został rozwiązany przez:
import pandas as pd tp = pd.read_csv('large_dataset.csv', iterator=True, chunksize=1000) # gives TextFileReader, which is iterable with chunks of 1000 rows. df = pd.concat(tp, ignore_index=True) # df is DataFrame. If errors, do `list(tp)` instead of `tp`
źródło
df = concate(tp, ignore_index=True)
?AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader"
. Masz pojęcie, co się tutaj dzieje?pd.concat(list(tp), ignore_index=True)
To jest starszy wątek, ale chciałem tylko zrzucić tutaj moje rozwiązanie obejścia. Początkowo próbowałem tego
chunksize
parametru (nawet przy dość małych wartościach, takich jak 10000), ale niewiele to pomogło; nadal miałem problemy techniczne z rozmiarem pamięci (mój plik CSV miał ~ 7,5 Gb).W tej chwili po prostu czytam fragmenty plików CSV w podejściu pętli for i dodaję je np. Do bazy danych SQLite krok po kroku:
import pandas as pd import sqlite3 from pandas.io import sql import subprocess # In and output file paths in_csv = '../data/my_large.csv' out_sqlite = '../data/my.sqlite' table_name = 'my_table' # name for the SQLite database table chunksize = 100000 # number of lines to process at each iteration # columns that should be read from the CSV file columns = ['molecule_id','charge','db','drugsnow','hba','hbd','loc','nrb','smiles'] # Get number of lines in the CSV file nlines = subprocess.check_output('wc -l %s' % in_csv, shell=True) nlines = int(nlines.split()[0]) # connect to database cnx = sqlite3.connect(out_sqlite) # Iteratively read CSV and dump lines into the SQLite table for i in range(0, nlines, chunksize): df = pd.read_csv(in_csv, header=None, # no header, define column header manually later nrows=chunksize, # number of rows to read at each iteration skiprows=i) # skip rows that were already read # columns to read df.columns = columns sql.to_sql(df, name=table_name, con=cnx, index=False, # don't use CSV file index index_label='molecule_id', # use a unique column from DataFrame as index if_exists='append') cnx.close()
źródło
pandas.read_csv
bezpośrednio zwraca (przynajmniej w wersji, której obecnie używam) iterator, jeśli po prostu podasziterator=True
ichunksize=chunksize
. W związku z tym po prostu wykonaszfor
pętlę przezpd.read_csv
wywołanie, zamiast ponownie tworzyć jego instancję za każdym razem. Jednak kosztuje to tylko koszt połączenia, może nie mieć znaczącego wpływu.iterator=True
Ichunksize
parametry istniały już wtedy, jeśli dobrze pamiętam. Może w starszej wersji był błąd, który spowodował wysadzenie pamięci - spróbuję następnym razem, gdy przeczytam dużą ramkę DataFrame w Pandach (teraz głównie używam Blaze do takich zadań)Poniżej znajduje się mój przebieg pracy.
import sqlalchemy as sa import pandas as pd import psycopg2 count = 0 con = sa.create_engine('postgresql://postgres:pwd@localhost:00001/r') #con = sa.create_engine('sqlite:///XXXXX.db') SQLite chunks = pd.read_csv('..file', chunksize=10000, encoding="ISO-8859-1", sep=',', error_bad_lines=False, index_col=False, dtype='unicode')
Bazując na rozmiarze pliku, lepiej zoptymalizuj rozmiar fragmentu.
for chunk in chunks: chunk.to_sql(name='Table', if_exists='append', con=con) count += 1 print(count)
Po umieszczeniu wszystkich danych w bazie danych możesz wyszukiwać te, których potrzebujesz, z bazy danych.
źródło
Jeśli chcesz załadować ogromne pliki csv, dask może być dobrą opcją. Naśladuje api pandy, więc wydaje się być bardzo podobny do pandy
link do dask na githubie
źródło
Możesz użyć Pytable zamiast pandy df. Jest przeznaczony do dużych zbiorów danych, a format pliku to hdf5. Więc czas przetwarzania jest stosunkowo szybki.
źródło