Как мне прочитать большой CSV-файл с пандами?


194

Я пытаюсь прочитать большой CSV-файл (около 6 ГБ) в пандах, и я получаю ошибку памяти:

MemoryError                               Traceback (most recent call last)
<ipython-input-58-67a72687871b> in <module>()
----> 1 data=pd.read_csv('aphro.csv',sep=';')

...

MemoryError: 

Любая помощь в этом?


3
Любопытно, что очень похожий вопрос был задан почти за год до этого ...
DarkCygnus


Ответы:


264

Ошибка показывает, что на машине недостаточно памяти для одновременного считывания всего CSV-файла в DataFrame. Предполагая, что вам не нужен весь набор данных в памяти одновременно, один из способов избежать этой проблемы - обработать CSV порциями (указав chunksizeпараметр):

chunksize = 10 ** 6
for chunk in pd.read_csv(filename, chunksize=chunksize):
    process(chunk)

chunksizeПараметр определяет количество строк на порцию. ( chunksizeКонечно, последний блок может содержать меньше строк)


17
вам обычно нужно 2X конечной памяти для чтения чего-либо (из csv, хотя другие форматы лучше при более низких требованиях к памяти). К вашему сведению, это правда, что вы пытаетесь сделать почти все одновременно. Намного лучше разбить его на части (который использует постоянную память).
Джефф

24
@altabq: Проблема в том, что у нас недостаточно памяти для создания единого DataFrame, содержащего все данные. Приведенное выше решение пытается справиться с этой ситуацией, сокращая порции (например, путем агрегирования или извлечения только нужной информации) по одной порции за раз - таким образом, экономя память. Что бы вы ни делали, НЕ звоните DF.append(chunk)внутри цикла. Это будет использовать O(N^2)операции копирования. Лучше добавить агрегированные данные в список , а затем построить DataFrame из списка с одного вызова к pd.DataFrameили pd.concat( в зависимости от типа агрегированных данных).
unutbu

12
@altabq: вызов DF.append(chunk)в цикле требует O(N^2)операций копирования, где Nразмер блоков, потому что каждый вызов DF.appendвозвращает новый DataFrame. Вызов pd.DataFrameили pd.concat один раз вне цикла уменьшает количество копий в O(N).
unutbu

5
@Pyderman: Да, chunksizeпараметр относится к количеству строк на чанк. chunksizeКонечно, последний блок может содержать меньше строк.
unutbu

7
@Pyderman: Да; Вызов pd.concat([list_of_dfs]) один раз после цикла намного быстрее, чем вызов, pd.concatили df.appendмного раз внутри цикла. Конечно, вам понадобится значительное количество памяти, чтобы вместить весь 6 ГБ csv в один DataFrame.
unutbu

86

Чанкинг не всегда должен быть первым портом захода для этой проблемы.

  1. Является ли файл большим из-за повторяющихся нечисловых данных или нежелательных столбцов?

    Если это так, вы можете иногда видеть значительную экономию памяти, читая в столбцах категории и выбирая необходимые столбцы с помощью параметра pd.read_csv usecols .

  2. Ваш рабочий процесс требует нарезки, манипуляции, экспорта?

    Если это так, вы можете использовать dask.dataframe для нарезки , выполнения своих вычислений и многократного экспорта. Чанкинг выполняется dask без вывода сообщений, который также поддерживает подмножество API pandas.

  3. Если ничего не помогает, читайте построчно через чанки.

    Кусок через панд или через библиотеку csv в крайнем случае.


34

Я поступил так:

chunks=pd.read_table('aphro.csv',chunksize=1000000,sep=';',\
       names=['lat','long','rf','date','slno'],index_col='slno',\
       header=None,parse_dates=['date'])

df=pd.DataFrame()
%time df=pd.concat(chunk.groupby(['lat','long',chunk['date'].map(lambda x: x.year)])['rf'].agg(['sum']) for chunk in chunks)

23
Есть ли причина, по которой вы переключились read_csvна read_table?
Пайдермен

33

Для больших данных я рекомендую использовать библиотеку "dask",
например:

# Dataframes implement the Pandas API
import dask.dataframe as dd
df = dd.read_csv('s3://.../2018-*-*.csv')

Вы можете прочитать больше из документации здесь .

Еще одна отличная альтернатива - использовать модин, потому что все функции идентичны пандам, но он используется в библиотеках распределенных фреймов данных, таких как dask.


