วิธีการประมาณว่า DataFrame ของแพนด้าต้องการหน่วยความจำเท่าใด


126

ฉันสงสัยว่า ... ถ้าฉันกำลังอ่านให้พูดว่าไฟล์ csv 400MB ลงในดาต้าเฟรมแพนด้า (โดยใช้ read_csv หรือ read_table) มีวิธีใดที่จะคาดเดาได้ว่าจะต้องใช้หน่วยความจำเท่าไหร่? แค่พยายามทำให้รู้สึกดีขึ้นของเฟรมข้อมูลและหน่วยความจำ ...


คุณสามารถดูกระบวนการได้ตลอดเวลาและเป็นการใช้หน่วยความจำสำหรับไฟล์เดียว หากคุณกำลังใช้ลินุกซ์ลองtopแล้วShift + Mจะเรียงลำดับการใช้งานหน่วยความจำของฉัน
JayQuerie.com

ผมรู้สึกว่าผมควรโฆษณานี้ปัญหาแพนด้าเปิด
Andy Hayden

3
ฉันมีดาต้าเฟรมขนาดใหญ่ที่มี 4 ล้านแถว ฉันค้นพบว่าชุดย่อยว่างนั้นx=df.loc[[]]ใช้เวลาไม่0.1กี่วินาทีในการคำนวณ (เพื่อแยกแถวที่เป็นศูนย์) และยิ่งไปกว่านั้นยังใช้หน่วยความจำหลายร้อยเมกะไบต์เช่นเดียวกับดาต้าเฟรมดั้งเดิมอาจเป็นเพราะการคัดลอกบางส่วนที่อยู่ด้านล่าง
OSA

ลิงก์ใหม่สำหรับโพสต์เก่าโดยผู้พัฒนาหลักของแพนด้า
saladi

คำตอบ:


98

df.memory_usage() จะส่งคืนจำนวนคอลัมน์ที่ใช้:

>>> df.memory_usage()

Row_ID            20906600
Household_ID      20906600
Vehicle           20906600
Calendar_Year     20906600
Model_Year        20906600
...

index=Trueหากต้องการให้มีการจัดทำดัชนีผ่าน

ดังนั้นเพื่อรับการใช้หน่วยความจำโดยรวม:

>>> df.memory_usage(index=True).sum()
731731000

นอกจากนี้การส่งผ่านdeep=Trueจะเปิดใช้งานรายงานการใช้หน่วยความจำที่แม่นยำยิ่งขึ้นซึ่งจะอธิบายถึงการใช้งานวัตถุที่มีอยู่ทั้งหมด

เนื่องจากการใช้หน่วยความจำไม่รวมหน่วยความจำที่ใช้โดยองค์ประกอบที่ไม่ใช่ส่วนประกอบของอาร์เรย์ if deep=False(กรณีเริ่มต้น)


1
ผลรวมของการใช้หน่วยความจำของคอลัมน์ทั้งหมดมีผลต่อการใช้หน่วยความจำจริงหรือ? ฉันนึกภาพออกว่าจะมีค่าใช้จ่ายมากกว่านี้
firelynx

14
คุณก็ต้องการเช่นกันdeep=True
smci

ผลรวมของ df.memory_usage () ไม่เท่ากับ sys.getsizeof (df)! มีค่าโสหุ้ยมากมาย ดังที่ smci กล่าวไว้คุณต้องdeep=True
vagabond

11
FYI memory_usage()ส่งคืนการใช้หน่วยความจำเป็นไบต์ (ตามที่คุณคาดหวัง)
engelen

2
ทำไมความแตกต่างอย่างมากระหว่าง with / without deep = True?
al

83

นี่คือการเปรียบเทียบวิธีการต่างๆ - sys.getsizeof(df)ง่ายที่สุด

สำหรับตัวอย่างนี้ dfคือ dataframe ที่มี 814 แถว 11 คอลัมน์ (2 ints 9 วัตถุ) - อ่านจาก shapefile 427kb

sys.getsizeof (DF)

