การอธิบายโมเดลข้อมูลแพนด้าและข้อดี

การแนะนำ

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 ExtensionArrays โดยทั่วไปเลเยอร์นี้จะถูกส่งไปยังการใช้งานพื้นฐาน เช่น จะใช้ NumPy API หากข้อมูลถูกเก็บไว้ในอาร์เรย์ NumPy pandas เก็บข้อมูลไว้ในนั้นและเรียกใช้วิธีการของมันโดยไม่ทำให้อินเทอร์เฟซสมบูรณ์ คุณสามารถอ่านเกี่ยวกับ pandas ExtensionArrays ได้ "ที่นี่"

โดยปกติแล้วอาร์เรย์ NumPy จะเป็นแบบสองมิติ ซึ่งนำเสนอข้อได้เปรียบด้านประสิทธิภาพมากมาย ซึ่งเราจะพิจารณาในภายหลัง pandas ExtensionArrays ส่วนใหญ่เป็นโครงสร้างข้อมูลแบบมิติเดียว ณ ขณะนี้ สิ่งนี้ทำให้คาดเดาสิ่งต่างๆ ได้มากขึ้น แต่มีข้อเสียบางประการเมื่อดูประสิทธิภาพในชุดการดำเนินการเฉพาะ

ExtensionArrays เปิดใช้งาน dataframes ที่ได้รับการสนับสนุนโดยอาร์เรย์ PyArrow รวมถึง dtypes อื่นๆ

บล็อก

โดยปกติดาต้าเฟรมจะประกอบด้วยคอลัมน์ที่แสดงโดยอาร์เรย์อย่างน้อยหนึ่งรายการ โดยปกติแล้ว คุณจะมีคอลเลกชันของอาร์เรย์เนื่องจากอาร์เรย์หนึ่งสามารถจัดเก็บ dtype เฉพาะได้เพียงชนิดเดียวเท่านั้น อาร์เรย์เหล่านี้จัดเก็บข้อมูลของคุณ แต่ไม่มีข้อมูลใดๆ เกี่ยวกับคอลัมน์ที่อาร์เรย์เหล่านี้เป็นตัวแทน ทุกอาร์เรย์จาก dataframe ของคุณจะถูกห่อด้วย Block ที่สอดคล้องกันหนึ่งรายการ Blocks เพิ่มข้อมูลเพิ่มเติมให้กับอาร์เรย์เหล่านี้ เช่น ตำแหน่งคอลัมน์ที่แสดงโดย Block นี้ Blocks ทำหน้าที่เป็นเลเยอร์รอบๆ อาร์เรย์จริงที่สามารถเพิ่มคุณค่าด้วยวิธีการอรรถประโยชน์ที่จำเป็นสำหรับการดำเนินการของแพนด้า

เมื่อดำเนินการจริงบนดาต้าเฟรม Block จะทำให้แน่ใจได้ว่าเมธอดจะส่งไปยังอาเรย์ต้นแบบ เช่น ถ้าคุณเรียก astype มันจะทำให้แน่ใจว่าการดำเนินการนี้ถูกเรียกบนอาเรย์

เลเยอร์นี้ไม่มีข้อมูลเกี่ยวกับคอลัมน์อื่นๆ ใน dataframe มันเป็นวัตถุเดี่ยว

BlockManager

ตามชื่อที่แนะนำ BlockManager จะประสาน Blocks ทั้งหมดที่เชื่อมต่อกับดาต้าเฟรมเดียว โดยจะเก็บ Blocks เองและข้อมูลเกี่ยวกับแกนของ dataframe ของคุณ เช่น ชื่อคอลัมน์และป้ายกำกับ Index สิ่งสำคัญที่สุดคือ จะส่งการดำเนินการส่วนใหญ่ไปยัง Blocks จริง

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" ที่มีอยู่ของเรา ขณะนี้มีสองวิธีที่เป็นไปได้ในการก้าวไปข้างหน้า:

  1. เพิ่มคอลัมน์ใหม่ลงในอาร์เรย์ที่มีอยู่ซึ่งเก็บคอลัมน์จำนวนเต็ม
  2. สร้างอาร์เรย์ใหม่ที่จัดเก็บเฉพาะคอลัมน์ใหม่เท่านั้น