11
Любые преимущества по сравнению с пандами, может быть
полезно

2
Я не использовал Dask очень долго, но основные преимущества в моих случаях использования заключались в том, что Dask может работать параллельно на нескольких машинах, а также помещать данные в виде срезов в память.
Симбараше Тимоти Моци

2
Спасибо! заменяет панд или работает поверх панд как слой
PirateApp

3
Добро пожаловать, он работает как оболочка для Numpy, Pandas и Scikit-Learn.
Симбараше Тимоти Моци

1
Я пытался столкнуться с несколькими проблемами с Dask и всегда выдает ошибку для всего. Даже с кусками Это выдает ошибки памяти тоже. Смотрите stackoverflow.com/questions/59865572/…
Genarito

10

Приведенный выше ответ уже удовлетворяет тему. В любом случае, если вам нужны все данные в памяти - взгляните на bcolz . Это сжатие данных в памяти. У меня был действительно хороший опыт с этим. Но его хватает много функций панд

Изменить: Я получил степень сжатия около 1/10 или оригинального размера, я думаю, конечно, в зависимости от типа данных. Важными недостающими чертами были агрегаты.


2
Пожалуйста, улучшите этот ответ, сообщив нам: а) какие коэффициенты сжатия вы получаете и б) какие основные характеристики панды отсутствуют? Это может обращаться с NA? строки? categoricals? даты?
smci

А? Это может обращаться с NA? строки? categoricals? даты? Это то, что делает чтение панд медленным и дряблым. NA и объекты, такие как строки (даже короткие), являются убийцами. Кстати, .ipynb, на который ссылается ваш блог, не работает.
smci

1
@ smci я читал тебе записку. но я предлагаю вам взглянуть на документы. я должен был бы прочитать их сам.
PlagTag

2
Итак, он не может обрабатывать NA, строки или даты. Я сомневаюсь, что он может обрабатывать поплавки либо.
smci

1
Я полагаю, что вы можете предварительно обработать пандами, используя chunksупомянутый метод, а затем использовать bcolz, если для анализа вам нужны все данные в памяти. Просто мысль.
JakeCowton

6

Вы можете читать данные как куски и сохранять каждый кусок как рассол.

import pandas as pd 
import pickle

in_path = "" #Path where the large file is
out_path = "" #Path to save the pickle files to
chunk_size = 400000 #size of chunks relies on your available memory
separator = "~"

reader = pd.read_csv(in_path,sep=separator,chunksize=chunk_size, 
                    low_memory=False)    


for i, chunk in enumerate(reader):
    out_file = out_path + "/data_{}.pkl".format(i+1)
    with open(out_file, "wb") as f:
        pickle.dump(chunk,f,pickle.HIGHEST_PROTOCOL)

На следующем шаге вы читаете в рассолах и добавляете каждый рассол к желаемому фрейму данных.

import glob
pickle_path = "" #Same Path as out_path i.e. where the pickle files are

data_p_files=[]
for name in glob.glob(pickle_path + "/data_*.pkl"):
   data_p_files.append(name)


df = pd.DataFrame([])
for i in range(len(data_p_files)):
    df = df.append(pd.read_pickle(data_p_files[i]),ignore_index=True)

3
Если ваш финал dfполностью помещается в память (как подразумевается) и содержит тот же объем данных, что и входные данные, вам, конечно, вообще не нужно разбивать на части?
JPP

В этом случае вам понадобится порция, если, например, ваш файл очень широкий (например, более 100 столбцов с большим количеством строковых столбцов). Это увеличивает память, необходимую для удержания df в памяти. Даже такой файл объемом 4 ГБ может использовать от 20 до 30 ГБ ОЗУ на коробке с 64 ГБ ОЗУ.
cdabel

4

Функции read_csv и read_table практически одинаковы. Но вы должны назначить разделитель «,», когда вы используете функцию read_table в вашей программе.

def get_from_action_data(fname, chunk_size=100000):
    reader = pd.read_csv(fname, header=0, iterator=True)
    chunks = []
    loop = True
    while loop:
        try:
            chunk = reader.get_chunk(chunk_size)[["user_id", "type"]]
            chunks.append(chunk)
        except StopIteration:
            loop = False
            print("Iteration is stopped")

    df_ac = pd.concat(chunks, ignore_index=True)

Было бы полезно, если бы в вашем посте было указано, что ваш вопрос. Как "В чем разница между read_csv и read_table?" или "Почему для чтения таблицы нужен разделитель?"
nate_weldon

