Pandalı büyük bir csv dosyasını nasıl okurum?


194

Pandalar büyük bir csv dosyasını (yaklaşık 6 GB) okumaya çalışıyorum ve bir bellek hatası alıyorum:

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

...

MemoryError: 

Bu konuda herhangi bir yardım?


3
Merakla, bundan hemen hemen bir yıl önce çok benzer bir soru soruldu ...
DarkCygnus


Yanıtlar:


263

Hata, makinenin tüm CSV'yi bir seferde bir DataFrame'e okumak için yeterli belleğe sahip olmadığını gösterir. Bellekteki tüm veri kümesine tek seferde ihtiyacınız olmadığını varsayarsak, sorunu önlemenin bir yolu CSV'yi yığınlar halinde işlemek olacaktır ( chunksizeparametreyi belirterek ):

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

chunksizeParametre yığın başına satır sayısını belirtir. (Son yığın chunksize, elbette satırdan daha az içerebilir .)


17
bir şeyi okumak için genellikle 2X son belleğe ihtiyacınız vardır (csv'den, ancak diğer formatlar daha düşük bellek gereksinimlerine sahip olmak daha iyidir). Bilginize bu hemen hemen her şeyi aynı anda yapmaya çalışmak için geçerlidir. Yığmak için çok daha iyi (sürekli bir bellek kullanımı var).
Jeff

24
@altabq: Buradaki sorun, tüm verileri tutan tek bir DataFrame oluşturmak için yeterli belleğimizin olmaması. Yukarıdaki çözüm, her seferinde bir parçayı azaltarak (örn. Sadece istenen bilgileri toplayarak veya çıkararak) bu durumla başa çıkmaya çalışır - böylece bellek tasarrufu sağlar. Ne yaparsanız yapın DF.append(chunk), döngü içinde aramayın . Bu, O(N^2)kopyalama işlemlerini kullanacaktır . Toplanan verileri bir listeye eklemek ve daha sonra (birleştirilmiş veri türüne bağlı olarak) veya bir çağrı ile DataFrame'i listeden oluşturmak daha iyidir . pd.DataFramepd.concat
unutbu

12
@altabq: DF.append(chunk)Bir döngüde çağrı yapmak, parçaların boyutu olan O(N^2)kopyalama işlemlerini gerektirir N, çünkü her çağrı DF.appendyeni bir DataFrame döndürür. Arayan pd.DataFrameya pd.concat bir kez döngü dışında kopyalama miktarını azaltır O(N).
unutbu

5
@Pyderman: Evet, chunksizeparametre yığın başına satır sayısını ifade eder. Son yığın chunksize, elbette satırlardan daha az içerebilir .
unutbu

7
@Pyderman: Evet; döngüden sonra bir pd.concat([list_of_dfs]) kez çağrı , çağrıdan çok daha hızlı pd.concatveya df.appenddöngü içinde birçok kez. Tabii ki, 6GB csv'nin tamamını bir DataFrame olarak tutmak için hatırı sayılır miktarda belleğe ihtiyacınız olacak.
unutbu

85

Chunking her zaman bu sorun için ilk çağrı noktası olmamalıdır.

  1. Sayısal olmayan veriler veya istenmeyen sütunlar nedeniyle dosya büyük mü?

    Öyleyse, bazen sütunları kategoriler olarak okuyarak ve pd.read_csv usecols parametresi aracılığıyla gerekli sütunları seçerek büyük bellek tasarrufları görebilirsiniz .

  2. İş akışınız dilimleme, değiştirme, dışa aktarma gerektiriyor mu?

    Öyleyse, dask.dataframe'i dilimlemek, hesaplamalarınızı yapmak ve tekrarlı olarak dışa aktarmak için kullanabilirsiniz. Parçalama, panda API'sinin bir alt kümesini de destekleyen dask tarafından sessizce gerçekleştirilir.

  3. Her şey başarısız olursa, parçalar aracılığıyla satır satır okuyun.

    Dobiş pandalar aracılığıyla veya üzeri csv kütüphanede son çare olarak.


3
Dask'ın farkında değildim. Bunun için +100!
noamtm

34

Ben böyle ilerledim:

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
Eğer gelen anahtarlamalı bir nedeni var mı read_csvhiç read_table?
Pyderman

33

Büyük veri l için "dask" kütüphanesini kullanmanızı öneririm
:

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

Buradaki belgelerden daha fazla bilgi edinebilirsiniz .

Diğer bir harika alternatif modin kullanmak olacaktır, çünkü tüm işlevsellik pandalarla aynıdır, ancak dask gibi dağıtılmış veri çerçevesi kitaplıklarından yararlanır.


11
Pandalar üzerinde herhangi bir fayda, birkaç işaretçi eklemeyi takdir edebilir
PirateApp

2
Dask'ı çok uzun süre kullanmadım, ancak kullanım durumlarımın ana avantajları Dask'ın birden fazla makinede paralel çalışabilmesi, aynı zamanda verileri belleğe dilimler halinde sığdırmasıydı.
Simbarashe Timothy Motsi

2
Teşekkürler! pandalar yerine bir dask mı yoksa bir katman olarak pandaların üstünde çalışıyor mu
PirateApp

3
Hoş geldiniz, Numpy, Pandalar ve Scikit-Learn için bir sarıcı olarak çalışıyor.
Simbarashe Timothy Motsi

1
Dask ile birkaç sorunla karşılaşmaya çalıştım ve her zaman her şey için bir hata atıyor. Parçalarla bile Bellek hatalarını da atar. Bkz. Stackoverflow.com/questions/59865572/…
Genarito

10

Yukarıdaki cevap zaten konuyu tatmin ediyor. Her neyse, bellekteki tüm verilere ihtiyacınız varsa - bcolz'a bir göz atın . Hafızadaki verileri sıkıştırıyor. Onunla gerçekten iyi bir deneyim yaşadım. Ama birçok panda özelliği eksik

Edit: Tabii ki veri türüne bağlı olarak, bence yaklaşık 1/10 veya orig boyutu sıkıştırma oranları var. Eksik olan önemli özellikler kümelerdi.


2
Lütfen bu cevabı bize a) hangi sıkıştırma oranlarını aldığınızı ve b) pandaların hangi ana özelliklerinin eksik olduğunu söyleyerek geliştirin? NA'larla başa çıkabilir mi? Teller? categoricals? tarih?
smci

