팬더로 큰 CSV 파일을 어떻게 읽습니까?


194

팬더에서 큰 CSV 파일 (약 6GB)을 읽으려고하는데 메모리 오류가 발생합니다.

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

...

MemoryError: 

이것에 대한 도움이 필요하십니까?


3
흥미롭게 도이 질문 이 있기 거의 1 년 전에 비슷한 질문제기 되었습니다.
DarkCygnus


이것이 귀하의 질문에 대답합니까? 팬더를 사용한 "대규모 데이터"작업 흐름
AMC

답변:


263

이 오류는 머신에 한 번에 전체 CSV를 DataFrame으로 읽을 수있는 메모리가 부족함을 나타냅니다. 한 번에 메모리에 전체 데이터 세트가 필요하지 않다고 가정하면 문제를 피하는 한 가지 방법 은 매개 변수 를 지정하여 CSV를 청크처리하는 것입니다 chunksize.

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

chunksize매개 변수는 청크 당 행 수를 지정합니다. (마지막 청크는 chunksize행 보다 적을 수 있습니다 .)


17
일반적으로 무언가를 읽으려면 최종 메모리의 2 배가 필요합니다 (CSV에서 다른 형식이 메모리 요구 사항이 적을수록 좋습니다). 참고로 거의 모든 작업을 한 번에 수행하려는 경우에 해당됩니다. 그것을 청크하는 것이 훨씬 좋습니다 (일관된 메모리 사용량이 있음).
Jeff

24
@ altabq : 여기서 문제는 모든 데이터를 보유하는 단일 DataFrame을 빌드 할 메모리가 충분하지 않다는 것입니다. 위의 솔루션은 한 번에 한 청크를 청크를 줄임으로써 (예를 들어 원하는 정보 만 집계하거나 추출하여) 메모리를 절약함으로써 이러한 상황에 대처하려고합니다. 무엇을 하든지 DF.append(chunk)루프 내부를 호출하지 마십시오 . O(N^2)복사 작업 이 사용 됩니다. 집계 된 데이터 를 list 에 추가 한 다음 또는 집계 된 데이터 유형에 따라 한 번의 호출 로 목록에서 DataFrame을 작성하는 것이 좋습니다 . pd.DataFramepd.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루프 내에서 여러 번 호출하는 것이 훨씬 빠릅니다 . 물론 전체 6GB CSV를 하나의 DataFrame으로 유지하려면 상당한 양의 메모리가 필요합니다.
unutbu

85

청킹이 항상이 문제의 첫 번째 포트가되는 것은 아닙니다.

  1. 숫자가 아닌 데이터가 반복되거나 원치 않는 열로 인해 파일이 커 집니까?

    그렇다면 때때로 열을 범주로 읽고 pd.read_csv usecols 매개 변수 를 통해 필요한 열을 선택하여 메모리를 크게 절약 할 수 있습니다 .

  2. 워크 플로우에 슬라이싱, 조작, 내보내기가 필요합니까?

    그렇다면 dask.dataframe 을 사용 하여 슬라이스하고 계산을 수행하고 반복적으로 내보낼 수 있습니다. 청킹은 dask에 의해 자동으로 수행되며 팬더 API의 서브 세트도 지원합니다.

  3. 다른 모든 방법이 실패하면 청크를 통해 한 줄씩 읽으십시오.

    최후의 수단으로 팬더 또는 csv 라이브러리 를 통한 청크 .


3
나는 Dask를 몰랐다. 그것을 위해 +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_csvread_table있습니까?
Pyderman

33

큰 데이터의 경우 다음과 같은 라이브러리 "dask"를 사용하는 것이 좋습니다
.

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

자세한 내용은 여기를 참조하십시오 .

또 다른 훌륭한 대안은 모든 기능이 팬더와 동일하지만 dask와 같은 분산 데이터 프레임 라이브러리를 활용하기 때문에 modin 을 사용 하는 것입니다.


11
팬더에 비해 어떤 이점이
있습니까

2
Dask를 오랫동안 사용하지는 않았지만 사용 사례의 주요 장점은 Dask가 여러 컴퓨터에서 병렬로 실행될 수 있다는 것입니다. 또한 데이터를 슬라이스로 메모리에 넣을 수도 있습니다.
Simbarashe Timothy Motsi

2
감사! 는 팬더를 대체하고 있거나 팬더 위에서 레이어로 작동
합니까?

3
Numpy, Pandas 및 Scikit-Learn의 래퍼로 작동합니다.
Simbarashe Timothy Motsi

