Великий, стійкий DataFrame у пандах


91

Я досліджую перехід на python і pandas як давній користувач SAS.

Однак під час запуску деяких тестів сьогодні я був здивований тим, що у python не вистачало пам’яті при спробі pandas.read_csv()створити файл csv на 128 Мб. Він мав близько 200 000 рядків і 200 стовпців переважно числових даних.

За допомогою SAS я можу імпортувати файл CSV у набір даних SAS, і він може бути таким же великим, як мій жорсткий диск.

Чи є щось аналогічне в pandas?

Я регулярно працюю з великими файлами і не маю доступу до розподіленої обчислювальної мережі.


Я не знайомий з пандами, але ви можете переглянути ітерацію файлу. pandas.pydata.org/pandas-docs/stable/…
monkut

Відповіді:


79

В принципі, у нього не повинно закінчитися пам'яті, але в даний час існують проблеми з пам'яттю read_csvу великих файлах, викликані якимись складними внутрішніми проблемами Python (це неясно, але це було відомо давно: http://github.com/pydata / pandas / issues / 407 ).

На даний момент не існує ідеального рішення (ось нудне: ви можете переписати файл по черзі в заздалегідь виділений масив NumPy або файл із картою пам'яті-- np.mmap), але це одне, з яким я працюватиму найближчим часом. Іншим рішенням є читання файлу меншими шматками (використання iterator=True, chunksize=1000), потім об'єднання, а потім pd.concat. Проблема виникає, коли ви витягуєте весь текстовий файл в пам’ять одним великим шматком.


1
Скажімо, я можу прочитати файл і об'єднати їх усі в один DataFrame. Чи повинен DataFrame знаходитися в пам'яті? За допомогою SAS я можу працювати з наборами даних будь-якого розміру, поки у мене є місце на жорсткому диску. Чи так само з DataFrames? У мене складається враження, що їх обмежує оперативна пам’ять, а не місце на жорсткому диску. Вибачте за запитання про нуб і дякую за допомогу. Мені подобається ваша книга.
Zelazny7

3
Правильно, вас обмежує оперативна пам’ять. SAS справді має набагато кращу підтримку для "позаядерної" обробки великих даних.
Wes McKinney

5
@WesMcKinney Ці обхідні шляхи більше не потрібні, оскільки через новий завантажувач csv ви потрапили в 0.10, так?
Габріель Грант

79

Вес, звичайно, має рацію! Я просто підготувався, щоб надати трохи більш повний приклад коду. У мене була та ж проблема з файлом 129 Мб, яку вирішили:

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`

6
Я думаю, ти можеш просто зробити df = concate(tp, ignore_index=True)?
Andy Hayden

@smci Спробував це швидко, використовуючи ті самі дані, що повторюються x4 (550 Мб) або x8 (1,1 Гб). Цікаво, що з або без [x для x в tp], x4 пройшов нормально, і x8 зазнав аварії в MemoryError.
fickludd

3
Я отримую цю помилку при її використанні: AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". Будь-яка ідея, що тут відбувається?
Принц Кумар

3
Ця помилка буде виправлена ​​за 0,14 (незабаром вийде), github.com/pydata/pandas/pull/6941 ; обхідний шлях для <0,14,0 - зробитиpd.concat(list(tp), ignore_index=True)
Джефф,

1
що якщо значення є рядками або категоріальними - я отримую помилку: несумісні категорії в категоріальному concat
As3adTintin

41

Це старіший потік, але я просто хотів залишити тут своє рішення для обходу. Спочатку я спробувавchunksize параметр (навіть із досить малими значеннями, як 10000), але це мало допомогло; все ще мали технічні проблеми з обсягом пам'яті (мій CSV становив ~ 7,5 Гб).

Зараз я просто читаю фрагменти файлів CSV у підході for-loop і додаю їх, наприклад, до бази даних SQLite поетапно:

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()    

4
Дуже корисно, щоб побачити реалістичний приклад використання фрагментованої функції читання. Дякую.
Алекс Кестнер,

5
Лише невелике зауваження до цієї старої теми: pandas.read_csvбезпосередньо повертає (принаймні у тій версії, яку я зараз використовую) ітератор, якщо ви просто надаєте iterator=Trueта chunksize=chunksize. Отже, ви просто зробите forцикл над pd.read_csvвикликом, замість того, щоб повторно створювати його кожного разу. Однак це коштує лише накладних витрат на дзвінок, можливо, це не суттєво впливає.
Joël

1
Привіт, Джоеле. Дякую за записку! Тоді параметри iterator=Trueі chunksizeвже існували, якщо я добре пам’ятаю. Можливо, у старшій версії була помилка, яка спричинила вибух пам'яті - я спробую ще раз, коли наступного разу прочитаю великий DataFrame у Pandas (зараз для таких завдань я в основному використовую Blaze)

6

Нижче мій робочий потік.

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')

Виходячи з розміру файлу, вам краще оптимізувати chunksize.

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

Отримавши всі дані в базі даних, ви можете запитати необхідні дані з бази даних.


3

Якщо ви хочете завантажити величезні файли CSV, хорошим варіантом може бути dask. Він імітує панд-апі, тому відчуває себе дуже схожим на панд

посилання на dask на github


Дякую, відколи я розмістив це, я використовую dask та формат паркету.
Zelazny7,

1

Ви можете використовувати Pytable, а не pandas df. Він призначений для великих наборів даних, а формат файлу - hdf5. Тож час обробки є відносно швидким.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.