Ha? NA'larla başa çıkabilir mi? Teller? categoricals? tarih? Bunlar panda csv okumayı yavaş ve gevşek yapan şeyler. NA'lar ve dizeler gibi nesneler (kısa olanlar bile) bir katildir. Btw blogunuzda başvurulan .ipynb kapalı.
smci

1
@ smci sana not okuyordum. ama dokümanlara bir göz atmanızı öneririm. onları kendim okumam gerekecekti.
PlagTag

2
Tamam, NA'ları, dizeleri veya tarihleri ​​işleyemez. Şamandıralarla da baş edebileceğinden şüpheliyim.
smci

1
chunksBahsedilen yöntemi kullanarak pandalarla önişleme yapabileceğinizi varsayalım , sonra analiz yapmak için bellekteki tüm verilere ihtiyacınız varsa bcolz kullanın. Sadece bir düşünce.
JakeCowton

6

Verileri parçalar halinde okuyabilir ve her bir parçayı turşu olarak kaydedebilirsiniz.

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)

Bir sonraki adımda turşuları okuyorsunuz ve her turşuyu istediğiniz veri çerçevesine ekliyorsunuz.

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
Nihai Eğer dfuyuyor tamamen bellekte (zımni olarak) ve girdi olarak aynı miktarda veri içeriyor, elbette hiç yığın gerekmez?
jpp

Örneğin, dosyanız çok genişse (çok sayıda dize sütunu olan 100'den fazla sütun gibi) bu durumda yığın oluşturmanız gerekir. Bu, df'yi bellekte tutmak için gereken belleği artırır. Böyle bir 4GB dosya bile 64 GB RAM içeren bir kutuda 20 ila 30 GB RAM kullanabilir.
cdabel

4

Read_csv ve read_table işlevi neredeyse aynıdır. Ancak programınızda read_table işlevini kullandığınızda ayırıcıyı “,” olarak atamanız gerekir.

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)

Sorunuzun bu yayında ne olduğunu belirtmeniz yardımcı olacaktır. "Read_csv ve read_table arasındaki fark nedir?" veya "Neden okuma tablosunun bir sınırlayıcıya ihtiyacı var?"
nate_weldon

1
Dosyanızın nasıl göründüğüne bağlıdır. Bazı dosyalar "," veya "|" gibi ortak sınırlayıcılara sahiptir. veya "\ t" olabilir, ancak 0x01, 0x02 (bunu oluşturur) vb.
Naufal

3

Çözüm 1:

Büyük verilere sahip pandaları kullanma

Çözüm 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
Burada yine 6 GB dosyasını tamamen belleğe yüklüyoruz, Herhangi bir seçenek var mı, mevcut parçayı işleyebilir ve bir sonraki parçayı okuyabiliriz
debaonline4u

6
sadece yapmayın dfList.append, her parçayı ( df) ayrı ayrı
işleyin

3

Aşağıda bir örnek verilmiştir:

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

Pandaları büyük dosyayı parçaya okuyup satır satır verdiyseniz, işte yaptığım şey

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

Zaten sağlanan potansiyel çözümlerin çoğunu temel alarak daha kapsamlı bir cevap vermek istiyorum. Ayrıca okuma sürecine yardımcı olabilecek bir potansiyel yardım daha belirtmek istiyorum.

