Большой постоянный DataFrame в пандах

93

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

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

С помощью SAS я могу импортировать CSV-файл в набор данных SAS, и он может быть размером с мой жесткий диск.

Есть что-то аналогичное в pandas?

Я регулярно работаю с большими файлами и не имею доступа к распределенной вычислительной сети.

Желязный7
источник
Я не знаком с пандами, но вы можете просмотреть итерацию файла. pandas.pydata.org/pandas-docs/stable/…
монкут,

Ответы:

80

В принципе, не должно быть нехватки памяти, но в настоящее время существуют проблемы с памятью 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, верно?
Габриэль Грант
81

Уэс, конечно, прав! Я просто пытаюсь предоставить более полный пример кода. У меня была такая же проблема с файлом 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)?
Энди Хейден
@smci Попробовал быстро с теми же данными повторить x4 (550 Мб) или x8 (1,1 Гб). Интересно, что с или без [x for x in 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
что, если значения являются строковыми или категориальными - я получаю сообщение об ошибке: несовместимые категории в категориальном
конкатенации
41

Это старый поток, но я просто хотел сбросить здесь свое обходное решение. Сначала я попробовал chunksizeпараметр (даже с довольно маленькими значениями, например 10000), но это не сильно помогло; остались технические проблемы с объемом памяти (мой CSV был ~ 7,5 Гб).

Прямо сейчас я просто читаю куски CSV-файлов в цикле for и добавляю их, например, в базу данных 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
Очень полезно, чтобы увидеть реалистичный пример использования функции чтения по частям. Спасибо.
Alex Kestner
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)

После того, как все данные будут в базе данных, вы можете запросить нужные из базы данных.

BEN_YO
источник
3

Если вы хотите загружать огромные файлы csv, dask может быть хорошим вариантом. Он имитирует api pandas, поэтому очень похож на pandas

ссылка на dask на github

user8108173
источник
Спасибо, так как я разместил это, я использую dask и формат паркета.
Zelazny7 09
1

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

Вяз662
источник