Bagaimana cara membaca file csv besar dengan panda?

194

Saya mencoba membaca file csv besar (aprox. 6 GB) dalam panda dan saya mendapatkan kesalahan memori:

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

...

MemoryError: 

Ada bantuan untuk ini?

Rajkumar Kumawat
sumber
3
Anehnya, pertanyaan yang sangat mirip ditanyakan hampir setahun sebelum ini ...
DarkCygnus
Kemungkinan rangkap dari Membaca file teks besar dengan Panda
unode
Apakah ini menjawab pertanyaan Anda? Pekerjaan "Data besar" mengalir menggunakan panda
AMC

Jawaban:

261

Kesalahan menunjukkan bahwa mesin tidak memiliki cukup memori untuk membaca seluruh CSV menjadi DataFrame sekaligus. Dengan asumsi Anda tidak memerlukan seluruh dataset dalam memori sekaligus, satu cara untuk menghindari masalah adalah dengan memproses CSV dalam potongan (dengan menentukan chunksizeparameter):

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

The chunksizeparameter menentukan jumlah baris per sepotong. (Potongan terakhir mungkin berisi kurang dari chunksizebaris, tentu saja.)

unutbu
sumber
17
Anda biasanya membutuhkan 2X memori akhir untuk membaca sesuatu (dari csv, meskipun format lain lebih baik karena memiliki persyaratan memori yang lebih rendah). FYI ini berlaku untuk mencoba melakukan hampir semua hal sekaligus. Jauh lebih baik untuk memotongnya (yang memiliki penggunaan memori konstan).
Jeff
24
@altabq: Masalahnya di sini adalah kita tidak memiliki cukup memori untuk membangun satu DataFrame yang menyimpan semua data. Solusi di atas mencoba untuk mengatasi situasi ini dengan mengurangi potongan (misalnya dengan menggabungkan atau mengekstraksi informasi yang diinginkan) satu potong pada satu waktu - sehingga menghemat memori. Apa pun yang Anda lakukan, JANGAN panggil DF.append(chunk)ke dalam lingkaran. Itu akan menggunakan O(N^2)operasi penyalinan. Lebih baik menambahkan data yang dikumpulkan ke daftar , dan kemudian membangun DataFrame dari daftar dengan satu panggilan ke pd.DataFrameatau pd.concat(tergantung pada jenis data yang dikumpulkan).
unutbu
12
@altabq: Memanggil DF.append(chunk)dalam satu lingkaran membutuhkan O(N^2)operasi penyalinan Ndengan ukuran potongan, karena setiap panggilan untuk DF.appendmengembalikan DataFrame baru. Memanggil pd.DataFrameatau pd.concat sekali di luar loop mengurangi jumlah penyalinan O(N).
unutbu
5
@Pyderman: Ya, chunksizeparameter mengacu pada jumlah baris per chunk. Potongan terakhir mungkin mengandung kurang dari chunksizebaris, tentu saja.
unutbu
7
@Pyderman: Ya; menelepon pd.concat([list_of_dfs]) sekali setelah loop jauh lebih cepat daripada menelepon pd.concatatau df.appendberkali-kali dalam loop. Tentu saja, Anda akan membutuhkan sejumlah besar memori untuk menahan seluruh csv 6GB sebagai satu DataFrame.
unutbu
85

Chunking seharusnya tidak selalu menjadi port of call pertama untuk masalah ini.

  1. Apakah file tersebut besar karena pengulangan data non-numerik atau kolom yang tidak diinginkan?

    Jika demikian, Anda kadang-kadang dapat melihat penghematan memori besar-besaran dengan membaca kolom sebagai kategori dan memilih kolom yang diperlukan melalui parameter pd.read_csv usecols .

  2. Apakah alur kerja Anda memerlukan pengirisan, manipulasi, ekspor?

    Jika demikian, Anda dapat menggunakan dask.dataframe untuk mengiris, melakukan perhitungan dan mengekspornya secara berulang. Pemotongan dilakukan secara diam-diam oleh dask, yang juga mendukung subset API panda.

  3. Jika semuanya gagal, baca baris demi baris melalui potongan.

    Potongan melalui panda atau melalui perpustakaan csv sebagai pilihan terakhir.

jpp
sumber
3
Saya tidak mengetahui Dask. +100 untuk itu!
noamtm
34

Saya melanjutkan seperti ini:

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)
Rajkumar Kumawat
sumber
22
Apakah ada alasan Anda beralih dari read_csvke read_table?
Pyderman
33

Untuk data besar saya sarankan Anda menggunakan pustaka "dask"
misalnya:

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

Anda dapat membaca lebih lanjut dari dokumentasi di sini .

Alternatif hebat lainnya adalah menggunakan modin karena semua fungsionalitasnya identik dengan panda, tetapi memanfaatkan pada pustaka dataframe terdistribusi seperti dask.

