Cách lưu trữ khung dữ liệu bằng Pandas


317

Ngay bây giờ tôi đang nhập CSVmột tệp dữ liệu khá lớn mỗi khi tôi chạy tập lệnh. Có một giải pháp tốt để giữ cho khung dữ liệu đó liên tục có sẵn ở giữa các lần chạy để tôi không phải dành toàn bộ thời gian chờ đợi tập lệnh chạy không?


2
Phải, đây là một trong những khiếu nại chính của tôi khi sử dụng Python - không có cách đơn giản nào để lưu và truy xuất khung dữ liệu. R và SAS thân thiện với người dùng hơn nhiều về mặt này.
RobertF

Câu trả lời:


481

Cách dễ nhất là ngâm nó bằng cách sử dụng to_pickle:

df.to_pickle(file_name)  # where to save it, usually as a .pkl

Sau đó, bạn có thể tải lại bằng cách sử dụng:

df = pd.read_pickle(file_name)

Lưu ý: trước 0.11.1 saveloadlà cách duy nhất để thực hiện việc này (hiện tại chúng không được hỗ trợ to_pickleread_pickletương ứng).


Một lựa chọn phổ biến khác là sử dụng HDF5 ( pytables ) cung cấp thời gian truy cập rất nhanh cho các bộ dữ liệu lớn:

store = HDFStore('store.h5')

store['df'] = df  # save it
store['df']  # load it

Các chiến lược nâng cao hơn được thảo luận trong sách dạy nấu ăn .


Vì 0.13 cũng có gói tin có thể tốt hơn cho khả năng tương tác, như là một thay thế nhanh hơn cho JSON hoặc nếu bạn có dữ liệu nặng về đối tượng / văn bản (xem câu hỏi này ).


8
@geekazoid save không được dùng cho to_pickle (tạo ra một dưa chua chứ không phải là csv, một đối tượng nhanh hơn / khác nhau rất nhiều).
Andy Hayden

9
@geekazoid Trong trường hợp dữ liệu cần được chuyển đổi sau khi tải (tức là chuỗi / đối tượng thành datetime64), điều này sẽ cần phải được thực hiện lại sau khi tải một csv đã lưu, dẫn đến mất hiệu suất. Pickle lưu dataframe ở trạng thái hiện tại do đó dữ liệu và định dạng của nó được giữ nguyên. Điều này có thể dẫn đến tăng hiệu suất lớn.
harbun

4
Cả dưa chua và HDFStore đều không thể lưu khung dữ liệu lớn hơn 8GB. Có những lựa chọn thay thế?
dùng1700890

1
@ user1700890 cố gắng tạo từ dữ liệu ngẫu nhiên (văn bản và mảng) và gửi câu hỏi mới. Tôi không nghĩ rằng điều này có thể đúng / nghi ngờ chúng ta đang thiếu một cái gì đó. Câu hỏi mới sẽ thu hút được nhiều ánh nhìn hơn, nhưng hãy cố gắng đưa / tạo DataFrame sao chép :)
Andy Hayden

1
@Y lòngLiu bạn có thể thay đổi chế độ sau khi stackoverflow
Andy Hayden

100

Mặc dù đã có một số câu trả lời tôi đã tìm thấy một so sánh hay, trong đó họ đã thử một số cách để tuần tự hóa Pandas DataFrames: Lưu trữ hiệu quả Pandas DataFrames .

Họ so sánh:

  • Pickle: định dạng dữ liệu ASCII gốc
  • cPickle, một thư viện C
  • Pickle-p2: sử dụng định dạng nhị phân mới hơn
  • json: thư viện jl Standardlib
  • json-no-index: như json, nhưng không có chỉ mục
  • Trình đóng gói: thay thế JSON nhị phân
  • CSV
  • hdfstore: định dạng lưu trữ HDF5

Trong thử nghiệm của họ, họ tuần tự hóa một DataFrame gồm 1.000.000 hàng với hai cột được kiểm tra riêng: một cột có dữ liệu văn bản, cột kia có số. Tuyên bố từ chối trách nhiệm của họ nói:

Bạn không nên tin tưởng rằng những gì tiếp theo sẽ khái quát hóa dữ liệu của bạn. Bạn nên xem dữ liệu của riêng bạn và tự chạy điểm chuẩn

