Làm cách nào để đọc tệp csv lớn với gấu trúc?


194

Tôi đang cố đọc một tệp csv lớn (aprox. 6 GB) trong gấu trúc và tôi đang gặp lỗi bộ nhớ:

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

...

MemoryError: 

Bất kỳ trợ giúp về điều này?


3
Thật kỳ lạ, một câu hỏi tương tự đã được hỏi gần một năm trước câu hỏi này ...
DarkCygnus


Điều này có trả lời câu hỏi của bạn không? Luồng công việc "dữ liệu lớn" sử dụng gấu trúc
AMC

Câu trả lời:


261

Lỗi cho thấy máy không có đủ bộ nhớ để đọc toàn bộ CSV vào DataFrame cùng một lúc. Giả sử bạn không cần toàn bộ dữ liệu trong bộ nhớ cùng một lúc, một cách để tránh sự cố sẽ là xử lý CSV theo từng khối (bằng cách chỉ định chunksizetham số):

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

Các chunksizetham số quy định cụ thể số lượng hàng trên mỗi đoạn. (Tất nhiên, đoạn cuối cùng có thể chứa ít hơn chunksizehàng, tất nhiên.)


17
bạn thường cần gấp 2 lần bộ nhớ cuối để đọc trong một cái gì đó (từ csv, mặc dù các định dạng khác tốt hơn khi có yêu cầu bộ nhớ thấp hơn). FYI điều này đúng khi cố gắng làm hầu hết mọi thứ cùng một lúc. Tốt hơn nhiều để chunk nó (có sử dụng bộ nhớ liên tục).
Jeff

24
@altabq: Vấn đề ở đây là chúng tôi không có đủ bộ nhớ để xây dựng một DataFrame duy nhất chứa tất cả dữ liệu. Giải pháp ở trên cố gắng đối phó với tình huống này bằng cách giảm các khối (ví dụ: bằng cách tổng hợp hoặc trích xuất thông tin mong muốn) mỗi lần một khối - do đó tiết kiệm bộ nhớ. Dù bạn làm gì, KHÔNG gọi DF.append(chunk)bên trong vòng lặp. Điều đó sẽ sử dụng các O(N^2)hoạt động sao chép. Tốt hơn là thêm dữ liệu tổng hợp vào danh sách , sau đó xây dựng DataFrame từ danh sách bằng một cuộc gọi đến pd.DataFramehoặc pd.concat(tùy thuộc vào loại dữ liệu tổng hợp).
unutbu

12
@altabq: Gọi DF.append(chunk)trong một vòng lặp yêu cầu O(N^2)sao chép các hoạt động trong đó Nkích thước của các khối, bởi vì mỗi cuộc gọi để DF.appendtrả về một DataFrame mới. Gọi pd.DataFramehoặc pd.concat một lần bên ngoài vòng lặp làm giảm số lượng sao chép vào O(N).
unutbu

5
@Pyderman: Có, chunksizetham số đề cập đến số lượng hàng trên mỗi đoạn. Đoạn cuối cùng có thể chứa ít hơn chunksizehàng, tất nhiên.
unutbu

7
@Pyderman: Vâng; gọi pd.concat([list_of_dfs]) một lần sau vòng lặp nhanh hơn nhiều so với gọi pd.concathoặc df.appendnhiều lần trong vòng lặp. Tất nhiên, bạn sẽ cần một lượng bộ nhớ đáng kể để chứa toàn bộ csv 6GB dưới dạng một DataFrame.
unutbu

85

Chunking không nên luôn là cổng gọi đầu tiên cho vấn đề này.

  1. Là tệp lớn do dữ liệu không phải là số lặp lại hoặc các cột không mong muốn?

    Nếu vậy, đôi khi bạn có thể thấy tiết kiệm bộ nhớ lớn bằng cách đọc trong các cột dưới dạng danh mục và chọn các cột bắt buộc thông qua tham số pd.read_csv usecols .

  2. Liệu quy trình làm việc của bạn yêu cầu cắt, thao tác, xuất khẩu?

    Nếu vậy, bạn có thể sử dụng dask.dataframe để cắt, thực hiện các phép tính của mình và xuất lặp lại. Chunking được thực hiện âm thầm bởi dask, cũng hỗ trợ một tập hợp con API gấu trúc.

  3. Nếu vẫn thất bại, đọc từng dòng thông qua các đoạn.

    Chunk qua gấu trúc hoặc qua thư viện csv như là phương sách cuối cùng.


