การอธิบายโมเดลข้อมูลแพนด้าและข้อดี
การแนะนำ
pandas ช่วยให้คุณสามารถเลือกระหว่างอาร์เรย์ประเภทต่างๆ เพื่อแสดงข้อมูลของ dataframe ของคุณได้ ในอดีต dataframes ส่วนใหญ่ได้รับการสนับสนุนโดยอาร์เรย์ NumPy pandas 2.0 แนะนำตัวเลือกในการใช้อาร์เรย์ PyArrow เป็นรูปแบบการจัดเก็บข้อมูล นอกจากนี้ ยังมีชั้นกลางระหว่างอาร์เรย์เหล่านี้กับ dataframe ของคุณ Block
และ BlockManager
เราจะมาดูกันว่าเลเยอร์นี้ประสานอาร์เรย์ต่างๆ อย่างไร โดยพื้นฐานแล้วมีอะไรอยู่เบื้องหลัง pd.DataFrame()
เราจะพยายามตอบทุกคำถามที่คุณอาจมีเกี่ยวกับระบบภายในของแพนด้า
โพสต์นี้จะแนะนำคำศัพท์บางอย่างที่จำเป็นเพื่อทำความเข้าใจวิธีการทำงานของ Copy-on-Write ซึ่งฉันจะเขียนเกี่ยวกับต่อไป
ฉันเป็นสมาชิกของทีมหลักของแพนด้าที่ทำงานเกี่ยวกับระบบภายใน เหนือสิ่งอื่นใด ขณะนี้ฉันกำลังทำงานที่ "Coiled" โดยเน้นที่ "Dask"
โครงสร้างข้อมูลหมีแพนด้า
โดยปกติแล้ว dataframe จะได้รับการสนับสนุนจากอาร์เรย์บางตัว เช่น อาร์เรย์ NumPy หรือ pandas ExtensionArray
อาร์เรย์เหล่านี้เก็บข้อมูลของดาต้าเฟรม pandas เพิ่มเลเยอร์กลางที่เรียกว่า Block
และ BlockManager
ที่ประสานอาร์เรย์เหล่านี้เพื่อให้การดำเนินการมีประสิทธิภาพมากที่สุดเท่าที่จะเป็นไปได้ นี่คือเหตุผลหนึ่งว่าทำไมวิธีการทำงานบนหลายคอลัมน์จึงรวดเร็วมากในแพนด้า มาดูรายละเอียดของเลเยอร์เหล่านี้กันดีกว่า
อาร์เรย์
ข้อมูลจริงของ dataframe สามารถจัดเก็บไว้ในชุดของอาร์เรย์ NumPy หรือ pandas ExtensionArray
s โดยทั่วไปเลเยอร์นี้จะถูกส่งไปยังการใช้งานพื้นฐาน เช่น จะใช้ NumPy API หากข้อมูลถูกเก็บไว้ในอาร์เรย์ NumPy pandas เก็บข้อมูลไว้ในนั้นและเรียกใช้วิธีการของมันโดยไม่ทำให้อินเทอร์เฟซสมบูรณ์ คุณสามารถอ่านเกี่ยวกับ pandas ExtensionArray
s ได้ "ที่นี่"
โดยปกติแล้วอาร์เรย์ NumPy จะเป็นแบบสองมิติ ซึ่งนำเสนอข้อได้เปรียบด้านประสิทธิภาพมากมาย ซึ่งเราจะพิจารณาในภายหลัง pandas ExtensionArray
s ส่วนใหญ่เป็นโครงสร้างข้อมูลแบบมิติเดียว ณ ขณะนี้ สิ่งนี้ทำให้คาดเดาสิ่งต่างๆ ได้มากขึ้น แต่มีข้อเสียบางประการเมื่อดูประสิทธิภาพในชุดการดำเนินการเฉพาะ
ExtensionArray
s เปิดใช้งาน dataframes ที่ได้รับการสนับสนุนโดยอาร์เรย์ PyArrow รวมถึง dtypes อื่นๆ
บล็อก
โดยปกติดาต้าเฟรมจะประกอบด้วยคอลัมน์ที่แสดงโดยอาร์เรย์อย่างน้อยหนึ่งรายการ โดยปกติแล้ว คุณจะมีคอลเลกชันของอาร์เรย์เนื่องจากอาร์เรย์หนึ่งสามารถจัดเก็บ dtype เฉพาะได้เพียงชนิดเดียวเท่านั้น อาร์เรย์เหล่านี้จัดเก็บข้อมูลของคุณ แต่ไม่มีข้อมูลใดๆ เกี่ยวกับคอลัมน์ที่อาร์เรย์เหล่านี้เป็นตัวแทน ทุกอาร์เรย์จาก dataframe ของคุณจะถูกห่อด้วย Block
ที่สอดคล้องกันหนึ่งรายการ Block
s เพิ่มข้อมูลเพิ่มเติมให้กับอาร์เรย์เหล่านี้ เช่น ตำแหน่งคอลัมน์ที่แสดงโดย Block
นี้ Block
s ทำหน้าที่เป็นเลเยอร์รอบๆ อาร์เรย์จริงที่สามารถเพิ่มคุณค่าด้วยวิธีการอรรถประโยชน์ที่จำเป็นสำหรับการดำเนินการของแพนด้า
เมื่อดำเนินการจริงบนดาต้าเฟรม Block
จะทำให้แน่ใจได้ว่าเมธอดจะส่งไปยังอาเรย์ต้นแบบ เช่น ถ้าคุณเรียก astype
มันจะทำให้แน่ใจว่าการดำเนินการนี้ถูกเรียกบนอาเรย์
เลเยอร์นี้ไม่มีข้อมูลเกี่ยวกับคอลัมน์อื่นๆ ใน dataframe มันเป็นวัตถุเดี่ยว
BlockManager
ตามชื่อที่แนะนำ BlockManager
จะประสาน Block
s ทั้งหมดที่เชื่อมต่อกับดาต้าเฟรมเดียว โดยจะเก็บ Block
s เองและข้อมูลเกี่ยวกับแกนของ dataframe ของคุณ เช่น ชื่อคอลัมน์และป้ายกำกับ Index
สิ่งสำคัญที่สุดคือ จะส่งการดำเนินการส่วนใหญ่ไปยัง Block
s จริง
df.replace(...)
BlockManager
ช่วยให้มั่นใจได้ว่า replace
จะถูกดำเนินการในทุก ๆ Block
dataframe รวมคืออะไร?
เรากำลังสมมติว่า dataframes ได้รับการสนับสนุนโดย NumPy dtypes เช่นข้อมูลสามารถจัดเก็บเป็นอาร์เรย์สองมิติได้
เมื่อมีการสร้างดาต้าเฟรม แพนด้าส่วนใหญ่จะตรวจสอบให้แน่ใจว่ามี Block
เพียงหนึ่งรายการต่อ dtype
df = pd.DataFrame( { "a": [1, 2, 3], "b": [1.5, 2.5, 3.5], "c": [10, 11, 12], "d": [10.5, 11.5, 12.5], } )
dataframe นี้มีสี่คอลัมน์ที่แสดงโดยสองอาร์เรย์: หนึ่งในอาร์เรย์เก็บประเภท d จำนวนเต็มในขณะที่อีกประเภทเก็บประเภท float นี่คือดาต้าเฟรมรวม
ตอนนี้ มาเพิ่มคอลัมน์ใหม่ให้กับ dataframe นี้:
df["new"] = 100
ซึ่งจะมีประเภทเดียวกับคอลัมน์ "a"
และ "c"
ที่มีอยู่ของเรา ขณะนี้มีสองวิธีที่เป็นไปได้ในการก้าวไปข้างหน้า:
- เพิ่มคอลัมน์ใหม่ลงในอาร์เรย์ที่มีอยู่ซึ่งเก็บคอลัมน์จำนวนเต็ม
- สร้างอาร์เรย์ใหม่ที่จัดเก็บเฉพาะคอลัมน์ใหม่เท่านั้น
ตัวเลือกแรกจะต้องเพิ่มคอลัมน์ใหม่ลงในอาร์เรย์ที่มีอยู่ สิ่งนี้จะต้องมีการคัดลอกข้อมูลเนื่องจาก NumPy ไม่รองรับการดำเนินการนี้หากไม่มีการคัดลอก นี่เป็นค่าใช้จ่ายที่สูงชันสำหรับการเพิ่มหนึ่งคอลัมน์
ตัวเลือกที่สองจะเพิ่มอาร์เรย์ที่สามให้กับคอลเลกชันอาร์เรย์ของเรา นอกจากนี้ยังไม่จำเป็นต้องดำเนินการใดๆ เพิ่มเติมอีกด้วย นี่ราคาถูกมาก ตอนนี้เรามี Block
s สองตัวที่เก็บข้อมูลจำนวนเต็ม นี่คือดาต้าเฟรมที่ไม่ถูกรวมเข้าด้วยกัน
ความแตกต่างเหล่านี้ไม่สำคัญมากนักตราบใดที่คุณดำเนินการตามคอลัมน์เท่านั้น จะส่งผลต่อประสิทธิภาพการดำเนินงานของคุณทันทีที่ทำงานบนหลายคอลัมน์ ตัวอย่างเช่น การดำเนินการ axis=1
ใดๆ จะเป็นการย้ายข้อมูลของ dataframe ของคุณ โดยทั่วไปการย้ายข้อมูลจะเป็นศูนย์คัดลอกหากดำเนินการบนดาต้าเฟรมที่สนับสนุนโดยอาร์เรย์ NumPy เดียว สิ่งนี้ไม่เป็นความจริงอีกต่อไปหากทุกคอลัมน์ได้รับการสนับสนุนโดยอาร์เรย์ที่แตกต่างกัน และด้วยเหตุนี้ จะต้องได้รับการลงโทษด้านประสิทธิภาพ
นอกจากนี้ยังจะต้องมีสำเนาเพื่อรับคอลัมน์จำนวนเต็มทั้งหมดจาก dataframe ของคุณเป็นอาร์เรย์ NumPy
df[["a", "c", "new"]].to_numpy()
สิ่งนี้จะสร้างสำเนาเนื่องจากผลลัพธ์จะต้องเก็บไว้ในอาร์เรย์ NumPy เดียว มันจะส่งคืนมุมมองบนดาต้าเฟรมรวม ซึ่งทำให้ราคาถูกมาก
เวอร์ชันก่อนหน้ามักทำให้เกิดการรวมข้อมูลภายในสำหรับวิธีการบางอย่าง ซึ่งจะทำให้ลักษณะการทำงานด้านประสิทธิภาพไม่สามารถคาดเดาได้ วิธีการเช่น reset_index
ทำให้เกิดการรวมบัญชีที่ไม่จำเป็นโดยสิ้นเชิง สิ่งเหล่านี้ส่วนใหญ่ถูกลบออกในช่วงสองสามรุ่นล่าสุด
โดยสรุป โดยทั่วไปแล้ว dataframe ที่รวมไว้จะดีกว่าข้อมูลที่ไม่ถูกรวมไว้ แต่ความแตกต่างนั้นขึ้นอยู่กับประเภทของการดำเนินการที่คุณต้องการดำเนินการเป็นอย่างมาก
บทสรุป
เราได้ดูเบื้องหลังของดาต้าเฟรมแพนด้าโดยย่อ เราได้เรียนรู้ว่า Blocks
และ BlockManagers
คืออะไร และพวกมันประสาน dataframe ของคุณอย่างไร ข้อกำหนดเหล่านี้จะพิสูจน์ได้ว่ามีคุณค่าเมื่อเราดูเบื้องหลังของ "Copy-on-Write"
ขอบคุณสำหรับการอ่าน.