>>> ระบบนำเข้า
>>> sys.getsizeof (df)
(ให้ผลลัพธ์เป็นไบต์)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(แสดงรายการแต่ละคอลัมน์ที่ 8 ไบต์ / แถว)

>>> df.memory_usage (). sum ()
71712
(แถวโดยประมาณ * cols * 8 ไบต์)

>>> df.memory_usage (ลึก = จริง)
(แสดงรายการการใช้หน่วยความจำเต็มของแต่ละคอลัมน์)

>>> df.memory_usage (ลึก = จริง) .sum ()
(ให้ผลลัพธ์เป็นไบต์)
462432

df.info ()

พิมพ์ข้อมูล dataframe ไปยัง stdout ในทางเทคนิคแล้วสิ่งเหล่านี้คือ kibibytes (KiB) ไม่ใช่กิโลไบต์ - ดังที่ docstring กล่าวว่า "การใช้หน่วยความจำแสดงในหน่วยที่มนุษย์อ่านได้ (การแทนค่าฐาน 2)" เพื่อให้ได้ไบต์จะคูณด้วย 1024 เช่น 451.6 KiB = 462,438 ไบต์

>>> df.info ()
...
การใช้หน่วยความจำ: 70.0+ KB

>>> df.info (memory_usage = 'ลึก')
...
การใช้หน่วยความจำ: 451.6 KB

วัตถุหรือโมดูลใดที่g โค้ดด้านบนอ้างถึง
zozo

@zozo woops - เป็นการพิมพ์ผิด - แก้ไขแล้ว
Brian Burns

2
ผมใช้df.info(memory_usage="deep")ก็จะส่งกลับ "392.6 MB" ในขณะที่sys.getsizeof(df)และdf.memory_usage(index=True, deep=True).sum()ทั้งผลตอบแทนโดยประมาณ "411718016" (~ 411MB) คุณช่วยอธิบายได้ไหมว่าเหตุใดผลลัพธ์ทั้ง 3 รายการจึงไม่สอดคล้องกัน ขอบคุณ
Catbuilts

2
@BrianBurns: df.memory_usage(deep=True).sum()ผลตอบแทนเกือบเท่ากันกับdf.memory_usage(index=True, deep=True).sum(). ในกรณีของฉันindexหน่วยความจำไม่ใช้เวลามาก ที่น่าสนใจพอฉันพบว่า411718016/1024/1024 = 392.6ดังนั้น df.info(memory_usage="deep")อาจใช้2^10การแปลงไบต์เป็นMBซึ่งทำให้ฉันสับสน ขอบคุณสำหรับความช่วยเหลือของคุณ: D.
Catbuilts

1
@Catbuilts อ่าอธิบายเลย! df.infoกำลังส่งคืน mebibytes (2 ^ 10) ไม่ใช่เมกะไบต์ (10 ^ 6) - จะแก้ไขคำตอบ
Brian Burns

43

ฉันคิดว่าฉันจะนำข้อมูลเพิ่มเติมมาสู่การอภิปราย

ฉันทำการทดสอบหลายชุดเกี่ยวกับปัญหานี้

เมื่อใช้resourceแพ็คเกจpython ฉันได้รับการใช้หน่วยความจำในกระบวนการของฉัน

และด้วยการเขียน csv ลงในStringIOบัฟเฟอร์ฉันสามารถวัดขนาดเป็นไบต์ได้อย่างง่ายดาย

ฉันทำการทดลองสองครั้งแต่ละรายการสร้างดาต้าเฟรม 20 ขนาดที่มีขนาดเพิ่มขึ้นระหว่าง 10,000 บรรทัดถึง 1,000,000 บรรทัด ทั้งที่มี 10 คอลัมน์

ในการทดลองครั้งแรกฉันใช้เฉพาะการลอยตัวในชุดข้อมูลของฉัน

นี่คือวิธีที่หน่วยความจำเพิ่มขึ้นเมื่อเทียบกับไฟล์ csv ซึ่งเป็นฟังก์ชันของจำนวนบรรทัด (ขนาดเป็นเมกะไบต์)