Mã nguồn cho bài kiểm tra mà họ đề cập có sẵn trực tuyến . Vì mã này không hoạt động trực tiếp nên tôi đã thực hiện một số thay đổi nhỏ, bạn có thể nhận được ở đây: serialize.py Tôi nhận được các kết quả sau:

kết quả so sánh thời gian

Họ cũng đề cập rằng với việc chuyển đổi dữ liệu văn bản thành dữ liệu phân loại, việc tuần tự hóa nhanh hơn nhiều. Trong thử nghiệm của họ nhanh gấp khoảng 10 lần (cũng xem mã kiểm tra).

Chỉnh sửa : Thời gian cao hơn cho dưa chua so với CSV có thể được giải thích bằng định dạng dữ liệu được sử dụng. Theo mặc định, picklesử dụng biểu diễn ASCII có thể in được, tạo ra các tập dữ liệu lớn hơn. Tuy nhiên, có thể thấy từ biểu đồ, dưa chua sử dụng định dạng dữ liệu nhị phân mới hơn (phiên bản 2, pickle-p2) có thời gian tải thấp hơn nhiều.

Một số tài liệu tham khảo khác:


1
Tôi cập nhật câu trả lời của tôi để giải thích câu hỏi của bạn. Để tóm tắt: theo mặc định, dưa chua lưu trữ dữ liệu ở định dạng ASCII.
kích hoạt

1
Ah, thanx cho lời giải thích đó! Lưu ý, dường như gấu trúc DataFrame .to_pickle đang sử dụng pkl.HIGHEST_PROTOCOL (nên là 2)
ntg

