วิธีการเก็บดาต้าเฟรมโดยใช้ Pandas


317

ตอนนี้ฉันกำลังนำเข้าไฟล์ข้อมูลขนาดใหญ่พอสมควรCSVทุกครั้งที่เรียกใช้สคริปต์ มีวิธีแก้ปัญหาที่ดีในการทำให้ดาต้าเฟรมนั้นมีให้ใช้อย่างต่อเนื่องในระหว่างรันดังนั้นฉันไม่ต้องเสียเวลารอให้สคริปต์รันหรือไม่?


2
ใช่นี่เป็นหนึ่งในข้อร้องเรียนสำคัญของฉันโดยใช้ Python - ไม่มีวิธีง่ายๆในการบันทึกและดึงข้อมูลเฟรม R และ SAS เป็นมิตรกับผู้ใช้มากกว่าในแง่นี้
RobertF

คำตอบ:


481

วิธีที่ง่ายที่สุดคือการดองโดยใช้to_pickle:

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

จากนั้นคุณสามารถโหลดกลับมาโดยใช้:

df = pd.read_pickle(file_name)

หมายเหตุ: ก่อนหน้า 0.11.1 saveและloadเป็นวิธีเดียวที่จะทำสิ่งนี้ได้ (ตอนนี้เลิกใช้แล้วto_pickleและเป็นไปread_pickleตามลำดับ)


อีกตัวเลือกยอดนิยมคือใช้HDF5 ( pytables ) ซึ่งมีเวลาเข้าถึงที่รวดเร็วมากสำหรับชุดข้อมูลขนาดใหญ่:

store = HDFStore('store.h5')

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

กลยุทธ์ขั้นสูงอื่น ๆ ที่จะกล่าวถึงในตำรา


ตั้งแต่ 0.13 นอกจากนี้ยังมีmsgpackซึ่งอาจจะดีกว่าสำหรับการทำงานร่วมกันเป็นทางเลือกที่เร็วกว่าสำหรับ JSON หรือถ้าคุณมีข้อมูลของออบเจ็กต์หลาม / ข้อความหนัก ๆ (ดูคำถามนี้ )


8
@ geekazoid บันทึกถูกคัดค้าน to_pickle (ซึ่งสร้างผักดองมากกว่า csv ซึ่งเป็นวัตถุที่เร็วกว่า / แตกต่างกันมาก)
Andy Hayden

9
@geekazoid ในกรณีที่ข้อมูลจำเป็นต้องถูกเปลี่ยนหลังจากโหลด (เช่นสตริง / วัตถุเป็น datetime64) สิ่งนี้จะต้องทำอีกครั้งหลังจากโหลด csv ที่บันทึกไว้ส่งผลให้ประสิทธิภาพลดลง pickle บันทึก dataframe ในสถานะปัจจุบันดังนั้นข้อมูลและรูปแบบจะถูกเก็บไว้ สิ่งนี้สามารถนำไปสู่การเพิ่มขึ้นอย่างมาก
harbun

4
ทั้ง pickle และ HDFStore ไม่สามารถบันทึก dataframe ได้มากกว่า 8GB มีทางเลือกอื่นอีกไหม?
user1700890

1
@ user1700890 พยายามสร้างจากข้อมูลแบบสุ่ม (ข้อความและอาร์เรย์) และโพสต์คำถามใหม่ ฉันไม่คิดว่ามันจะถูก / สงสัยว่าเราขาดอะไรไป คำถามใหม่จะได้รับดวงตามากขึ้น แต่พยายามที่จะรวม / สร้าง DataFrame ที่ reproduces :)
แอนดี้เฮย์เดน

1
@YixingLiu คุณสามารถเปลี่ยนโหมดหลังจากความจริงstackoverflow.com/a/16249655/1240268
Andy Hayden

100

แม้ว่าจะมีคำตอบบางอย่างแล้วผมพบว่าการเปรียบเทียบที่ดีในการที่พวกเขาพยายามหลายวิธีที่จะเป็นอันดับนุ่น DataFrames: มีประสิทธิภาพร้านนุ่น DataFrames

พวกเขาเปรียบเทียบ:

  • pickle: รูปแบบข้อมูล ASCII ดั้งเดิม
  • cPickle, ไลบรารี C
  • pickle-p2: ใช้รูปแบบไบนารีที่ใหม่กว่า
  • json: ไลบรารีมาตรฐาน json
  • json-no-index: เช่น json แต่ไม่มี index
  • msgpack: ทางเลือก JSON ไบนารี
  • CSV
  • hdfstore: รูปแบบการจัดเก็บ HDF5