Simbarashe Timothy Motsi
sumber
11
Setiap manfaat lebih dari panda, bisa menghargai menambahkan beberapa petunjuk lagi
PirateApp
2
Saya belum pernah menggunakan Dask untuk waktu yang lama tetapi keuntungan utama dalam kasus penggunaan saya adalah bahwa Dask dapat berjalan paralel pada beberapa mesin, juga dapat memuat data sebagai irisan ke dalam memori.
Simbarashe Timothy Motsi
2
Terima kasih! adalah dask pengganti panda atau berfungsi di atas panda sebagai layer
PirateApp
3
Selamat datang, ini berfungsi sebagai pembungkus untuk Numpy, Panda, dan Scikit-Learn.
Simbarashe Timothy Motsi
1
Saya sudah mencoba menghadapi beberapa masalah dengan Dask dan selalu melakukan kesalahan untuk semuanya. Bahkan dengan potongan itu melempar kesalahan Memori juga. Lihat stackoverflow.com/questions/59865572/…
Genarito
10

Jawaban di atas sudah memuaskan topik. Bagaimanapun, jika Anda membutuhkan semua data dalam memori - lihat bcolz . Ini mengompresi data dalam memori. Saya memiliki pengalaman yang sangat bagus dengannya. Tetapi banyak fitur panda yang hilang

Sunting: Saya mendapatkan tingkat kompresi sekitar 1/10 atau ukuran orig saya pikir, tentu saja tergantung pada jenis data. Fitur penting yang hilang adalah agregat.

PlagTag
sumber
2
Harap tingkatkan jawaban ini dengan memberi tahu kami a) rasio kompresi apa yang Anda dapatkan dan b) fitur utama apa dari panda yang hilang? Bisakah ia menangani NAS? string? kategorikal? tanggal?
smci
Hah? Bisakah ia menangani NAS? string? kategorikal? tanggal? Ini adalah hal-hal yang membuat panda csv membaca lambat dan lembek. NAS dan objek seperti string (bahkan yang pendek) adalah pembunuh. Namun, .ipynb yang dirujuk dari blog Anda tidak aktif.
smci
1
@smci saya sedang membaca catatan Anda. tapi saya sarankan Anda melihat dokumen. Saya perlu membacanya sendiri.
PlagTag
2
Ok jadi itu tidak bisa menangani As, string, atau tanggal. Saya ragu itu bisa menangani pelampung juga.
smci
1
Saya kira Anda bisa memproses dengan panda menggunakan chunksmetode yang disebutkan, kemudian menggunakan bcolz jika Anda membutuhkan semua data dalam memori untuk melakukan analisis. Hanya pemikiran saja.
JakeCowton
6

Anda dapat membaca data sebagai potongan dan menyimpan setiap potongan sebagai acar.

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)

Pada langkah selanjutnya Anda membaca di acar dan menambahkan setiap acar ke bingkai data yang Anda inginkan.

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)
Lukas Humpe
sumber
3
Jika akhir Anda dfsepenuhnya sesuai dengan memori (seperti yang tersirat) dan berisi jumlah data yang sama dengan input Anda, tentunya Anda tidak perlu memotong sama sekali?
jpp
Anda harus memotong dalam kasus ini jika, misalnya, file Anda sangat lebar (seperti lebih dari 100 kolom dengan banyak kolom string). Ini meningkatkan memori yang diperlukan untuk menahan df dalam memori. Bahkan file 4GB seperti ini dapat berakhir dengan menggunakan antara 20 dan 30 GB RAM pada sebuah kotak dengan 64 GB RAM.
cdabel
4

Fungsi read_csv dan read_table hampir sama. Tetapi Anda harus menetapkan pembatas “,” ketika Anda menggunakan fungsi read_table di program Anda.

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)
Tyrion W
sumber
Akan membantu jika menyatakan apa pertanyaan Anda di pos ini. Seperti "Apa perbedaan antara read_csv dan read_table?" atau "Mengapa tabel baca memerlukan pembatas?"
nate_weldon
1
Tergantung bagaimana file Anda terlihat. Beberapa file memiliki pembatas umum seperti "," atau "|" atau "\ t" tetapi Anda mungkin melihat file lain dengan pembatas seperti 0x01, 0x02 (membuat yang ini) dll. Jadi read_table lebih cocok untuk pembatas yang tidak biasa tetapi read_csv dapat melakukan pekerjaan yang sama sama baiknya.
Naufal
3

Solusi 1:

Menggunakan panda dengan data besar

Solusi 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)
kambing hitam
sumber
3
Di sini sekali lagi kita memuat file 6 GB total ke memori, Apakah ada opsi, kita dapat memproses potongan saat ini dan kemudian membaca potongan berikutnya
debaonline4u
6
jangan lakukan dfList.append, hanya memproses setiap chunk ( df) secara terpisah
gokul_uf
3

Berikut ini contohnya:

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)
Jonathask
sumber
2

Anda dapat mencoba sframe, yang memiliki sintaksis yang sama dengan panda tetapi memungkinkan Anda untuk memanipulasi file yang lebih besar dari RAM Anda.

nunodsousa
sumber
"Data dalam SFrame disimpan secara kolom pada sisi Server GraphLab" apakah ini layanan atau paket?
Danny Wang
2

Jika Anda menggunakan panda membaca file besar ke dalam chunk dan kemudian menghasilkan baris demi baris, inilah yang telah saya lakukan

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))
paulg
sumber
1