2
Có vẻ như blog được liên kết ở trên ( Lưu trữ hiệu quả Pandas DataFrames đã bị xóa. Tôi đã tự so sánh với .to_pickle()(sử dụng lưu trữ nhị phân) so với .to_hdf()(không nén). Mục tiêu là tốc độ, kích thước tệp cho HDF là 11x Pickle và thời gian tải là 5x Pickle. Dữ liệu của tôi là ~ 5k tệp với ~ 7k hàng x 6 cols mỗi tệp, chủ yếu là số.
hamx0r

1
Trang vẫn tồn tại, bạn chỉ cần xóa dấu gạch chéo: Lưu trữ hiệu quả Pandas DataFrames
IanSR

2
@Mike Williamson, trong thử nghiệm của tôi, Pickle tải nhanh hơn 5x so với HDF và cũng chiếm 1/11 dung lượng đĩa (tức là hdf lớn hơn 11 lần trên đĩa và mất 5x Thời gian để tải từ đĩa như dưa chua đã làm). đây là tất cả trên python 3 với gấu trúc 0.22.0.
hamx0r

35

Nếu tôi hiểu chính xác, bạn đã sử dụng pandas.read_csv()nhưng muốn tăng tốc quá trình phát triển để bạn không phải tải tệp mỗi khi bạn chỉnh sửa tập lệnh của mình, điều đó có đúng không? Tôi có một vài đề xuất:

  1. bạn chỉ có thể tải một phần của tệp CSV bằng cách pandas.read_csv(..., nrows=1000)chỉ tải bit trên cùng của bảng trong khi bạn đang thực hiện phát triển

  2. sử dụng ipython cho một phiên tương tác, sao cho bạn giữ bảng gấu trúc trong bộ nhớ khi bạn chỉnh sửa và tải lại tập lệnh của mình.

  3. chuyển đổi csv thành bảng HDF5

  4. cập nhật sử dụng DataFrame.to_feather()pd.read_feather()lưu trữ dữ liệu ở định dạng nhị phân lông tương thích R siêu nhanh (trong tay tôi, nhanh hơn một chút so với pandas.to_pickle()dữ liệu số và nhanh hơn nhiều trên dữ liệu chuỗi).

Bạn cũng có thể quan tâm đến câu trả lời này trên stackoverflow.


Bạn có biết tại sao to_feathersẽ hoạt động tốt trên dữ liệu chuỗi? Tôi đã điểm chuẩn to_pickleto_featuretrên khung dữ liệu số và dưa chua của tôi nhanh hơn khoảng 3 lần.
zyxue

@zyxue câu hỏi hay, thật lòng tôi không chơi nhiều với những thứ lông vũ, vì vậy tôi không có câu trả lời

20

Pickle hoạt động tốt!

import pandas as pd
df.to_pickle('123.pkl')    #to save the dataframe, df to 123.pkl
df1 = pd.read_pickle('123.pkl') #to load 123.pkl back to the dataframe df

8
Lưu ý rằng các tệp được tạo không phải là tệp csv, có thể tốt hơn là sử dụng tiện ích mở rộng .pklnhư được đề xuất trong câu trả lời @Andy Haydens.
agold 6/11/2015

5

Bạn có thể sử dụng tập tin định dạng lông. Nó cực kỳ nhanh.

df.to_feather('filename.ft')

Và dữ liệu sau đó có thể được sử dụng trực tiếp bằng Rcách sử dụng featherthư viện.
James Hirschorn

4

Pandas DataFrames có to_picklechức năng hữu ích để lưu DataFrame:

import pandas as pd

a = pd.DataFrame({'A':[0,1,0,1,0],'B':[True, True, False, False, False]})
print a
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False

a.to_pickle('my_file.pkl')

b = pd.read_pickle('my_file.pkl')
print b
#    A      B
# 0  0   True
# 1  1   True
# 2  0  False
# 3  1  False
# 4  0  False

4

Như đã đề cập, có các tùy chọn và định dạng tệp khác nhau ( HDF5 , JSON , CSV , parquet , SQL ) để lưu trữ khung dữ liệu. Tuy nhiên, picklekhông phải là công dân hạng nhất (tùy thuộc vào thiết lập của bạn), bởi vì:

  1. picklelà một rủi ro bảo mật tiềm năng. Hình thành tài liệu Python cho dưa chua :

Cảnh báo Các picklemô-đun không phải là an toàn đối với dữ liệu có sai sót hoặc cố xây dựng. Không bao giờ tháo dữ liệu nhận được từ một nguồn không đáng tin cậy hoặc không được xác thực.

  1. picklechậm Tìm ở đâyở đây điểm chuẩn.

Tùy thuộc vào thiết lập / sử dụng của bạn, cả hai giới hạn đều không áp dụng, nhưng tôi không khuyến nghị sử dụng picklenhư là sự tồn tại mặc định cho các khung dữ liệu của gấu trúc.


1

Các định dạng tệp Numpy khá nhanh đối với dữ liệu số

Tôi thích sử dụng các tệp numpy vì chúng nhanh và dễ làm việc. Đây là một điểm chuẩn đơn giản để lưu và tải một khung dữ liệu với 1 cột 1 triệu điểm.

import numpy as np
import pandas as pd

num_dict = {'voltage': np.random.rand(1000000)}
num_df = pd.DataFrame(num_dict)

sử dụng %%timeitchức năng ma thuật của ipython

%%timeit
with open('num.npy', 'wb') as np_file:
    np.save(np_file, num_df)

đầu ra là

100 loops, best of 3: 5.97 ms per loop

để tải dữ liệu trở lại vào khung dữ liệu

%%timeit
with open('num.npy', 'rb') as np_file:
    data = np.load(np_file)

data_df = pd.DataFrame(data)

đầu ra là

100 loops, best of 3: 5.12 ms per loop

KHÔNG TỆ!

TIÊU DÙNG

Có vấn đề nếu bạn lưu tệp numpy bằng python 2 và sau đó thử mở bằng python 3 (hoặc ngược lại).


6
lưu ý rằng giải pháp này sẽ xóa tất cả các tên cột của bạn và thay đổi tất cả dữ liệu số nguyên của bạn thành float :(
Joseph Garvin

0

https://docs.python.org/3/l Library / topperle.html

Các định dạng giao thức dưa chua:

Giao thức phiên bản 0 là giao thức gốc có thể đọc được của con người và có khả năng tương thích ngược với các phiên bản trước của Python.

Giao thức phiên bản 1 là định dạng nhị phân cũ cũng tương thích với các phiên bản trước của Python.

Giao thức phiên bản 2 đã được giới thiệu trong Python 2.3. Nó cung cấp hiệu quả hơn nhiều cho các lớp học kiểu mới. Tham khảo PEP 307 để biết thông tin về các cải tiến do giao thức 2 mang lại.

Giao thức phiên bản 3 đã được thêm vào Python 3.0. Nó có hỗ trợ rõ ràng cho các đối tượng byte và không thể được giải nén bởi Python 2.x. Đây là giao thức mặc định và giao thức được đề xuất khi cần tương thích với các phiên bản Python 3 khác.

Giao thức phiên bản 4 đã được thêm vào Python 3.4. Nó bổ sung hỗ trợ cho các đối tượng rất lớn, chọn nhiều loại đối tượng hơn và một số tối ưu hóa định dạng dữ liệu. Tham khảo PEP 3154 để biết thông tin về các cải tiến do giao thức 4 mang lại.


0

khả năng tương thích pyarrow trên các phiên bản

Di chuyển tổng thể đã được chuyển đến pyarrow / lông (cảnh báo khấu hao từ gấu trúc / Trình thông báo). Tuy nhiên, tôi có một thách thức với pyarrow với đặc điểm tạm thời Dữ liệu được tuần tự hóa với pyarrow 0.15.1 không thể được giải tuần tự hóa với 0.16.0 MROWI -7961 . Tôi đang sử dụng tuần tự hóa để sử dụng redis vì vậy phải sử dụng mã hóa nhị phân.

Tôi đã kiểm tra lại các tùy chọn khác nhau (sử dụng sổ ghi chép jupyter)

import sys, pickle, zlib, warnings, io
class foocls:
    def pyarrow(out): return pa.serialize(out).to_buffer().to_pybytes()
    def msgpack(out): return out.to_msgpack()
    def pickle(out): return pickle.dumps(out)
    def feather(out): return out.to_feather(io.BytesIO())
    def parquet(out): return out.to_parquet(io.BytesIO())

warnings.filterwarnings("ignore")
for c in foocls.__dict__.values():
    sbreak = True
    try:
        c(out)
        print(c.__name__, "before serialization", sys.getsizeof(out))
        print(c.__name__, sys.getsizeof(c(out)))
        %timeit -n 50 c(out)
        print(c.__name__, "zlib", sys.getsizeof(zlib.compress(c(out))))
        %timeit -n 50 zlib.compress(c(out))
    except TypeError as e:
        if "not callable" in str(e): sbreak = False
        else: raise
    except (ValueError) as e: print(c.__name__, "ERROR", e)
    finally: 
        if sbreak: print("=+=" * 30)        
warnings.filterwarnings("default")

Với các kết quả sau cho khung dữ liệu của tôi (trong outbiến jupyter)

pyarrow before serialization 533366
pyarrow 120805
1.03 ms ± 43.9 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pyarrow zlib 20517
2.78 ms ± 81.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
msgpack before serialization 533366
msgpack 109039
1.74 ms ± 72.8 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
msgpack zlib 16639
3.05 ms ± 71.7 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
pickle before serialization 533366
pickle 142121
733 µs ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
pickle zlib 29477
3.81 ms ± 60.4 µs per loop (mean ± std. dev. of 7 runs, 50 loops each)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
feather ERROR feather does not support serializing a non-default index for the index; you can .reset_index() to make the index into column(s)
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=
parquet ERROR Nested column branch had multiple children: struct<x: double, y: double>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

lông và sàn gỗ không hoạt động cho khung dữ liệu của tôi. Tôi sẽ tiếp tục sử dụng pyarrow. Tuy nhiên tôi sẽ bổ sung với dưa chua (không nén). Khi ghi vào bộ nhớ cache lưu trữ các biểu mẫu pyarrow và dưa chua. Khi đọc từ dự phòng bộ đệm đến dưa chua nếu khử lưu huỳnh pyarrow không thành công.


Điều này không trả lời câu hỏi
Jason S

0

Định dạng tùy thuộc vào trường hợp sử dụng của bạn

  • Lưu DataFrame giữa các phiên máy tính xách tay - lông , nếu bạn đã quen với dưa chua - cũng ok.
  • Lưu DataFrame ở kích thước tệp nhỏ nhất có thể - parquet hoặc pickle.gz (kiểm tra những gì tốt hơn cho dữ liệu của bạn)
  • Lưu một DataFrame rất lớn (hơn 10 triệu hàng) - hdf
  • Có thể đọc dữ liệu trên nền tảng khác (không phải Python) không hỗ trợ các định dạng khác - csv , csv.gz , kiểm tra xem sàn gỗ có được hỗ trợ không
  • Có thể đánh giá bằng mắt / sử dụng Excel / Google Sheets / Git diff - csv
  • Lưu một DataFrame chiếm gần hết RAM - csv

So sánh các định dạng tệp gấu trúc có trong video này .

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.