ในการทดสอบพวกเขาจะทำการเรียงลำดับ DataFrame 1,000,000 แถวด้วยการทดสอบสองคอลัมน์แยกกัน: หนึ่งรายการกับข้อมูลข้อความและอีกคอลัมน์หนึ่งพร้อมตัวเลข ข้อจำกัดความรับผิดชอบของพวกเขาพูดว่า:

คุณไม่ควรเชื่อมั่นว่าสิ่งต่อไปนี้เป็นการสรุปข้อมูลของคุณ คุณควรดูข้อมูลของคุณเองและทำการทดสอบด้วยตนเอง

รหัสที่มาสำหรับการทดสอบซึ่งพวกเขาเรียกใช้ได้ออนไลน์ เนื่องจากรหัสนี้ไม่ทำงานโดยตรงฉันได้ทำการเปลี่ยนแปลงเล็กน้อยซึ่งคุณสามารถรับได้ที่นี่: serialize.py ฉันได้รับผลลัพธ์ต่อไปนี้:

ผลการเปรียบเทียบเวลา

พวกเขายังกล่าวด้วยว่าด้วยการแปลงข้อมูลข้อความเป็นข้อมูลที่เป็นหมวดหมู่การทำให้เป็นอนุกรมนั้นเร็วกว่ามาก ในการทดสอบของพวกเขาประมาณ 10 ครั้งเร็ว (ดูรหัสทดสอบ)

แก้ไข : เวลาในการดองมากกว่า CSV สามารถอธิบายได้ด้วยรูปแบบข้อมูลที่ใช้ โดยค่าเริ่มต้นpickleใช้การแสดง ASCII ที่พิมพ์ได้ซึ่งสร้างชุดข้อมูลที่ใหญ่กว่า ดังที่สามารถเห็นได้จากกราฟอย่างไรก็ตาม pickle ที่ใช้รูปแบบข้อมูลไบนารีใหม่ (รุ่น 2 pickle-p2) มีเวลาในการโหลดน้อยกว่ามาก

ข้อมูลอ้างอิงอื่น ๆ :


1
ฉันอัพเดตคำตอบเพื่ออธิบายคำถามของคุณ ในการสรุป: โดยค่าเริ่มต้นดองจะเก็บข้อมูลในรูปแบบ ASCII
agold

1
ขอบคุณสำหรับคำอธิบายนั้น! ตามที่ทราบแล้วแพนด้า DataFrame .to_pickle ดูเหมือนจะใช้ pkl.HIGHEST_PROTOCOL (ควรเป็น 2)
ntg

