DataFrame lớn, liên tục trong gấu trúc


91

Tôi đang tìm cách chuyển sang python và gấu trúc với tư cách là người dùng SAS lâu năm.

Tuy nhiên, khi chạy một số bài kiểm tra ngày hôm nay, tôi đã rất ngạc nhiên rằng python đã hết bộ nhớ khi cố gắng với pandas.read_csv()tệp csv 128mb. Nó có khoảng 200.000 hàng và 200 cột chủ yếu là dữ liệu số.

Với SAS, tôi có thể nhập tệp csv vào tập dữ liệu SAS và nó có thể lớn bằng ổ cứng của tôi.

Có một cái gì đó tương tự trong pandas?

Tôi thường xuyên làm việc với các tệp lớn và không có quyền truy cập vào mạng máy tính phân tán.


Tôi không quen thuộc với gấu trúc, nhưng bạn có thể muốn xem qua việc lặp lại tệp. pandas.pydata.org/pandas-docs/stable/...
monkut

Câu trả lời:


79

Về nguyên tắc, nó sẽ không hết bộ nhớ, nhưng hiện có những vấn đề về bộ nhớ read_csvtrên các tệp lớn do một số vấn đề phức tạp bên trong Python gây ra (điều này là mơ hồ nhưng nó đã được biết đến từ lâu: http://github.com/pydata / pandas / issue / 407 ).

Hiện tại không có giải pháp hoàn hảo (đây là một giải pháp tẻ nhạt: bạn có thể phiên âm từng hàng một thành một mảng NumPy được phân bổ trước hoặc tệp ánh xạ bộ nhớ-- np.mmap), nhưng tôi sẽ làm việc trong tương lai gần. Một giải pháp khác là đọc tệp trong các phần nhỏ hơn (sử dụng iterator=True, chunksize=1000) sau đó nối sau đó với pd.concat. Vấn đề xảy ra khi bạn kéo toàn bộ tệp văn bản vào bộ nhớ trong một lần nhấn lớn.


1
Giả sử tôi có thể đọc tệp và nối tất cả chúng lại với nhau thành một DataFrame. DataFrame có phải nằm trong bộ nhớ không? Với SAS, tôi có thể làm việc với các tập dữ liệu ở bất kỳ kích thước nào miễn là tôi có dung lượng ổ cứng. Nó có giống với DataFrames không? Tôi có ấn tượng rằng chúng bị hạn chế bởi RAM chứ không phải dung lượng ổ cứng. Xin lỗi vì câu hỏi của noob và cảm ơn bạn đã giúp đỡ. Tôi thích cuốn sách của bạn.
Zelazny

3
Đúng vậy, bạn bị hạn chế bởi RAM. SAS thực sự đã hỗ trợ tốt hơn nhiều cho việc xử lý dữ liệu lớn "ngoài lõi".
Wes McKinney

5
@WesMcKinney Những cách giải quyết này sẽ không còn cần thiết nữa, vì bạn đã có trình tải csv mới trong phiên bản 0.10, phải không?
Gabriel Grant,

79

Wes tất nhiên là đúng! Tôi chỉ đang cố gắng cung cấp một mã ví dụ hoàn chỉnh hơn một chút. Tôi gặp sự cố tương tự với tệp 129 Mb, được giải quyết bằng cách:

import pandas as pd

tp = pd.read_csv('large_dataset.csv', iterator=True, chunksize=1000)  # gives TextFileReader, which is iterable with chunks of 1000 rows.
df = pd.concat(tp, ignore_index=True)  # df is DataFrame. If errors, do `list(tp)` instead of `tp`

6
Tôi nghĩ rằng bạn chỉ có thể làm gì df = concate(tp, ignore_index=True)?
Andy Hayden

@smci Đã thử điều này nhanh chóng với cùng một dữ liệu được lặp lại x4 (550 Mb) hoặc x8 (1.1Gb). Điều thú vị là, có hoặc không có [x cho x in tp], x4 vẫn hoạt động tốt và x8 gặp sự cố trong MemoryError.
fickludd

3
Tôi nhận được lỗi này khi sử dụng nó: AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". Bất kỳ ý tưởng những gì đang xảy ra ở đây?
Prince Kumar

3
Lỗi này sẽ được sửa trong 0.14 (sắp phát hành), github.com/pydata/pandas/pull/6941 ; giải pháp cho <0.14.0 là phải làmpd.concat(list(tp), ignore_index=True)
Jeff

1
gì xảy ra nếu các giá trị là chuỗi hoặc phân loại - tôi đang nhận được lỗi: loại không tương thích trong concat phân loại
As3adTintin

41

Đây là một chủ đề cũ hơn, nhưng tôi chỉ muốn kết xuất giải pháp thay thế của mình ở đây. Ban đầu tôi đã thử chunksizetham số (ngay cả với các giá trị khá nhỏ như 10000), nhưng nó không giúp được gì nhiều; vẫn gặp sự cố kỹ thuật với kích thước bộ nhớ (CSV của tôi là ~ 7,5 Gb).

Ngay bây giờ, tôi chỉ đọc các phần của tệp CSV theo cách tiếp cận vòng lặp và thêm chúng vào cơ sở dữ liệu SQLite từng bước một:

import pandas as pd
import sqlite3
from pandas.io import sql
import subprocess

# In and output file paths
in_csv = '../data/my_large.csv'
out_sqlite = '../data/my.sqlite'

table_name = 'my_table' # name for the SQLite database table
chunksize = 100000 # number of lines to process at each iteration

# columns that should be read from the CSV file
columns = ['molecule_id','charge','db','drugsnow','hba','hbd','loc','nrb','smiles']

# Get number of lines in the CSV file
nlines = subprocess.check_output('wc -l %s' % in_csv, shell=True)
nlines = int(nlines.split()[0]) 

# connect to database
cnx = sqlite3.connect(out_sqlite)

# Iteratively read CSV and dump lines into the SQLite table
for i in range(0, nlines, chunksize):

    df = pd.read_csv(in_csv,  
            header=None,  # no header, define column header manually later
            nrows=chunksize, # number of rows to read at each iteration
            skiprows=i)   # skip rows that were already read

    # columns to read        
    df.columns = columns

    sql.to_sql(df, 
                name=table_name, 
                con=cnx, 
                index=False, # don't use CSV file index
                index_label='molecule_id', # use a unique column from DataFrame as index
                if_exists='append') 
cnx.close()    

4
Siêu hữu ích để xem trường hợp sử dụng thực tế cho tính năng đọc phân đoạn. Cảm ơn.
Alex Kestner

5
Chỉ là một nhận xét nhỏ, cho chủ đề cũ này: pandas.read_csvtrả về trực tiếp (ít nhất là trên phiên bản tôi hiện đang sử dụng) một trình lặp nếu bạn chỉ cần cung cấp iterator=Truechunksize=chunksize. Do đó, bạn sẽ chỉ forlặp lại pd.read_csvcuộc gọi, thay vì lặp lại cuộc gọi mỗi lần. Tuy nhiên, chi phí này chỉ là chi phí cuộc gọi, có thể không có tác động đáng kể.
Joël

1
Chào, Joel. Cảm ơn đã lưu ý! Các iterator=Truechunksizetham số đã tồn tại trước đó nếu tôi nhớ không nhầm. Có lẽ đã có một lỗi trong phiên bản cũ khiến ký ức đòn-up - Tôi sẽ cung cấp cho nó một thử lần sau tôi đọc một DataFrame lớn trong Pandas (tôi chủ yếu sử dụng Blaze bây giờ cho công việc như vậy)

6

Dưới đây là quy trình làm việc của tôi.

import sqlalchemy as sa
import pandas as pd
import psycopg2

count = 0
con = sa.create_engine('postgresql://postgres:pwd@localhost:00001/r')
#con = sa.create_engine('sqlite:///XXXXX.db') SQLite
chunks = pd.read_csv('..file', chunksize=10000, encoding="ISO-8859-1",
                     sep=',', error_bad_lines=False, index_col=False, dtype='unicode')

Dựa trên kích thước tệp của bạn, bạn nên tối ưu hóa kích thước khối tốt hơn.

 for chunk in chunks:
        chunk.to_sql(name='Table', if_exists='append', con=con)
        count += 1
        print(count)

Sau khi có tất cả dữ liệu trong Cơ sở dữ liệu, Bạn có thể truy vấn những dữ liệu bạn cần từ cơ sở dữ liệu.


3

Nếu bạn muốn tải các tệp csv lớn, dask có thể là một lựa chọn tốt. Nó bắt chước api của gấu trúc, vì vậy nó có cảm giác khá giống với gấu trúc

liên kết đến dask trên github


Cảm ơn, kể từ khi tôi đăng bài này, tôi đã sử dụng dask và định dạng parquet.
Zelazny7

1

Bạn có thể sử dụng Pytable thay vì pandas df. Nó được thiết kế cho các tập dữ liệu lớn và định dạng tệp là hdf5. Nên thời gian xử lý hồ sơ tương đối nhanh.

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.