1
Dask와 관련하여 몇 가지 문제에 직면했지만 항상 모든 것에 오류가 발생합니다. 청크에서도 메모리 오류가 발생합니다. 참조 stackoverflow.com/questions/59865572/...
Genarito

10

위의 답변은 이미 주제를 만족시키고 있습니다. 어쨌든 메모리에 모든 데이터가 필요한 경우 bcolz를 살펴보십시오. . 메모리의 데이터를 압축합니다. 나는 그것에 대해 정말 좋은 경험을했습니다. 그러나 팬더 기능이 많이 없습니다.

편집 : 물론 데이터의 종류에 따라 약 1/10 또는 orig 크기의 압축률을 얻었습니다. 누락 된 중요한 기능은 집계였습니다.


2
a) 어떤 압축비와 b) 팬더의 어떤 주요 기능이 없는지 알려 주시면이 답변을 개선하십시오. NA를 처리 할 수 ​​있습니까? 문자열? 범주 형? 데이트?
smci

응? NA를 처리 할 수 ​​있습니까? 문자열? 범주 형? 데이트? 이것들은 팬더 CSV가 느리고 연약한 독서를하는 것들입니다. 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를 메모리에 저장하는 데 필요한 메모리가 증가합니다. 이와 같은 4GB 파일조차도 64GB RAM이있는 상자에서 20 ~ 30GB의 RAM을 사용할 수 있습니다.
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는 동일한 작업을 수행 할 수 있습니다.
Naufal

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

팬더를 사용하면 큰 파일을 덩어리로 읽은 다음 한 행씩 생성하면 여기에 내가 한 일이 있습니다.

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메소드 의 메모리 압력을 줄이는 데 사용할 수있는 매우 강력한 매개 변수입니다 . 참조 대답을. 팬더는 기본적으로 데이터의 dtype을 유추하려고합니다.

데이터 구조를 참조하면, 저장된 모든 데이터는 메모리 할당이 발생합니다. 기본 수준에서 아래 값을 참조하십시오 (아래 표는 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옵션을 on으로 설정할 수 있습니다 read_csv. 배열 항목을 실제로는 8 비트 정수 ( np.int8또는np.uint8 )로 .

다음 dtype 맵을 확인하십시오.

출처 : https://pbpython.com/pandas_dtypes.html

{column : type}과 같은 dtypepandas 메소드에서 매개 변수로 매개 변수를 전달할 수 있습니다 read.

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는 청킹 부분을 처리하므로 팬더와 달리 처리 단계를 정의하고 Dask가 작업을 수행하도록 할 수 있습니다. Dask는 compute및 / 또는 명시 적으로 푸시하기 전에 계산을 적용하지 않습니다 persist( 차이는 여기 의 답변 참조 ).

기타 보조기구 (아이디어)

  • 데이터를 위해 설계된 ETL 흐름. 원시 데이터에서 필요한 것만 유지합니다.
    • 먼저 Dask 또는 PySpark와 같은 프레임 워크를 사용하여 전체 데이터에 ETL을 적용하고 처리 된 데이터를 내 보냅니다.
    • 그런 다음 처리 된 데이터가 전체 메모리에 맞는지 확인하십시오.
  • RAM을 늘리십시오.
  • 클라우드 플랫폼에서 해당 데이터 작업을 고려하십시오.

0

위의 답변 외에도 CSV를 처리 한 다음 csv, parquet 또는 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

누군가가 여전히 이런 것을 찾고 있다면 모딘 이라는 새로운 라이브러리 가 도움이 될 수 있습니다. 읽기에 도움이되는 분산 컴퓨팅을 사용합니다. 다음은 그 기능을 팬더와 비교 한 좋은 기사 입니다. 본질적으로 팬더와 동일한 기능을 사용합니다.

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

이 새로운 모듈이 기존 모듈 modin과 어떻게 비교 되는지에 대해 언급 할 수 있습니까 dask.dataframe? 예를 들어, 모든 로컬 CPU 코어를 활용하려면 팬더에서 dask로 이동을 참조하십시오 .
jpp

0

chunksize 옵션을 사용하기 전에 @unutbu에서 언급 한 청크 for-loop 내부에 작성하려는 프로세스 기능에 대해 확신하려면 nrows 옵션을 사용하면됩니다.

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

프로세스 블록이 준비되었다고 확신하면 전체 데이터 프레임에 대한 청킹 for 루프에 넣을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.