ตัวเลือกแรกจะต้องเพิ่มคอลัมน์ใหม่ลงในอาร์เรย์ที่มีอยู่ สิ่งนี้จะต้องมีการคัดลอกข้อมูลเนื่องจาก NumPy ไม่รองรับการดำเนินการนี้หากไม่มีการคัดลอก นี่เป็นค่าใช้จ่ายที่สูงชันสำหรับการเพิ่มหนึ่งคอลัมน์

ตัวเลือกที่สองจะเพิ่มอาร์เรย์ที่สามให้กับคอลเลกชันอาร์เรย์ของเรา นอกจากนี้ยังไม่จำเป็นต้องดำเนินการใดๆ เพิ่มเติมอีกด้วย นี่ราคาถูกมาก ตอนนี้เรามี Blocks สองตัวที่เก็บข้อมูลจำนวนเต็ม นี่คือดาต้าเฟรมที่ไม่ถูกรวมเข้าด้วยกัน

ความแตกต่างเหล่านี้ไม่สำคัญมากนักตราบใดที่คุณดำเนินการตามคอลัมน์เท่านั้น จะส่งผลต่อประสิทธิภาพการดำเนินงานของคุณทันทีที่ทำงานบนหลายคอลัมน์ ตัวอย่างเช่น การดำเนินการ axis=1 ใดๆ จะเป็นการย้ายข้อมูลของ dataframe ของคุณ โดยทั่วไปการย้ายข้อมูลจะเป็นศูนย์คัดลอกหากดำเนินการบนดาต้าเฟรมที่สนับสนุนโดยอาร์เรย์ NumPy เดียว สิ่งนี้ไม่เป็นความจริงอีกต่อไปหากทุกคอลัมน์ได้รับการสนับสนุนโดยอาร์เรย์ที่แตกต่างกัน และด้วยเหตุนี้ จะต้องได้รับการลงโทษด้านประสิทธิภาพ

นอกจากนี้ยังจะต้องมีสำเนาเพื่อรับคอลัมน์จำนวนเต็มทั้งหมดจาก dataframe ของคุณเป็นอาร์เรย์ NumPy

df[["a", "c", "new"]].to_numpy()

สิ่งนี้จะสร้างสำเนาเนื่องจากผลลัพธ์จะต้องเก็บไว้ในอาร์เรย์ NumPy เดียว มันจะส่งคืนมุมมองบนดาต้าเฟรมรวม ซึ่งทำให้ราคาถูกมาก

เวอร์ชันก่อนหน้ามักทำให้เกิดการรวมข้อมูลภายในสำหรับวิธีการบางอย่าง ซึ่งจะทำให้ลักษณะการทำงานด้านประสิทธิภาพไม่สามารถคาดเดาได้ วิธีการเช่น reset_index ทำให้เกิดการรวมบัญชีที่ไม่จำเป็นโดยสิ้นเชิง สิ่งเหล่านี้ส่วนใหญ่ถูกลบออกในช่วงสองสามรุ่นล่าสุด

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

บทสรุป

เราได้ดูเบื้องหลังของดาต้าเฟรมแพนด้าโดยย่อ เราได้เรียนรู้ว่า Blocks และ BlockManagers คืออะไร และพวกมันประสาน dataframe ของคุณอย่างไร ข้อกำหนดเหล่านี้จะพิสูจน์ได้ว่ามีคุณค่าเมื่อเราดูเบื้องหลังของ "Copy-on-Write"

ขอบคุณสำหรับการอ่าน.