1
Это зависит от того, как выглядит ваш файл. Некоторые файлы имеют общие разделители, такие как «,» или «|» или "\ t", но вы можете увидеть другие файлы с разделителями, такими как 0x01, 0x02 (создание этого) и т. д. Таким образом, read_table больше подходит для необычных разделителей, но read_csv может выполнять ту же работу так же хорошо.
Науфал

3

Решение 1:

Использование панд с большими данными

Решение 2:

TextFileReader = pd.read_csv(path, chunksize=1000)  # the number of rows per chunk

dfList = []
for df in TextFileReader:
    dfList.append(df)

df = pd.concat(dfList,sort=False)

3
Здесь мы снова загружаем файл размером 6 ГБ полностью в память. Есть ли варианты, мы можем обработать текущий кусок и затем прочитать следующий кусок
debaonline4u

6
просто не делайте dfList.append, просто обрабатывайте каждый чанк ( df) отдельно
gokul_uf

3

Вот следующий пример:

chunkTemp = []
queryTemp = []
query = pd.DataFrame()

for chunk in pd.read_csv(file, header=0, chunksize=<your_chunksize>, iterator=True, low_memory=False):

    #REPLACING BLANK SPACES AT COLUMNS' NAMES FOR SQL OPTIMIZATION
    chunk = chunk.rename(columns = {c: c.replace(' ', '') for c in chunk.columns})

    #YOU CAN EITHER: 
    #1)BUFFER THE CHUNKS IN ORDER TO LOAD YOUR WHOLE DATASET 
    chunkTemp.append(chunk)

    #2)DO YOUR PROCESSING OVER A CHUNK AND STORE THE RESULT OF IT
    query = chunk[chunk[<column_name>].str.startswith(<some_pattern>)]   
    #BUFFERING PROCESSED DATA
    queryTemp.append(query)

#!  NEVER DO pd.concat OR pd.DataFrame() INSIDE A LOOP
print("Database: CONCATENATING CHUNKS INTO A SINGLE DATAFRAME")
chunk = pd.concat(chunkTemp)
print("Database: LOADED")

#CONCATENATING PROCESSED DATA
query = pd.concat(queryTemp)
print(query)

2

Вы можете попробовать sframe, который имеет тот же синтаксис, что и pandas, но позволяет вам манипулировать файлами, которые больше вашей оперативной памяти.


Ссылка на документы SFrame: turi.com/products/create/docs/generated/graphlab.SFrame.html
ankostis

«Данные в SFrame хранятся по столбцам на стороне сервера GraphLab» - это услуга или пакет?
Дэнни Ван

2

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

import pandas as pd

def chunck_generator(filename, header=False,chunk_size = 10 ** 5):
   for chunk in pd.read_csv(filename,delimiter=',', iterator=True, chunksize=chunk_size, parse_dates=[1] ): 
        yield (chunk)

def _generator( filename, header=False,chunk_size = 10 ** 5):
    chunk = chunck_generator(filename, header=False,chunk_size = 10 ** 5)
    for row in chunk:
        yield row

if __name__ == "__main__":
filename = r'file.csv'
        generator = generator(filename=filename)
        while True:
           print(next(generator))

1

Я хочу дать более полный ответ, основанный на большинстве потенциальных решений, которые уже предоставлены. Я также хочу указать на еще одну потенциальную помощь, которая может помочь процессу чтения.

Вариант 1: dtypes

«dtypes» - довольно мощный параметр, который вы можете использовать для уменьшения нагрузки на память readметодов. Смотрите это и этот ответ. Панды, по умолчанию, пытаются определить типы данных.

Обращаясь к структурам данных, для каждой сохраненной информации происходит распределение памяти. На базовом уровне обратитесь к значениям ниже (в таблице ниже приведены значения для языка программирования C):

The maximum value of UNSIGNED CHAR = 255                                    
The minimum value of SHORT INT = -32768                                     
The maximum value of SHORT INT = 32767                                      
The minimum value of INT = -2147483648                                      
The maximum value of INT = 2147483647                                       
The minimum value of CHAR = -128                                            
The maximum value of CHAR = 127                                             
The minimum value of LONG = -9223372036854775808                            
The maximum value of LONG = 9223372036854775807

Обратитесь к этой странице, чтобы увидеть соответствие между типами NumPy и C.