Saya ingin membuat jawaban yang lebih komprehensif berdasarkan sebagian besar solusi potensial yang sudah disediakan. Saya juga ingin menunjukkan satu lagi bantuan potensial yang dapat membantu proses membaca.

Opsi 1: dtypes

"Dtypes" adalah parameter yang cukup kuat yang dapat Anda gunakan untuk mengurangi tekanan memori readmetode. Lihat ini dan ini jawaban . Panda, secara default, mencoba menyimpulkan dtipe data.

Mengacu pada struktur data, setiap data yang disimpan, alokasi memori dilakukan. Pada tingkat dasar lihat nilai-nilai di bawah ini (Tabel di bawah ini menggambarkan nilai-nilai untuk bahasa pemrograman 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

Lihat ini halaman untuk melihat kecocokan antara tipe NumPy dan C.

Katakanlah Anda memiliki array bilangan bulat digit . Anda dapat menetapkan secara teoritis dan praktis, katakanlah array dengan tipe integer 16-bit, tetapi Anda kemudian akan mengalokasikan lebih banyak memori daripada yang sebenarnya Anda perlukan untuk menyimpan array itu. Untuk mencegah hal ini, Anda dapat mengatur dtypeopsi pada read_csv. Anda tidak ingin menyimpan item array sebagai integer panjang di mana sebenarnya Anda bisa memasangnya dengan integer 8-bit ( np.int8ataunp.uint8 ).

Amati dtype peta berikut.

Sumber: https://pbpython.com/pandas_dtypes.html

Anda dapat melewatkan dtypeparameter sebagai parameter pada metode panda seperti dict pada readlike {kolom: 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)

Opsi 2: Baca oleh Potongan-potongan

Membaca data dalam potongan memungkinkan Anda untuk mengakses bagian dari data dalam memori, dan Anda dapat menerapkan preprocessing pada data Anda dan menyimpan data yang diproses daripada data mentah. Akan jauh lebih baik jika Anda menggabungkan opsi ini dengan yang pertama, dtypes .

Saya ingin menunjukkan bagian buku masak panda untuk proses itu, di mana Anda dapat menemukannya di sini . Perhatikan dua bagian di sana;

Opsi 3: Dask

Dask adalah kerangka kerja yang didefinisikan dalam situs web Dask sebagai:

Dask menyediakan paralelisme canggih untuk analitik, memungkinkan kinerja pada skala untuk alat yang Anda sukai

Ia dilahirkan untuk menutupi bagian-bagian penting yang tidak dapat dijangkau oleh panda. Dask adalah kerangka kerja yang kuat yang memungkinkan Anda lebih banyak mengakses data dengan memprosesnya secara terdistribusi.

Anda dapat menggunakan dask untuk memproses data Anda secara keseluruhan, Dask menangani bagian chunking, jadi tidak seperti panda, Anda bisa menentukan langkah-langkah pemrosesan Anda dan membiarkan Dask melakukan pekerjaannya. Dask tidak menerapkan perhitungan sebelum secara eksplisit didorong oleh computedan / atau persist(lihat jawabannya di sini untuk perbedaannya).

Bantuan Lainnya (Gagasan)

  • Aliran ETL dirancang untuk data. Menyimpan hanya apa yang dibutuhkan dari data mentah.
    • Pertama, terapkan ETL ke seluruh data dengan kerangka kerja seperti Dask atau PySpark, dan ekspor data yang diproses.
    • Kemudian lihat apakah data yang diproses bisa masuk dalam memori secara keseluruhan.
  • Pertimbangkan meningkatkan RAM Anda.
  • Pertimbangkan untuk bekerja dengan data itu di platform cloud.
batal
sumber
0

Selain jawaban di atas, bagi mereka yang ingin memproses CSV dan kemudian mengekspor ke csv, parket atau SQL, d6tstack adalah pilihan bagus lainnya. Anda dapat memuat banyak file dan berkaitan dengan perubahan skema data (kolom tambah / dihapus). Chunked out of core support sudah ada di dalamnya.

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
citynorman
sumber
0

Jika seseorang masih mencari sesuatu seperti ini, saya menemukan bahwa perpustakaan baru ini yang disebut modin dapat membantu. Ini menggunakan komputasi terdistribusi yang dapat membantu membaca. Inilah artikel yang bagus membandingkan fungsinya dengan panda. Ini pada dasarnya menggunakan fungsi yang sama dengan panda.

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)
Jaskaran
sumber
Bisakah Anda mengomentari bagaimana modul baru ini modindibandingkan dengan yang sudah mapan dask.dataframe? Sebagai contoh, lihat berpindah dari panda ke dask untuk memanfaatkan semua core cpu lokal .
jpp
0

Sebelum menggunakan opsi chunksize jika Anda ingin memastikan tentang fungsi proses yang ingin Anda tulis di dalam chunking for-loop seperti yang disebutkan oleh @unutbu, Anda cukup menggunakan opsi nrows.

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

Setelah Anda yakin bahwa blok proses sudah siap, Anda bisa meletakkannya di chunking for loop untuk seluruh kerangka data.

sam
sumber