2
ดูเหมือนว่าบล็อกที่ลิงก์ไว้ด้านบน ( ลบPanda Store อย่างมีประสิทธิภาพ DataFramesแล้วฉันทำการเปรียบเทียบของตัวเองกับ.to_pickle()(ซึ่งใช้ที่เก็บข้อมูลไบนารี) เทียบกับ.to_hdf()(โดยไม่บีบอัด) เป้าหมายคือความเร็วขนาดไฟล์สำหรับ HDF คือ 11x Pickle และเวลาในการโหลด คือ 5x Pickle ข้อมูลของฉันคือ ~ 5k ไฟล์ของ ~ 7k แถว x 6 คอลัมน์แต่ละส่วนใหญ่เป็นตัวเลข
hamx0r

1
หน้ายังคงมีอยู่คุณเพียงแค่ต้องลบเครื่องหมายทับต่อท้าย: จัดเก็บข้อมูลแพนด้าอย่างมีประสิทธิภาพ DataFrames
IanSR

2
@ ไมค์วิลเลียมสันในการทดสอบของฉันดองได้เร็วกว่าการโหลดมากกว่า HDF 5 เท่าและยังใช้พื้นที่ดิสก์ 1/11 (เช่น hdf มีขนาดใหญ่ขึ้น 11x บนดิสก์และใช้เวลาในการโหลดจากดิสก์เท่าที่ทำได้) นี่คือทั้งหมดใน python 3 กับ pandas 0.22.0
hamx0r

35

ถ้าฉันเข้าใจถูกต้องคุณกำลังใช้อยู่แล้วpandas.read_csv()แต่ต้องการเร่งกระบวนการพัฒนาเพื่อให้คุณไม่ต้องโหลดไฟล์ทุกครั้งที่คุณแก้ไขสคริปต์ใช่ไหม? ฉันมีคำแนะนำเล็กน้อย:

  1. คุณสามารถโหลดไฟล์ CSV เพียงบางส่วนที่ใช้pandas.read_csv(..., nrows=1000)เพื่อโหลดบิตบนสุดของตารางในขณะที่คุณกำลังพัฒนา

  2. ใช้ipythonสำหรับเซสชันแบบโต้ตอบเช่นที่คุณเก็บตารางหมีแพนด้าไว้ในหน่วยความจำในขณะที่คุณแก้ไขและโหลดสคริปต์ของคุณใหม่

  3. แปลง csv เป็นตาราง HDF5

  4. การปรับปรุงการใช้งานDataFrame.to_feather()และpd.read_feather()การเก็บข้อมูลในการวิจัยได้ขนรูปแบบไบนารีที่เป็นซุปเปอร์รวดเร็ว (อยู่ในมือของฉันเล็กน้อยเร็วกว่าpandas.to_pickle()บนข้อมูลที่เป็นตัวเลขและเร็วขึ้นมากกับข้อมูลสตริง)

คุณอาจสนใจคำตอบนี้ใน stackoverflow


คุณรู้หรือไม่ว่าทำไมto_featherจะทำงานได้ดีกับข้อมูลสตริง? ฉันวัดประสิทธิผลto_pickleและto_featureใน dataframe ตัวเลขของฉันและดองเป็นเรื่องเกี่ยวกับ 3x เร็วขึ้น
zyxue

@zyxue คำถามที่ดีฉันสุจริตไม่ได้เล่นมากกับสิ่งที่ขนดังนั้นฉันจึงไม่ได้รับคำตอบ
โนอาห์

20

ผักดองทำงานได้ดี!

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
โปรดทราบว่าไฟล์ที่สร้างขึ้นไม่ใช่ไฟล์ csv อาจเป็นการดีกว่าถ้าใช้นามสกุล.pklตามที่แนะนำใน @Andy Haydens answer
agold

5

คุณสามารถใช้ไฟล์รูปแบบขนนก มันเร็วมาก

df.to_feather('filename.ft')

และข้อมูลสามารถนำมาใช้โดยตรงโดยRใช้featherห้องสมุด
James Hirschorn

4

Pandas DataFrames มีto_pickleฟังก์ชั่นที่มีประโยชน์สำหรับการบันทึก 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

ดังที่กล่าวไปแล้วมีตัวเลือกและรูปแบบไฟล์ที่แตกต่างกัน ( HDF5 , JSON , CSV , parquet , SQL ) เพื่อจัดเก็บกรอบข้อมูล อย่างไรก็ตามpickleไม่ใช่พลเมืองชั้นหนึ่ง (ขึ้นอยู่กับการตั้งค่าของคุณ) เนื่องจาก:

  1. pickleความเสี่ยงด้านความปลอดภัยที่อาจเกิดขึ้น สร้างเอกสาร Python สำหรับการดอง :

คำเตือนpickleโมดูลจะไม่ปลอดภัยกับข้อมูลที่ผิดพลาดหรือสร้างมีเจตนาร้าย อย่าถอนการรวบรวมข้อมูลที่ได้รับจากแหล่งที่ไม่น่าเชื่อถือหรือไม่ได้รับการรับรองความถูกต้อง

  1. pickleช้า ค้นหาที่นี่และที่นี่มาตรฐาน

ขึ้นอยู่กับการตั้งค่า / การใช้งานของคุณทั้งสองข้อ จำกัด ใช้ไม่ได้ แต่ฉันจะไม่แนะนำpickleเป็นค่าเริ่มต้นสำหรับการคงอยู่ของเฟรมข้อมูลแพนด้า


1

รูปแบบไฟล์ค่อนข้างง่ายสำหรับข้อมูลตัวเลข

ฉันชอบที่จะใช้ไฟล์ numpy เพราะมันรวดเร็วและใช้งานง่าย นี่คือมาตรฐานง่ายๆสำหรับการบันทึกและโหลดดาต้าเฟรมที่มี 1 คอลัมน์ 1 ล้านคะแนน

import numpy as np
import pandas as pd

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

ใช้%%timeitฟังก์ชั่นเวทย์มนต์ของ ipython

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

ผลลัพธ์คือ

100 loops, best of 3: 5.97 ms per loop

เพื่อโหลดข้อมูลกลับสู่ dataframe

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

data_df = pd.DataFrame(data)

ผลลัพธ์คือ

100 loops, best of 3: 5.12 ms per loop

ไม่เลว!

ข้อเสีย

มีปัญหาหากคุณบันทึกไฟล์ numpy โดยใช้ python 2 จากนั้นลองเปิดใช้ python 3 (หรือกลับกัน)


6
โปรดทราบว่าวิธีนี้จะลบชื่อคอลัมน์ทั้งหมดของคุณและเปลี่ยนข้อมูลจำนวนเต็มทั้งหมดของคุณเพื่อลอย :(
Joseph Garvin

0

https://docs.python.org/3/library/pickle.html

รูปแบบโปรโตคอล pickle:

โพรโทคอลรุ่น 0 เป็นโปรโตคอลดั้งเดิมที่“ มนุษย์อ่านได้” และเข้ากันได้กับ Python รุ่นก่อนหน้า

Protocol version 1 เป็นรูปแบบไบนารี่แบบเก่าซึ่งสามารถใช้งานร่วมกับ Python รุ่นก่อนหน้าได้

โปรโตคอลรุ่น 2 เปิดตัวใน Python 2.3 มันให้การดองที่มีประสิทธิภาพมากขึ้นของชั้นเรียนรูปแบบใหม่ อ้างถึง PEP 307 สำหรับข้อมูลเกี่ยวกับการปรับปรุงที่นำเสนอโดยโปรโตคอล 2

เพิ่มโพรโทคอลรุ่น 3 ใน Python 3.0 มันมีการสนับสนุนอย่างชัดเจนสำหรับวัตถุไบต์และไม่สามารถยกเลิกการแก้ไขโดย Python 2.x นี่เป็นโปรโตคอลเริ่มต้นและโปรโตคอลที่แนะนำเมื่อต้องการความเข้ากันได้กับรุ่น Python 3 อื่น ๆ

เพิ่มโพรโทคอลรุ่น 4 ใน Python 3.4 มันเพิ่มการสนับสนุนสำหรับวัตถุที่มีขนาดใหญ่มากการเลือกชนิดของวัตถุมากขึ้นและการปรับรูปแบบข้อมูลให้เหมาะสม อ้างถึง PEP 3154 สำหรับข้อมูลเกี่ยวกับการปรับปรุงที่นำเสนอโดยโปรโตคอล 4


0

ความเข้ากันได้ pyarrow ข้ามรุ่น

การเคลื่อนไหวโดยรวมนั้นเป็นการทำที่แคบ / ขนนก แต่ฉันมีความท้าทายกับ pyarrow กับชั่วคราวในสเปคข้อมูลต่อเนื่องกับ pyarrow 0.15.1 ไม่สามารถ deserialized กับ 0.16.0 ลูกศร-7961 ฉันใช้การทำให้เป็นอนุกรมเพื่อใช้ redis ดังนั้นต้องใช้การเข้ารหัสไบนารี

ฉันได้ลองตัวเลือกต่าง ๆ อีกครั้ง (โดยใช้สมุดบันทึก 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")

ด้วยผลลัพธ์ต่อไปนี้สำหรับ data frame ของฉัน (ในoutตัวแปร 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>
=+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+==+=

ขนนกและปาร์เก้ไม่ทำงานกับกรอบข้อมูลของฉัน ฉันจะใช้ pyarrow ต่อไป อย่างไรก็ตามฉันจะเสริมด้วยดอง (ไม่มีการบีบอัด) เมื่อเขียนไปยังแคชร้านค้ารูปแบบ pyarrow และดองต่อเนื่อง เมื่ออ่านจากแคชทางเลือกในการดองถ้า pyram deserialisation ล้มเหลว


สิ่งนี้ไม่ตอบคำถาม
Jason S

0

รูปแบบขึ้นอยู่กับการใช้งานของคุณ

  • บันทึก DataFrame ระหว่างเซสชันโน้ตบุ๊ก - ที่เป็นขนหากคุณคุ้นเคยกับการดอง - ก็ใช้ได้เช่นกัน
  • บันทึก DataFrame ในขนาดไฟล์ที่เล็กที่สุดที่เป็นไปได้ - ปาร์เก้หรือpickle.gz (ตรวจสอบว่ามีอะไรดีกว่าสำหรับข้อมูลของคุณ)
  • บันทึก DataFrame ที่มีขนาดใหญ่มาก (10+ ล้านแถว) - hdf
  • สามารถอ่านข้อมูลบนแพลตฟอร์มอื่น (ไม่ใช่ Python) ที่ไม่รองรับรูปแบบอื่น - csv , csv.gzตรวจสอบว่าสนับสนุนparquet หรือไม่
  • สามารถตรวจสอบด้วยตาของคุณ / ใช้ Excel / Google ชีต / Git diff - csv
  • บันทึก DataFrame ที่ใช้ RAM เกือบทั้งหมด - csv

การเปรียบเทียบรูปแบบไฟล์แพนด้าอยู่ในวิดีโอนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.