1.Seçenek: Türler

"dtypes" readyöntemlerin bellek basıncını düşürmek için kullanabileceğiniz oldukça güçlü bir parametredir . Bkz bu ve bu cevap. Pandalar varsayılan olarak verilerin türlerini çıkarmaya çalışırlar.

Veri yapılarına atıfta bulunulan her veri, bir bellek tahsisi gerçekleşir. Temel düzeyde aşağıdaki değerlere bakın (Aşağıdaki tabloda C programlama dili değerleri gösterilmektedir):

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 ve C tipleri arasındaki eşleşmeyi görmek için bu sayfaya bakın .

Let Diyelim ki bir tamsayı dizisi olduğunu varsayalım basamak . Teorik ve pratik olarak 16 bit tamsayı türü dizisi atayabilirsiniz, ancak daha sonra bu diziyi depolamanızdan daha fazla bellek ayırabilirsiniz. Bunu önlemek için dtypeseçeneği açık olarak ayarlayabilirsiniz read_csv. Dizi öğelerini gerçekte 8 bit tamsayı ( np.int8veya np.uint8) ile sığdırabileceğiniz uzun tamsayı olarak saklamak istemezsiniz .

Aşağıdaki dtype haritasını inceleyin.

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

dtypeParametreyi panda yöntemlerinde parametre olarak read{column: type} gibi belirtildiği gibi geçirebilirsiniz .

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)

Seçenek 2: Parçalar tarafından okuyun

Verileri parçalar halinde okumak, bellekteki verilerin bir kısmına erişmenizi sağlar ve verilerinize önişleme uygulayabilir ve ham veriler yerine işlenmiş verileri koruyabilirsiniz. Bu seçeneği birincisi ile birleştirirseniz çok daha iyi olur, dtypes .

Burada bulabileceğiniz panda yemek kitabı bölümlerine dikkat çekmek istiyorum . Bu iki bölüme dikkat edin;

Seçenek 3: Dask

Dask, Dask'ın web sitesinde şu şekilde tanımlanan bir çerçevedir :

Dask, sevdiğiniz araçlar için ölçekte performans sağlayan analitik için gelişmiş paralellik sağlar

Pandaların ulaşamayacağı gerekli parçaları kapsayacak şekilde doğdu. Dask, dağıtılmış bir şekilde işleyerek çok daha fazla veri erişimine izin veren güçlü bir çerçevedir.

Verilerinizi bir bütün olarak önişlemek için dask'ı kullanabilirsiniz, Dask parçalama kısmına bakar, bu nedenle pandaların aksine sadece işlem adımlarınızı tanımlayabilir ve Dask'ın işi yapmasına izin verebilirsiniz. Açıkça tarafından itilir önce Dask hesaplamaları geçerli değildir computeve / veya persist(cevaba bakınız burada farkı).

Diğer Yardımlar (Fikirler)

  • Veriler için tasarlanmış ETL akışı. Ham verilerden sadece gerekli olanı tutmak.
    • İlk olarak, Dast veya PySpark gibi çerçevelerle tüm verilere ETL uygulayın ve işlenen verileri dışa aktarın.
    • Ardından, işlenen verilerin bir bütün olarak belleğe sığabileceğine bakın.
  • RAM'inizi artırmayı düşünün.
  • Bu verilerle bir bulut platformunda çalışmayı düşünün.

0

Yukarıdaki cevaplara ek olarak, CSV'yi işlemek ve sonra csv, parke veya SQL'e ihracat yapmak isteyenler için d6tstack başka bir iyi seçenektir. Birden fazla dosya yükleyebilirsiniz ve veri şeması değişiklikleri (eklenen / kaldırılan sütunlar) ile ilgilenir. Çekirdek desteğinden parçalanmış zaten yerleşik.

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

Birisi hala böyle bir şey arıyorsa, modin adlı bu yeni kütüphanenin yardımcı olabileceğini buldum . Okumaya yardımcı olabilecek dağıtılmış bilgi işlem kullanır. İşte işlevselliğini pandalarla karşılaştıran güzel bir makale . Esasen pandalarla aynı işlevleri kullanır.

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

Bu yeni modülün modinköklü ile nasıl karşılaştırıldığı hakkında yorum yapabilir misiniz dask.dataframe? Örneğin, tüm yerel işlemci çekirdeklerini kullanmak için pandalardan dask'a geçiş konusuna bakın .
jpp

0

Chunksize seçeneğini kullanmadan önce, @unutbu tarafından belirtildiği gibi, parçalama for döngüsü içine yazmak istediğiniz işlem işlevinden emin olmak istiyorsanız, nrows seçeneğini kullanabilirsiniz.

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

İşlem bloğunun hazır olduğundan emin olduktan sonra, bunu tüm veri çerçevesi için parçalama döngüsüne koyabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.