3
Tôi đã không nhận thức được Dask. +100 cho điều đó!
noamtm

34

Tôi đã tiến hành như thế này:

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
Có một lý do bạn chuyển từ read_csvtới read_table?
Pyderman

33

Đối với dữ liệu lớn, tôi khuyên bạn nên sử dụng thư viện "dask",
vd:

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

Bạn có thể đọc thêm từ các tài liệu ở đây .

Một lựa chọn tuyệt vời khác là sử dụng modin vì tất cả các chức năng giống hệt với gấu trúc nhưng nó tận dụng các thư viện khung dữ liệu phân tán như dask.


11
Bất kỳ lợi ích nào đối với gấu trúc, có thể đánh giá cao việc thêm một vài gợi ý nữa
PirateApp

2
Tôi đã không sử dụng Dask rất lâu nhưng ưu điểm chính trong các trường hợp sử dụng của tôi là Dask có thể chạy song song trên nhiều máy, nó cũng có thể điều chỉnh dữ liệu dưới dạng các lát cắt vào bộ nhớ.
Simbarashe Timothy Motsi

2
cảm ơn! là một sự thay thế cho gấu trúc hoặc nó hoạt động trên đỉnh của gấu trúc như một lớp
PirateApp

3
Xin chào, nó hoạt động như một trình bao bọc cho Numpy, Pandas và Scikit-Learn.
Simbarashe Timothy Motsi

1
Tôi đã cố gắng đối mặt với một số vấn đề với Dask và luôn đưa ra lỗi cho mọi thứ. Ngay cả với khối Nó cũng ném lỗi Bộ nhớ. Xem stackoverflow.com/questions/59865572/
Mạnh

10

Câu trả lời trên đã thỏa mãn chủ đề. Dù sao, nếu bạn cần tất cả dữ liệu trong bộ nhớ - hãy xem bcolz . Nó nén dữ liệu trong bộ nhớ. Tôi đã có kinh nghiệm thực sự tốt với nó. Nhưng nó thiếu rất nhiều tính năng của gấu trúc

Chỉnh sửa: Tôi có tốc độ nén ở khoảng 1/10 hoặc kích thước orig tôi nghĩ, tất nhiên tùy thuộc vào loại dữ liệu. Các tính năng quan trọng bị thiếu là tổng hợp.


2
Vui lòng cải thiện câu trả lời này bằng cách cho chúng tôi biết a) tỷ lệ nén nào bạn nhận được và b) những tính năng chính của gấu trúc mà nó thiếu? Nó có thể xử lý NA không? dây? phân loại? ngày?
smci

Huh? Nó có thể xử lý NA không? dây? phân loại? ngày? Đây là những điều làm cho gấu trúc csv đọc chậm và chập chờn. NA và các đối tượng như chuỗi (thậm chí ngắn) là một kẻ giết người. Btw .ipynb được tham chiếu từ blog của bạn không hoạt động.
smci

1
@smci tôi đang đọc bạn lưu ý. nhưng tôi đề nghị bạn nên xem tài liệu. tôi sẽ cần phải đọc chúng một mình.
PlagTag

2
Ok vì vậy nó không thể xử lý NA, chuỗi hoặc ngày. Tôi nghi ngờ nó có thể xử lý nổi.
smci

1
Tôi cho rằng bạn có thể tiền xử lý với gấu trúc bằng chunksphương pháp được đề cập, sau đó sử dụng bcolz nếu bạn cần tất cả dữ liệu trong bộ nhớ để thực hiện phân tích. Chỉ là một ý nghĩ.
JakeCowton

