2012-07-24 27 views
77

Eksploruję przełączanie na pytony i pandy jako długoletni użytkownik SAS.Duża, trwała DataFrame w pandach

Jednak podczas niektórych testów dzisiaj, byłem zaskoczony, że pyton zabrakło pamięci podczas próby pandas.read_csv() pliku CSV 128 MB. Miał około 200 000 wierszy i 200 kolumn głównie danych liczbowych.

Dzięki SAS mogę importować plik CSV do zbioru danych SAS i może on być tak duży jak mój dysk twardy.

Czy jest coś analogicznego w pandas?

Regularnie pracuję z dużymi plikami i nie mam dostępu do rozproszonej sieci komputerowej.

+0

nie jestem zaznajomiony z pand, ale może chcesz przejrzeć iteracja pliku. http://pandas.pydata.org/pandas-docs/stable/io.html # iterowanie-through-files-chunk-by-chunk – monkut

Odpowiedz

69

W zasadzie nie powinno zabraknąć pamięci, ale obecnie występują problemy z pamięcią z read_csv na dużych plikach spowodowane przez skomplikowane wewnętrzne problemy Pythona (jest to niejasne, ale znane od dawna: http://github.com/pydata/pandas/issues/407).

W tej chwili nie ma idealnego rozwiązania (tutaj jest nudny: można transkrybować plik rząd po rzędzie do wstępnie przydzielonej tablicy NumPy lub pliku odwzorowanego w pamięci - np.mmap), ale to jeden Będę pracował w najbliższej przyszłości. Innym rozwiązaniem jest odczytanie pliku na mniejsze fragmenty (użyj iterator=True, chunksize=1000), a następnie połącz z pd.concat. Problem pojawia się po przeciągnięciu całego pliku tekstowego do pamięci w jednym dużym slurpie.

+1

Powiedz, że mogę odczytać plik i połączyć je wszystkie w jedną ramkę DataFrame. Czy DataFrame musi znajdować się w pamięci? Dzięki SAS mogę pracować z zestawami danych o dowolnej wielkości, o ile mam miejsce na dysku twardym. Czy to samo z DataFrames? Mam wrażenie, że są ograniczone przez pamięć RAM, a nie na twardym dysku. Przepraszam za pytanie noob i dziękuję za pomoc. Cieszę się z twojej książki. – Zelazny7

+3

Dobrze, jesteś ograniczony przez RAM. SAS rzeczywiście ma znacznie lepsze wsparcie dla "out-core" big data processing. –

+5

@WesMcKinney Te obejścia nie powinny być dłużej potrzebne, ponieważ nowy program ładujący CSV wylądował w wersji 0.10, prawda? –

71

Wes ma oczywiście rację! Po prostu dzwonię, żeby podać trochę pełniejszy przykładowy kod. Miałem ten sam problem z pliku 129 MB, który został rozwiązany przez:

from pandas import * 

tp = read_csv('large_dataset.csv', iterator=True, chunksize=1000) # gives TextFileReader, which is iterable with chunks of 1000 rows. 
df = concat(tp, ignore_index=True) # df is DataFrame. If errors, do `list(tp)` instead of `tp` 
+4

Myślę, że możesz po prostu zrobić 'df = concate (tp, ignore_index = True)'? –

+0

@AndyHayden: Oczywiście! Naprawiono to. – fickludd

+0

@smci Próbowałem to szybko z tymi samymi danymi powtórzonymi x4 (550 Mb) lub x8 (1.1 Gb). Co ciekawe, z lub bez [x dla x in tp], x4 przeszedł dobrze, a x8 rozbił się w MemoryError. – fickludd

33

ten jest stary wątek, ale ja po prostu chciałem zrzucić moje obejście problemu tutaj. Początkowo wypróbowałem parametr chunksize (nawet przy dość małych wartościach, takich jak 10000), ale to nie pomogło; wciąż miał problemy techniczne z rozmiarem pamięci (mój CSV wynosił ~ 7,5 Gb).

Teraz, po prostu czytać fragmenty plików CSV w dla pętli podejścia i dodać je na przykład do SQLite kroku bazy 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()  
+2

Bardzo przydatny do zobaczenia realistyczny przypadek użycia dla funkcji czytania fragmentów. Dzięki. –

+4

Po prostu mała uwaga do tego starego tematu: 'pandas.read_csv' bezpośrednio zwraca (przynajmniej w wersji, której aktualnie używam) iterator, jeśli po prostu podasz' iterator = True' i 'chunksize = chunksize'. Dlatego po prostu wykonuj pętlę 'for' zamiast wywołania' pd.read_csv', zamiast powtarzać ją za każdym razem. Jednak to kosztuje tylko koszt połączenia, może nie mieć znaczącego wpływu. –

+1

Cześć, Joel. Dzięki za notatkę! Parametry 'iterator = True' i' chunksize' już istniały wtedy, jeśli dobrze pamiętam. Może był błąd w starszej wersji, który spowodował powiększenie pamięci - następnym razem spróbuję przeczytać dużą ramkę DataFrame w Pandach (głównie używam teraz Blaze do takich zadań) – Sebastian

1

Można użyć Pytable zamiast pandy DF. Jest przeznaczony dla dużych zestawów danych, a format pliku jest w formacie hdf5. Czas przetwarzania jest stosunkowo szybki.

1

Poniżej znajduje się mój przepływ pracy.

import sqlalchemy as sa 
import pandas as pd 
import psycopg2 

count = 0 
con = sa.create_engine('postgresql://postgres:[email protected]: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') 

W zależności od rozmiaru pliku lepiej zoptymalizować porcje.

for chunk in chunks: 
     chunk.to_sql(name='Table', if_exists='append', con=con) 
     count += 1 
     print(count) 

Po uzyskaniu wszystkich danych w bazie danych można wyszukiwać potrzebne informacje z bazy danych.

3

Jeśli chcesz wczytać duże pliki CSV, dobrym rozwiązaniem może być daszek. To naśladuje API pandy, więc czuje się bardzo podobny do Pandy

link to dask on github

+0

Dzięki, od kiedy napisałem to używam beczki i formatu parkietu. – Zelazny7

Powiązane problemy