หน่วยความจำและขนาด CSV เป็นเมกะไบต์เป็นฟังก์ชันของจำนวนแถวที่มีรายการลอย

การทดลองครั้งที่สองฉันมีแนวทางเดียวกัน แต่ข้อมูลในชุดข้อมูลประกอบด้วยสตริงสั้น ๆ เท่านั้น

หน่วยความจำและขนาด CSV เป็นเมกะไบต์เป็นฟังก์ชันของจำนวนแถวที่มีรายการสตริง

ดูเหมือนว่าความสัมพันธ์ของขนาด csv และขนาดของ dataframe จะแตกต่างกันค่อนข้างมาก แต่ขนาดในหน่วยความจำจะใหญ่ขึ้นเสมอโดยปัจจัย 2-3 (สำหรับขนาดเฟรมในการทดลองนี้)

ฉันชอบที่จะตอบคำถามนี้ให้สมบูรณ์ด้วยการทดลองเพิ่มเติมโปรดแสดงความคิดเห็นว่าคุณต้องการให้ฉันลองอะไรเป็นพิเศษ


แกน y ของคุณคืออะไร?
Ilya V.Schurov

1
max_rss และขนาด csv บนดิสก์เป็นเมกะไบต์
firelynx

31

คุณต้องทำสิ่งนี้ในทางกลับกัน

In [4]: DataFrame(randn(1000000,20)).to_csv('test.csv')

In [5]: !ls -ltr test.csv
-rw-rw-r-- 1 users 399508276 Aug  6 16:55 test.csv

หน่วยความจำทางเทคนิคเกี่ยวกับสิ่งนี้ (ซึ่งรวมถึงดัชนี)

In [16]: df.values.nbytes + df.index.nbytes + df.columns.nbytes
Out[16]: 168000160

ดังนั้นหน่วยความจำ 168MB พร้อมไฟล์ 400MB 1M แถว 20 คอลัมน์ลอย

DataFrame(randn(1000000,20)).to_hdf('test.h5','df')

!ls -ltr test.h5
-rw-rw-r-- 1 users 168073944 Aug  6 16:57 test.h5

กะทัดรัดกว่ามากเมื่อเขียนเป็นไฟล์ HDF5 แบบไบนารี

In [12]: DataFrame(randn(1000000,20)).to_hdf('test.h5','df',complevel=9,complib='blosc')

In [13]: !ls -ltr test.h5
-rw-rw-r-- 1 users 154727012 Aug  6 16:58 test.h5

ข้อมูลเป็นแบบสุ่มดังนั้นการบีบอัดจึงไม่ช่วยอะไรมากเกินไป


ที่ฉลาดมาก! มีความคิดอย่างไรในการวัดหน่วยความจำที่คุณต้องการอ่านไฟล์โดยใช้read_csv?
Andy Hayden

ไม่มีความคิดที่จะวัดตามที่คุณอ่าน IIRC สามารถเป็นหน่วยความจำสุดท้ายที่จำเป็นในการเก็บข้อมูลได้มากถึง 2 เท่า (จากบทความของ wes) แต่ฉันคิดว่าเขานำมันลงสู่หน่วยความจำคงที่ + สุดท้าย
เจฟฟ์

อ่าฉันต้องอ่านซ้ำฉันจำได้ว่า 2x เป็นขั้นต่ำทางทฤษฎีที่สะดวกสำหรับอัลกอริทึมบางอย่างถ้ามันยิ่งน้อยไป
Andy Hayden

คุณสามารถใช้iotoplike top/ htopสำหรับการรับชม (ตามเวลาจริง) ประสิทธิภาพ IO
Phillip Cloud

1
nbytesจะเป็นการประเมินที่ต่ำเกินไปหากคุณมีเช่นสตริงในดาต้าเฟรม
osa

10

หากคุณรู้จักdtypeอาร์เรย์ของคุณคุณสามารถคำนวณจำนวนไบต์ที่จะใช้ในการจัดเก็บข้อมูลของคุณได้โดยตรง + บางส่วนสำหรับออบเจ็กต์ Python เอง แอตทริบิวต์การใช้งานของอาร์เรย์คือnumpy nbytesคุณสามารถหาจำนวนไบต์จากอาร์เรย์ในแพนด้าได้DataFrameโดยทำ