6

Bạn có thể đọc dữ liệu dưới dạng khối và lưu từng đoạn dưới dạng dưa chua.

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)

Trong bước tiếp theo, bạn đọc trong dưa chua và nối từng dưa vào khung dữ liệu mong muốn của bạn.

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
Nếu cuối cùng của bạn dfhoàn toàn phù hợp với bộ nhớ (như ngụ ý) và chứa cùng một lượng dữ liệu như đầu vào của bạn, chắc chắn bạn không cần phải chunk cả?
jpp

Bạn sẽ cần phải chunk trong trường hợp này nếu, ví dụ, tệp của bạn rất rộng (như lớn hơn 100 cột với rất nhiều cột chuỗi). Điều này làm tăng bộ nhớ cần thiết để giữ df trong bộ nhớ. Ngay cả một tệp 4GB như thế này cũng có thể sử dụng từ 20 đến 30 GB RAM trên hộp có RAM 64 GB.
cdabel

4

Hàm read_csv và read_table gần như giống nhau. Nhưng bạn phải gán dấu phân cách “và khi bạn sử dụng hàm read_table trong chương trình của mình.

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)

Nó sẽ giúp nếu nêu câu hỏi của bạn trong bài viết này. Giống như "sự khác biệt giữa read_csv và read_table là gì?" hoặc "Tại sao bảng đọc cần một dấu phân cách?"
nate_weldon

1
Nó phụ thuộc vào tập tin của bạn trông như thế nào. Một số tệp có các dấu phân cách phổ biến như "," hoặc "|" hoặc "\ t" nhưng bạn có thể thấy các tệp khác có dấu phân cách, chẳng hạn như 0x01, 0x02 (tạo cái này lên), v.v.
Naufal

3

Giải pháp 1:

Sử dụng gấu trúc với dữ liệu lớn

Giải pháp 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
Ở đây một lần nữa, chúng tôi đang tải tập tin 6 GB hoàn toàn vào bộ nhớ, Có tùy chọn nào không, chúng tôi có thể xử lý đoạn hiện tại và sau đó đọc đoạn tiếp theo
debaonline4u

6
chỉ không làm dfList.append, chỉ xử lý riêng từng đoạn ( df)
gokul_uf

3

Dưới đây là một ví dụ:

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

Nếu bạn sử dụng gấu trúc đọc tệp lớn thành khối và sau đó nhường từng hàng, đây là những gì tôi đã làm

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

Tôi muốn đưa ra một câu trả lời toàn diện hơn dựa trên hầu hết các giải pháp tiềm năng đã được cung cấp. Tôi cũng muốn chỉ ra một trợ giúp tiềm năng có thể giúp quá trình đọc.

Lựa chọn 1: bánh quy

"Dtypes" là một tham số khá mạnh mẽ mà bạn có thể sử dụng để giảm áp lực bộ nhớ của các readphương thức. Xem điều này và câu trả lời này . Gấu trúc, theo mặc định, cố gắng suy ra các loại dữ liệu.

