Courses
หากใช้ Python วิเคราะห์ข้อมูลเป็นประจำ เช่นเดียวกับที่ทำกันอยู่บ่อย การอ่านไฟล์ CSV คือหนึ่งในงานที่พบบ่อยที่สุด แต่เมื่อชุดข้อมูลมีขนาดใหญ่ขึ้น วิธีนี้อาจช้าลงหรือกินหน่วยความจำมากขึ้น
Polars คือไลบรารี DataFrame ที่ทันสมัยและรวดเร็วสำหรับ Python ออกแบบมาเป็นทางเลือกสมรรถนะสูงแทน Pandas จัดการชุดข้อมูลขนาดใหญ่ได้ลื่นไหลกว่ามาก เพราะสร้างขึ้นโดยมุ่งเน้นความเร็วและการใช้หน่วยความจำต่ำ
ฟังก์ชันหลักของ Polars pl.read_csv() ให้วิธีง่าย ๆ ในการโหลดไฟล์ CSV เข้าสู่ DataFrame พร้อมตัวเลือกในตัวเพื่อควบคุมการพาร์ส ชนิดข้อมูล และการใช้หน่วยความจำ
ในคู่มือนี้ จะแสดงวิธีการอ่านไฟล์ CSV ควบคุมการพาร์ส และจัดการชุดข้อมูลขนาดใหญ่ด้วยฟังก์ชัน pl.read_csv() ของ Polars หากเพิ่งเริ่มต้น แนะนำคอร์ส Introduction to Polars เพื่อเรียนรู้การจัดการข้อมูลและสกัดอินไซต์ด้วย Polars
การใช้งานพื้นฐานของ pl.read_csv()
ก่อนจะไปต่อ ลองดูบทช่วยสอน Python Polars เพื่อเรียนรู้การตั้งค่าสภาพแวดล้อม
มาดูกันว่าฟังก์ชัน pl.read_csv() ทำงานอย่างไร:
# import the module
import polars as pl
# Load CSV into a Polars DataFrame
fuel_data = pl.read_csv("Fuel_Consumption_2000-2022.csv")
# Preview first few rows
print(fuel_data.head())
ในตัวอย่างข้างต้น Polars จะอ่านไฟล์และโหลดเข้าเป็น DataFrame ฟังก์ชันจะส่งคืน Polars DataFrame ซึ่งเป็นโครงสร้างข้อมูลแบบตาราง คล้ายกับที่ได้ใน pandas
โดยค่าเริ่มต้น pl.read_csv():
-
ถือว่าแถวแรกเป็นชื่อตารางคอลัมน์ (
has_header=True) -
เดาชนิดข้อมูลของคอลัมน์โดยอัตโนมัติ
-
ใช้เครื่องหมายจุลภาค (
,) เป็นตัวคั่น -
อ่านทั้งไฟล์เข้าสู่หน่วยความจำ
พารามิเตอร์ที่พบบ่อยใน pl.read_csv()
เมื่อทราบแล้วว่า pl.read_csv() โหลดข้อมูลอย่างไร มาดูว่าพารามิเตอร์ต่อไปนี้ช่วยปรับวิธีที่ Polars พาร์สข้อมูลได้อย่างไร
พาธไฟล์และแหล่งข้อมูล
Polars อนุญาตให้โหลดข้อมูลจากแหล่งต่าง ๆ ได้ สามารถส่งพาธแบบสตริงภายในเครื่อง อ็อบเจ็กต์ Pathlib หรือแม้แต่ URL ตัวอย่างโค้ดต่อไปนี้อ่านไฟล์ csv ขนาดใหญ่จากเว็บ ที่มีข้อมูล GDP ของประเทศต่าง ๆ ในหลายปี
# Reading from a URL
url = "https://raw.githubusercontent.com/datasets/gdp/master/data/gdp.csv"
gdp_data = pl.read_csv(url)
ตัวคั่นและตัวแยก
ไม่ใช่ทุกไฟล์ CSV จะใช้จุลภาค สามารถใช้พารามิเตอร์ separator เพื่อรองรับแท็บ เซมิโคลอน ไพพ์ หรืออักขระอื่น ๆ ตัวอย่างด้านล่างแสดงวิธีกำหนดตัวคั่นเมื่ออ่านไฟล์
# Reading a Semicolon-separated file
sales_data = pl.read_csv("2024_sales.csv", separator=";")
# Reading a Tab-separated file (TSV)
orders_data = pl.read_csv("all_orders.tsv", separator="\t")
การจัดการเฮดเดอร์
ดังที่เห็นก่อนหน้า Polars จะถือว่าแถวแรกของข้อมูลเป็นชื่อคอลัมน์ หากไฟล์ไม่มีแถวเฮดเดอร์ ให้ใช้พารามิเตอร์ has_header=False ตามด้านล่าง Polars จะกำหนดชื่อคอลัมน์ให้โดยอัตโนมัติ เช่น column_1, column_2, column_3 เป็นต้น
# Load file without a header
orders_data = pl.read_csv("all_orders.csv", has_header=False)
ยังสามารถเปลี่ยนชื่อคอลัมน์โดยระบุชื่อคอลัมน์ที่ต้องการได้ เช่น:
# Providing specific column names
sales_data = pl.read_csv("sales_April.csv", new_columns=[
"OrderDate", "OrderNumber", "ProductKey", "SalespersonKey", "Salesperson"])
การเข้ารหัสอักขระ
ไฟล์ CSV อาจใช้การเข้ารหัสข้อความต่างกัน หากพบอักขระประหลาดหรือข้อผิดพลาด ให้ระบุการเข้ารหัส:
# Load CSV into a Polars DataFrame
fuel_data = pl.read_csv("Fuel_Consumption_2000-2022.csv", encoding="utf8")
ตัวเลือกการเข้ารหัสที่พบบ่อยอื่น ๆ ได้แก่ ”latin1” และ ”utf8-lossy” ซึ่งรองรับอักขระไม่ถูกต้องได้เหมาะสม
วิธีเลือกคอลัมน์ขณะอ่าน CSV ใน Polars
เมื่อทำงานกับไฟล์ CSV ขนาดใหญ่ มักไม่จำเป็นต้องใช้ทุกคอลัมน์ Polars อนุญาตให้โหลดเฉพาะคอลัมน์ที่ต้องการด้วยพารามิเตอร์ columns
# Load only specific columns
fuel_data = pl.read_csv("Fuel_Consumption_2000-2022.csv",
columns=["YEAR", "MAKE", "MODEL"]
)
การเลือกเฉพาะคอลัมน์ที่ต้องการตั้งแต่ตอนโหลดไฟล์ ช่วยลดการใช้หน่วยความจำเพราะไม่ต้องโหลดข้อมูลที่ไม่จำเป็น วิธีนี้ยังช่วยเร่งการอ่านไฟล์และปรับปรุงประสิทธิภาพของไปป์ไลน์โดยรวม
วิธีจัดการชนิดข้อมูล (สคีมา) ใน Polars CSV
Polars เป็นไลบรารีที่มีการกำหนดชนิดข้อมูลอย่างเข้มงวด หมายความว่าทุกคอลัมน์ต้องมีชนิดข้อมูลที่สอดคล้องกัน เช่น ทั้งหมดเป็นจำนวนเต็มหรือทั้งหมดเป็นสตริง
การเดาชนิดข้อมูลอัตโนมัติ
โดยค่าเริ่มต้น Polars จะตรวจสอบข้อมูลและกำหนดชนิดคอลัมน์ให้อัตโนมัติ วิธีเริ่มต้นนี้ใช้ได้ดีในหลายกรณี แต่บางครั้งอาจตีความคอลัมน์ผิด เช่น มอง ID เป็นจำนวนเต็มแทนที่จะเป็นสตริง
การกำหนดสคีมาเอง
เมื่ออยากให้ DataFrame มีความสม่ำเสมอ ควรกำหนดสคีมาด้วยตนเอง วิธีนี้ทำให้สอดคล้องกันข้ามหลายไฟล์ และช่วยหลีกเลี่ยงข้อผิดพลาดที่เกี่ยวข้องกับชนิดข้อมูลในกระบวนการถัดไป
ในกรณีส่วนใหญ่ ให้ใช้ schema_overrides เพื่อระบุชนิดข้อมูลของคอลัมน์บางคอลัมน์ ขณะที่ปล่อยให้ Polars เดาที่เหลือ
import polars as pl
# Manually overriding specific data types
empl_data = pl.read_csv(
"all_employees.csv",
schema_overrides={
"id": pl.Int64,
"name": pl.String,
"age": pl.Int64,
"salary": pl.Float64
}
)
หรือใช้ schema เพื่อกำหนดโครงสร้างของ DataFrame ทั้งหมด
# Manually define the full schema
empl_data = pl.read_csv(
"all_employees.csv",
schema={
"id": pl.Int64,
"name": pl.String,
"age": pl.Int64,
"salary": pl.Float64
}
)
วิธีจัดการค่าที่ขาดหาย ใน Polars CSV
โดยค่าเริ่มต้น Polars จะตรวจจับค่าที่ขาดหายเหล่านี้และแสดงเป็น null บางครั้งค่าที่หายอาจแทนด้วยสตริงเฉพาะ เช่น ”NA”, ”N/A” หรือ ”missing” สามารถกำหนดได้ด้วย null_values เพื่อให้ถือเป็น null
# Treating "N/A" and "EMPTY" as nulls
survey_data = pl.read_csv(
"survey_results.csv",
null_values=["N/A", "EMPTY", "null"]
)
เมื่อจัดการค่าที่ขาดหายได้ถูกต้อง จะช่วยให้ชนิดข้อมูลสม่ำเสมอและป้องกันการคำนวณที่ผิดพลาด ลดข้อผิดพลาดระหว่างการวิเคราะห์และการสร้างแบบจำลอง
การอ่านไฟล์ CSV ขนาดใหญ่ใน Polars
เมื่อทำงานกับชุดข้อมูลขนาดใหญ่เกินหน่วยความจำ RAM ที่มี Polars โดดเด่นมากในการประมวลผลข้อมูลโดยไม่ต้องโหลดทั้งไฟล์ในคราวเดียว
Lazy Loading ด้วย scan_csv()
แทนที่จะโหลดข้อมูลเข้าหน่วยความจำทันทีแบบ read_csv() Polars มีทางเลือกแบบ lazy คือ scan_csv() ซึ่งสร้างแผนคำสั่งที่เหมาะสมและรันเฉพาะสิ่งที่จำเป็น
ในตัวอย่างด้านล่าง pl.scan_csv จะสแกนไฟล์ CSV โดยไม่โหลดมันขึ้นมา สร้างคิวรีเพื่อดึงเฉพาะคอลัมน์ “YEAR”, “MAKE” และ “MODEL” ที่ยี่ห้อรถเป็น “ACURA” แล้วจึงรันคิวรีนั้น การทำงานนี้จะโหลดเฉพาะส่วนข้อมูลที่กรองแล้วเข้าสู่หน่วยความจำ แทนที่จะโหลดทั้งไฟล์
# Lazily reference the file (no data loaded yet)
fuel_consumption = pl.scan_csv("Fuel_Consumption_2000-2022.csv")
fuel_consumption_filtered = fuel_consumption.select(
["YEAR", "MAKE", "MODEL"]).filter(pl.col("MAKE") == "ACURA")
# Execute the query and load only the required data
fuel_consumption_acura = fuel_consumption_filtered.collect()
แนะนำให้ใช้ scan_csv() เมื่อ:
- ทำงานกับไฟล์ขนาดใหญ่ที่กินหน่วยความจำมากเกินไป
- ใช้ทรานส์ฟอร์เมชันหลายขั้น เช่น การกรอง การเลือก และการจัดกลุ่มข้อมูล
- ต้องการการปรับแต่งคิวรีให้เหมาะสมก่อนรัน
การสตรีมและประสิทธิภาพหน่วยความจำ
Polars ยังรองรับการสตรีมเพื่อประมวลผลข้อมูลเป็นชังก์ ๆ ทำให้หน่วยความจำพีกคงที่ไม่ว่าจะขนาดไฟล์เท่าใด
สำหรับไฟล์ CSV ขนาดใหญ่ เริ่มด้วย scan_csv() เพื่อสร้างคิวรีแบบ lazy เมื่อเรียก scan_csv() ไฟล์จะยังไม่ถูกโหลดเต็ม
# Read data in chunks
fuel_consumption = pl.scan_csv("Fuel_Consumption_2000-2022.csv",
low_memory=True)
เนื่องจากวิธีข้างต้นยังใช้ read_csv วิธีที่ดีที่สุดคือผสานการรันแบบ lazy เข้ากับการสตรีม
ในตัวอย่างด้านล่าง Polars ประมวลผลไฟล์แบบไปป์ไลน์ ทีละแบตช์ทีละแถว โดยเก็บเฉพาะแถวที่ผ่านการกรอง (“CYLINDERS” > 3) ไว้ในหน่วยความจำ ณ เวลาใดเวลาหนึ่ง
# Lazily reference the file (no data loaded yet)
fuel_consumption = pl.scan_csv("Fuel_Consumption_2000-2022.csv")
result = fuel_consumption.filter(pl.col("CYLINDERS") > 3)
# Stream execution instead of loading everything at once
fuel_consumption_high = result.collect(engine="streaming")
ความได้เปรียบด้านประสิทธิภาพเมื่อเทียบกับ Pandas
เมื่อเทียบกับ pandas Polars มักทำงานได้ดีกว่าในการอ่านและประมวลผล CSV ดังที่จะเห็นในส่วนถัดไป
Polars read_csv เทียบกับ Pandas read_csv
ตารางด้านล่างสรุปความแตกต่างระหว่าง Polars และ Pandas เมื่ออ่าน csv ใน Python
|
ประเด็น |
Polars |
Pandas |
|
ความเร็ว |
เร็วกว่าเพราะมีมัลติเธรดและการพาร์สที่ปรับแต่ง |
ช้ากว่าเมื่อไฟล์ใหญ่ (ส่วนใหญ่ทำงานแบบซิงเกิลเธรด) |
|
การใช้หน่วยความจำ |
ใช้ทรัพยากรต่ำ; รองรับแบบ lazy + สตรีมมิง |
โหลดทั้งชุดข้อมูลเข้าสู่หน่วยความจำแบบ eager |
|
ไวยากรณ์ |
|
|
|
โมเดลการรัน |
รองรับการรันแบบ lazy ( |
รันแบบ eager (ทันที) เท่านั้น |
|
การเพิ่มประสิทธิภาพ |
มีการปรับแต่งคิวรีในตัว (projection, filtering) |
มีการเพิ่มประสิทธิภาพอัตโนมัติจำกัด |
|
กรณีใช้งานที่เหมาะสม |
ชุดข้อมูลขนาดใหญ่ เวิร์กโฟลว์ที่ต้องการสมรรถนะสูง |
ชุดข้อมูลขนาดเล็ก การวิเคราะห์อย่างรวดเร็ว |
แนะนำให้อ่านบทความเกี่ยวกับความแตกต่างระหว่าง Pandas vs. Polars เพื่อพิจารณาว่าเครื่องมือใดเหมาะกับความต้องการเชิงวิเคราะห์มากที่สุด
การอ่าน CSV จากแหล่งต่าง ๆ
ดังที่เห็นก่อนหน้า สามารถใช้ Polars อ่านไฟล์ csv จากหลายแหล่ง ตัวอย่างด้านบนได้แสดงวิธีอ่านจากไฟล์ภายในเครื่องและ URL
นอกจากนี้ Polars จัดการไฟล์ CSV แบบบีบอัดได้อัตโนมัติ ตัวอย่างเช่น โค้ดต่อไปนี้อ่านข้อมูลจากไฟล์ .gzip
# Read gzip-compressed CSV
census_data = pl.read_csv("2018_census.csv.gz")
ข้อผิดพลาดที่พบบ่อยและการแก้ไขปัญหา
แม้จะใช้เอนจินสมรรถนะสูงอย่าง Polars ก็ยังอาจพบปัญหาได้ โดยเฉพาะตอนอ่านไฟล์ csv ต่อไปนี้คือปัญหาทั่วไปที่มักพบและแนวทางแก้ไข
-
ข้อผิดพลาดการเข้ารหัส: อาจพบข้อผิดพลาดอย่าง
UnicodeDecodeErrorซึ่งหมายความว่าไฟล์ไม่ได้ใช้การเข้ารหัส UTF-8 ปัญหานี้พบได้บ่อยในไฟล์เก่าหรือ CSV ที่ส่งออกจาก Excel บางเวอร์ชัน วิธีแก้คือกำหนดการเข้ารหัสให้ถูกต้อง หรือใช้“utf8-lossy”หากไฟล์มีอักขระไม่สม่ำเสมอหรือเสียหาย -
ปัญหาตัวคั่น: หาก DataFrame โหลดขึ้นมาแต่ข้อมูลไปกองอยู่คอลัมน์เดียว แสดงว่า Polars ไม่รู้จักตัวคั่น ให้แก้โดยกำหนดตัวคั่นที่ถูกต้องอย่างชัดเจนด้วยอาร์กิวเมนต์
separatorเช่น เซมิโคลอน (;) แท็บ (\t) หรือไพพ์ (|) -
ชนิดข้อมูลไม่ถูกต้อง: บางครั้ง Polars อาจเดาชนิดคอลัมน์ผิด เช่น อ่าน ID เชิงตัวเลขเป็นสตริง หรือพบข้อผิดพลาดการพาร์ส เมื่อเกิดเหตุการณ์นี้ สามารถใช้
schema_overridesเพื่อระบุชนิดข้อมูลของคอลัมน์เฉพาะ หากต้องการกำหนดชนิดให้ทุกคอลัมน์ให้ใช้schemaและยังสามารถเพิ่มinfer_schema_lengthเพื่อให้ Polars ตรวจสอบหลายแถวขึ้นก่อนเดาสคีมา -
ปัญหาหน่วยความจำเมื่อไฟล์ใหญ่: หากโพรเซส Python ล่มเมื่อลองโหลดไฟล์ขนาดใหญ่ แปลว่า RAM ไม่พอ ให้สลับจาก
read_csv()เป็นscan_csv()เพื่อโหลดแบบ lazy เพื่อลดการใช้หน่วยความจำและเพิ่มประสิทธิภาพ อาจโหลดคอลัมน์ให้น้อยลง หรือใช้การรันแบบสตรีมเพื่อนำเข้าข้อมูลเป็นชังก์
สรุป
ฟังก์ชัน read_csv() ใน Polars ใช้งานง่ายแต่ทรงพลังพอสำหรับงานจริง โดยส่วนตัวเห็นว่า Polars มีข้อได้เปรียบชัดเจนเมื่อทำงานกับชุดข้อมูลที่เติบโตขึ้น
ขั้นตอนถัดไป ลองอ่านบล็อกเกี่ยวกับ เอนจิน GPU ของ Polars เพื่อเรียนรู้การใช้งานต่าง ๆ และหากพร้อมลงมือปฏิบัติ Super Bowl Analytics with Polars แบบโค้ดตาม เพื่อแก้โจทย์วิเคราะห์จริง ทำการคำนวณ และประยุกต์เทคนิค ML พื้นฐานกับปัญหาวิเคราะห์ข้อมูลกีฬาได้
FAQs
ความแตกต่างระหว่าง read_csv() และ scan_csv() ใน Polars คืออะไร?
read_csv() โหลดข้อมูลเข้าสู่หน่วยความจำแบบ eager ในขณะที่ scan_csv() ใช้การรันแบบ lazy และจะประมวลผลข้อมูลก็ต่อเมื่อเรียก .collect()
ควรใช้ scan_csv() แทน read_csv() เมื่อใด?
ใช้ scan_csv() กับชุดข้อมูลขนาดใหญ่หรือเมื่อเชื่อมทรานส์ฟอร์เมชันหลายขั้น เพราะช่วยปรับแต่งการรันและลดการใช้หน่วยความจำ
Polars อ่านเฉพาะคอลัมน์บางคอลัมน์จากไฟล์ CSV ได้หรือไม่?
ได้ ใช้พารามิเตอร์ columns=[...] ใน read_csv() หรือเลือกคอลัมน์ในคิวรีแบบ lazy ด้วย scan_csv()
จะจัดการค่าที่ขาดหายในไฟล์ CSV ของ Polars อย่างไร?
Polars ถือว่าค่าว่างเป็น null ตามค่าเริ่มต้น และสามารถกำหนดเครื่องหมาย null แบบกำหนดเองด้วย null_values=
Polars รองรับไฟล์ CSV ที่บีบอัดหรือไม่?
ได้ รองรับฟอร์แมตแบบบีบอัดอย่าง .gz และ .zip โดยไม่ต้องแตกไฟล์เอง