Як я читаю великий файл 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


Відповіді:


263

Помилка показує, що пристрою не вистачає пам’яті, щоб одночасно прочитати весь CSV у DataFrame. Якщо припустити, що вам не потрібен весь набір даних у пам’яті за один раз, одним із способів уникнути проблеми було б обробляти CSV шматками (вказавши chunksizeпараметр):

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

chunksizeПараметр визначає кількість рядків на порцію. (Звичайно, останній фрагмент може містити менше chunksizeрядків.)


17
як правило, вам потрібно 2X кінцевої пам'яті, щоб прочитати щось (з CSV, хоча інші формати краще мати менші потреби в пам'яті). FYI, це справедливо для того, щоб спробувати зробити майже все, і відразу. Набагато краще відрізати його (що має постійне використання пам'яті).
Джефф

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

85

Ченкінг не завжди повинен бути першим портом виклику для цієї проблеми.

  1. Чи великий файл через неодноразові нечислові дані або небажані стовпці?

    Якщо так, то іноді ви можете бачити величезну економію пам’яті, читаючи в стовпцях як категорії та вибираючи необхідні стовпці за допомогою параметра pd.read_csv usecols .

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

    Якщо так, ви можете використовувати dask.dataframe, щоб нарізати, виконувати свої обчислення та експортувати ітеративно. Chunking виконується безшумно dask, який також підтримує підмножину API панди.

  3. Якщо все інше не вдається, читайте по черзі по шматках.

    Шматок через панду або через бібліотеку csv як крайній захід.


3
Я не знав Даска. +100 для цього!
noamtm

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)

22
Чи є причина , ви перейшли від read_csvдо read_table?
Підерман

33

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

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

Більше з документації ви можете прочитати тут .

Ще однією чудовою альтернативою було б використання modin, оскільки вся функціональність ідентична пандам, але вона використовується на розподілених бібліотеках фрейму даних, таких як dask.


11
Будь-які переваги в порівнянні з пандами, можемо оцінити додавання ще кількох покажчиків
PirateApp,

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

2
Дякую! є дак заміною пандам чи це працює поверх панд як шар
PirateApp

3
Ласкаво просимо, він працює як обгортка для Numpy, Pandas та Scikit-Learn.
Сімбараше Тимофій Моці

1
Я намагався зіткнутися з декількома проблемами з Dask і завжди кидає помилку на все. Навіть з шматками, це також кидає помилки пам'яті. Див stackoverflow.com/questions/59865572 / ...
Genarito

10

Наведена відповідь вже задовольняє тему. У будь-якому випадку, якщо вам потрібні всі дані в пам'яті - подивіться на bcolz . Його стискання даних в пам'яті. Я мав із цим справді хороший досвід. Але в ньому відсутня багато можливостей панди

Редагувати: я отримав коефіцієнт стиснення приблизно в 1/10 або розмір оригіналу, я думаю, звичайно залежно від виду даних. Важливими ознаками, які були відсутні, були агрегати.


2
Будь ласка, вдосконаліть цю відповідь, сказавши нам: а) які коефіцієнти стиснення ви отримуєте та б) які основні особливості панд йому відсутні? Чи може він поводитися з NA? струни? категоричні? дати?
smci

Так? Чи може він поводитися з NA? струни? категоричні? дати? Це те, що робить читання pandas csv повільним і в'ялим. NA та об'єкти, такі як рядки (навіть короткі), є вбивцею. Btw .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, який має той самий синтаксис, що і панди, але дозволяє вам маніпулювати файлами, які перевищують вашу ОЗП.


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

"Дані в SFrame зберігаються у стовпцях на сервері GraphLab" це послуга чи пакет?
Danny Wang

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" - досить потужний параметр, який можна використовувати для зниження тиску пам'яті 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).

Дотримуйтесь наступної мапи типу.

Джерело: https://pbpython.com/pandas_dtypes.html

Ви можете передавати dtypeпараметр як параметр методів pandas у вигляді dict на 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: Прочитайте Чанки

Читання даних по шматочках дозволяє отримати доступ до частини даних в пам'яті, і ви можете застосувати попередню обробку даних і зберегти оброблені дані, а не необроблені дані. Було б набагато краще, якщо ви поєднаєте цей варіант з першим, типом .

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

Варіант 3: Даск

Dask - це рамка, визначена на веб-сайті Dask як:

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

Він народився для покриття необхідних частин, куди панди не можуть дістатися. Dask - це потужна рамка, яка дозволяє набагато більше доступу до даних, обробляючи їх розподіленим способом.

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

Інші засоби (ідеї)

  • Потік ETL, розроблений для даних. Зберігати лише те, що потрібно від необроблених даних.
    • По-перше, застосуйте ETL до цілих даних із рамками, такими як Dask або PySpark, та експортуйте оброблені дані.
    • Потім подивіться, чи можуть оброблені дані вміститися в пам'яті в цілому.
  • Подумайте про збільшення оперативної пам’яті.
  • Розглянемо роботу з цими даними на хмарній платформі.

0

Окрім наведених вище відповідей, для тих, хто хоче обробити CSV, а потім експортувати в CSV, паркет або SQL, d6tstack - ще один хороший варіант. Ви можете завантажити декілька файлів, і це стосується змін схеми даних (додані / видалені стовпці). Зведена основна підтримка вже вбудована.

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, якщо ви хочете бути впевнені у функції процесу, яку ви хочете записати всередині циклу chunking for-loop, як згадував @unutbu, ви можете просто скористатися параметром nrows.

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

Після того, як ви впевнені, що блок процесу готовий, ви можете помістити це в фрагмент циклу для всього фрейму даних.

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