Đề cập đến cấu trúc dữ liệu, mọi dữ liệu được lưu trữ, phân bổ bộ nhớ đều diễn ra. Ở mức cơ bản, hãy tham khảo các giá trị bên dưới (Bảng bên dưới minh họa các giá trị cho ngôn ngữ lập trình 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

Tham khảo này trang để xem sự kết hợp giữa các loại NumPy và C.

Hãy nói rằng bạn có một mảng các số nguyên của chữ số . Về mặt lý thuyết và thực tế, bạn có thể gán mảng kiểu số nguyên 16 bit, nhưng sau đó bạn sẽ phân bổ nhiều bộ nhớ hơn mức bạn thực sự cần để lưu trữ mảng đó. Để ngăn chặn điều này, bạn có thể đặt dtypetùy chọn trên read_csv. Bạn không muốn lưu trữ các mục mảng dưới dạng số nguyên dài trong đó thực sự bạn có thể khớp chúng với số nguyên 8 bit ( np.int8hoặc np.uint8).

Quan sát bản đồ dtype sau.

Nguồn: https://pbpython.com/pandas_dtypes.html

Bạn có thể truyền dtypetham số dưới dạng tham số trên các phương thức gấu trúc dưới dạng dict trên readnhư {cột: 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)

Cách 2: Đọc bởi Chunks

Đọc dữ liệu theo từng khối cho phép bạn truy cập một phần dữ liệu trong bộ nhớ và bạn có thể áp dụng tiền xử lý trên dữ liệu của mình và bảo toàn dữ liệu đã xử lý thay vì dữ liệu thô. Sẽ tốt hơn nhiều nếu bạn kết hợp tùy chọn này với cái đầu tiên, dtypes .

Tôi muốn chỉ ra các phần sách dạy nấu ăn của gấu trúc cho quá trình đó, nơi bạn có thể tìm thấy nó ở đây . Lưu ý hai phần đó;

Tùy chọn 3: Dask

Dask là một khung được định nghĩa trong trang web của Dask là:

Dask cung cấp tính song song nâng cao cho các phân tích, cho phép hiệu suất ở quy mô cho các công cụ bạn yêu thích

Nó được sinh ra để bao gồm những phần cần thiết nơi gấu trúc không thể với tới. Dask là một khung công tác mạnh mẽ cho phép bạn truy cập dữ liệu nhiều hơn bằng cách xử lý nó theo cách phân tán.

Bạn có thể sử dụng dask để xử lý toàn bộ dữ liệu của mình, Dask sẽ chăm sóc phần chunk, vì vậy không giống như gấu trúc, bạn chỉ có thể xác định các bước xử lý của mình và để Dask thực hiện công việc. Dask không áp dụng các tính toán trước khi nó được đẩy mạnh computevà / hoặc persist(xem câu trả lời ở đây để biết sự khác biệt).

Các phương tiện khác (Ý tưởng)

  • Lưu lượng ETL được thiết kế cho dữ liệu. Chỉ giữ lại những gì cần thiết từ dữ liệu thô.
    • Đầu tiên, áp dụng ETL cho toàn bộ dữ liệu với các khung như Dask hoặc PySpark và xuất dữ liệu đã xử lý.
    • Sau đó xem liệu dữ liệu được xử lý có thể phù hợp với toàn bộ bộ nhớ không.
  • Xem xét tăng RAM của bạn.
  • Xem xét làm việc với dữ liệu đó trên nền tảng đám mây.

0

Ngoài các câu trả lời ở trên, đối với những người muốn xử lý CSV và sau đó xuất sang csv, parquet hoặc SQL, d6tstack là một lựa chọn tốt khác. Bạn có thể tải nhiều tệp và nó xử lý các thay đổi lược đồ dữ liệu (các cột được thêm / xóa). Chunked ra khỏi hỗ trợ cốt lõi đã được xây dựng trong.

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

Trong trường hợp ai đó vẫn đang tìm kiếm thứ gì đó như thế này, tôi thấy rằng thư viện mới có tên modin này có thể giúp ích. Nó sử dụng điện toán phân tán có thể giúp đọc. Đây là một bài viết tốt so sánh chức năng của nó với gấu trúc. Nó chủ yếu sử dụng các chức năng tương tự như gấu trúc.

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

Bạn có thể nhận xét về cách mô-đun mới này modinso sánh với các thiết lập tốt dask.dataframe? Ví dụ, xem di chuyển từ gấu trúc sang dask để sử dụng tất cả các lõi cpu cục bộ .
jpp

0

Trước khi sử dụng tùy chọn chunksize nếu bạn muốn chắc chắn về chức năng quy trình mà bạn muốn viết bên trong vòng lặp chunking như được đề cập bởi @unutbu, bạn chỉ cần sử dụng tùy chọn mũi tên.

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

Khi bạn chắc chắn rằng khối quy trình đã sẵn sàng, bạn có thể đặt khối đó vào vòng lặp for cho toàn bộ khung dữ liệu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.