nbytes = sum(block.values.nbytes for block in df.blocks.values())

objectอาร์เรย์ dtype จัดเก็บ 8 ไบต์ต่อออบเจ็กต์ (อาร์เรย์ dtype ของอ็อบเจ็กต์เก็บตัวชี้เป็นทึบแสงPyObject) ดังนั้นหากคุณมีสตริงใน csv ของคุณคุณต้องคำนึงถึงสิ่งread_csvเหล่านั้นที่จะเปลี่ยนเป็นobjectอาร์เรย์ dtype และปรับการคำนวณของคุณตามนั้น

แก้ไข:

ดูnumpyหน้าประเภทสเกลาร์สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับobject dtype. เนื่องจากมีการจัดเก็บเฉพาะข้อมูลอ้างอิงคุณจึงต้องคำนึงถึงขนาดของวัตถุในอาร์เรย์ด้วย ตามที่กล่าวไว้ในหน้านั้นอาร์เรย์อ็อบเจ็กต์ค่อนข้างคล้ายกับอlistอบเจ็กต์Python


ขอบคุณฟิลลิป! เพียงเพื่อชี้แจง - สำหรับสตริงเราต้องการ 8 ไบต์สำหรับตัวชี้ไปยังวัตถุสตริงบวกกับวัตถุสตริงจริงหรือไม่?
Anne

1
ใช่สำหรับวัตถุทุกประเภทคุณจะต้องมีตัวชี้ขนาด 8 ไบต์ + ขนาด (วัตถุ)
Viktor Kerkez

1
แนะนำ df.blocks.values ​​() ดูเหมือนว่าตอนนี้ df.blocks จะเป็น dict
MRocklin

8

ใช่มี. หมีแพนด้าจะจัดเก็บข้อมูลของคุณในndarrayโครงสร้างตัวเลข2 มิติโดยจัดกลุ่มตาม dtypes ndarrayโดยพื้นฐานแล้วคืออาร์เรย์ข้อมูลดิบ C ที่มีส่วนหัวขนาดเล็ก คุณสามารถประมาณขนาดได้โดยคูณขนาดของไฟล์dtypeมันที่มีกับขนาดของอาร์เรย์

ตัวอย่างเช่นหากคุณมี 1,000 แถวที่มี 2 np.int32และ 5 np.float64คอลัมน์ DataFrame ของคุณจะมีnp.int32อาร์เรย์2x1000 หนึ่งชุดและnp.float64อาร์เรย์5x1000 หนึ่งชุดซึ่ง ได้แก่ :

4 ไบต์ * 2 * 1,000 + 8 ไบต์ * 5 * 1000 = 48000 ไบต์


@AndyHayden ค่าก่อสร้างคุณหมายถึงอะไร? ขนาดของอินสแตนซ์ของDataFrame?
Phillip Cloud

ขอบคุณวิคเตอร์! @ แอนดี้ - คิดว่าค่าก่อสร้างสูงแค่ไหน?
แอน

ไม่รวมถึง แต่pandasมีการใช้งานread_tableใน Cython ที่มีประสิทธิภาพมาก(ดีกว่า loadtxt ของ numpy มาก) ดังนั้นฉันจึงถือว่ามันแยกวิเคราะห์และจัดเก็บข้อมูลลงในไฟล์ndarray.
Viktor Kerkez

@PhillipCloud คุณต้องสร้างมันขึ้นมาซึ่งต้องใช้ความจำ .. ฉันดูเหมือนจะจำได้สองเท่าของขนาดที่กล่าวถึง? ...
Andy Hayden

6

สิ่งนี้ฉันเชื่อว่าสิ่งนี้ทำให้ขนาดในหน่วยความจำวัตถุใด ๆ ใน python จำเป็นต้องตรวจสอบภายในโดยคำนึงถึงแพนด้าและจำนวนนับ

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.