Допустим, у вас есть массив целых чисел . Вы можете как теоретически, так и практически назначить, скажем, массив 16-битного целочисленного типа, но тогда вы выделите больше памяти, чем вам нужно для хранения этого массива. Чтобы предотвратить это, вы можете установить dtypeопцию на read_csv. Вы не хотите хранить элементы массива в виде длинного целого числа, где на самом деле вы можете поместить их в 8-битное целое число ( np.int8или np.uint8).

Обратите внимание на следующую карту dtype.

Источник: https://pbpython.com/pandas_dtypes.html

Вы можете передать dtypeпараметр в качестве параметра в методах pandas как параметр readтипа {column: type}.

import numpy as np
import pandas as pd

df_dtype = {
        "column_1": int,
        "column_2": str,
        "column_3": np.int16,
        "column_4": np.uint8,
        ...
        "column_n": np.float32
}

df = pd.read_csv('path/to/file', dtype=df_dtype)

Вариант 2: Чтение чанками

Чтение данных по частям позволяет вам получить доступ к части данных в памяти, и вы можете применить предварительную обработку к вашим данным и сохранить обработанные данные, а не необработанные данные. Было бы намного лучше, если бы вы комбинировали эту опцию с первой, dtypes .

Я хочу указать разделы поваренной книги панд для этого процесса, где вы можете найти их здесь . Обратите внимание на эти два раздела там;

Вариант 3: Dask

Dask - это фреймворк, который определен на веб-сайте Dask как:

Dask обеспечивает расширенный параллелизм для аналитики, обеспечивая масштабируемость для любимых инструментов

Он был рожден, чтобы покрыть необходимые части, куда панды не могут добраться. Dask - это мощная инфраструктура, которая предоставляет вам гораздо больший доступ к данным за счет их распределенной обработки.

Вы можете использовать dask для предварительной обработки ваших данных в целом, Dask заботится о фрагментации, поэтому в отличие от pandas, вы можете просто определить этапы обработки и позволить Dask выполнять всю работу. Dask не применяет вычисления до тех пор, пока они явно не выдвинуты computeи / или persist( разницу см. В ответе здесь ).

Другие средства (идеи)

  • ETL поток предназначен для данных. Хранение только того, что необходимо из необработанных данных.
    • Сначала примените ETL ко всем данным с помощью таких платформ, как Dask или PySpark, и экспортируйте обработанные данные.
    • Затем посмотрите, могут ли обработанные данные быть помещены в память в целом.
  • Рассмотрите возможность увеличения вашей оперативной памяти.
  • Рассмотрите возможность работы с этими данными на облачной платформе.

0

В дополнение к ответам выше, для тех, кто хочет обработать CSV и затем экспортировать в csv, parquet или SQL, d6tstack является еще одним хорошим вариантом. Вы можете загрузить несколько файлов, и это имеет дело с изменениями схемы данных (добавлены / удалены столбцы). Поддержка Chunked из ядра уже встроена.

def apply(dfg):
    # do stuff
    return dfg

c = d6tstack.combine_csv.CombinerCSV([bigfile.csv], apply_after_read=apply, sep=',', chunksize=1e6)

# or
c = d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), apply_after_read=apply, chunksize=1e6)

# output to various formats, automatically chunked to reduce memory consumption
c.to_csv_combine(filename='out.csv')
c.to_parquet_combine(filename='out.pq')
c.to_psql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # fast for postgres
c.to_mysql_combine('mysql+mysqlconnector://usr:pwd@localhost/db', 'tablename') # fast for mysql
c.to_sql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # slow but flexible

0

В случае, если кто-то все еще ищет что-то подобное, я обнаружил, что эта новая библиотека под названием modin может помочь. Он использует распределенные вычисления, которые могут помочь с чтением. Вот хорошая статья, сравнивающая его функциональность с пандами. По сути, он использует те же функции, что и панды.

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)

Можете ли вы прокомментировать, как этот новый модуль modinсравнивается с устоявшимся dask.dataframe? Например, см. Переход от панд к даску, чтобы использовать все локальные ядра процессора .
JPP

0

Прежде чем использовать опцию chunksize, если вы хотите быть уверены в функции процесса, которую вы хотите записать внутри цикла for chunking, как упомянуто @unutbu, вы можете просто использовать опцию nrows.

small_df = pd.read_csv(filename, nrows=100)

Как только вы убедитесь, что блок процесса готов, вы можете поместить его в цикл chunking for для всего кадра данных.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.