การอ่านไฟล์ขนาดใหญ่เป็นชิ้น
ประมวลผลไฟล์ที่มีขนาดเกิน RAM โดยอ่านเป็นชิ้นด้วย chunksize ใน read_csv และรวมผลลัพธ์ทีละส่วน
การอ่านไฟล์ขนาดใหญ่เป็นชิ้น เป็นบทเรียน Pandas & NumPy Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Pandas & NumPy Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน
เมื่อไฟล์มีขนาดใหญ่กว่า RAM
คอขวดที่พบได้บ่อยในกระบวนการประมวลผลข้อมูลจริงคือไฟล์ CSV ที่มีขนาดใหญ่กว่า RAM ที่มีอยู่ หากไฟล์มีขนาด 20 GB แต่เครื่องมี RAM 16 GB การใช้ pd.read_csv('file.csv') จะทำงานล้มเหลวด้วย MemoryError วิธีแก้คือ การอ่านข้อมูลเป็นส่วนย่อย โดยโหลดไฟล์เป็นชิ้นขนาดคงที่ ประมวลผลแต่ละชิ้น และสะสมผลลัพธ์ วิธีนี้ช่วยให้วิเคราะห์ไฟล์ขนาดใดก็ได้โดยไม่ต้องโหลดไฟล์ทั้งหมดเข้าสู่หน่วยความจำ
import pandas as pd
import numpy as np
# Simulate a large CSV by writing one
np.random.seed(0)
sample = pd.DataFrame({
'date': pd.date_range('2023-01-01', periods=100000, freq='h'),
'region': np.random.choice(['North','South','East','West'], 100000),
'sales': np.random.randint(100, 1000, 100000)
})
sample.to_csv('/tmp/large_sales.csv', index=False)
print(f'File size: {pd.io.common.get_handle("/tmp/large_sales.csv", "r").handle.seek(0, 2)/1e6:.1f} MB... (simulated)')
print('Rows:', len(sample))พารามิเตอร์ chunksize ใน read_csv
ส่งค่า chunksize=n ให้กับ pd.read_csv() เพื่อให้คืนค่าเป็น ตัววนซ้ำ TextFileReader แทน DataFrame ในแต่ละรอบ ตัววนซ้ำจะส่งคืน n แถวถัดไปเป็น DataFrame วิธีนี้ทำให้มีเพียง n แถวอยู่ในหน่วยความจำในแต่ละครั้ง ค่าเริ่มต้นที่เหมาะสมสำหรับ chunksize คือ 100,000 แถว ซึ่งเล็กพอที่จะอยู่ใน RAM แต่ใหญ่พอที่จะควบคุมค่าใช้จ่ายส่วนเกินจากการรับส่งข้อมูลเข้าออกได้ ปรับค่าตาม RAM ที่มีและจำนวนคอลัมน์
import pandas as pd
# Read file in chunks of 25,000 rows
chunk_iter = pd.read_csv('/tmp/large_sales.csv', chunksize=25000)
# Peek at the first chunk
first_chunk = next(chunk_iter)
print('First chunk shape:', first_chunk.shape)
print(first_chunk.head(3))การวนซ้ำผ่านส่วนข้อมูล
ใช้ลูป for กับตัววนซ้ำส่วนข้อมูลเพื่อประมวลผลแต่ละส่วน สำหรับการดำเนินการง่าย ๆ เช่น นับแถว หาผลรวมของคอลัมน์ หรือกรองข้อมูล ให้ประมวลผลแต่ละส่วนแยกกันและสะสมผลลัพธ์ไว้ในรายการหรือยอดรวมสะสม ควรเรียกใช้ตัววนซ้ำภายในคำสั่ง with เสมอ หรือสร้างตัววนซ้ำใหม่สำหรับการทำงานของกระบวนการประมวลผลแต่ละครั้ง เนื่องจากตัววนซ้ำจะถูกใช้จนหมดเพียงครั้งเดียวและเริ่มใหม่ไม่ได้
import pandas as pd
total_rows = 0
total_sales = 0.0
chunk_count = 0
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
total_rows += len(chunk)
total_sales += chunk['sales'].sum()
chunk_count += 1
print(f'Chunks processed: {chunk_count}')
print(f'Total rows: {total_rows:,}')
print(f'Total sales: {total_sales:,.0f}')
print(f'Avg sales per row: {total_sales/total_rows:.2f}')การกรองแถวระหว่างแบ่งข้อมูลเป็นส่วน
ใช้ตัวกรองแถวภายในลูปเพื่อทิ้งข้อมูลที่ไม่ต้องการก่อนสะสมข้อมูล วิธีนี้ช่วยให้ใช้หน่วยความจำต่ำ เพราะเก็บไว้เฉพาะแถวที่ตรงตามเงื่อนไขเท่านั้น ตัวอย่างเช่น หากต้องการดึงแถวของภูมิภาค 'North' ทั้งหมดจากไฟล์ขนาด 10 GB ให้กรองแต่ละส่วนก่อนเพิ่มลงในรายการผลลัพธ์ สุดท้าย pd.concat จะจัดการเฉพาะข้อมูลย่อยที่ผ่านการกรองแล้ว ซึ่งมีขนาดเล็กกว่ามาก
import pandas as pd
filtered_chunks = []
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
# Keep only North region rows
north = chunk[chunk['region'] == 'North']
if len(north) > 0:
filtered_chunks.append(north)
north_df = pd.concat(filtered_chunks, ignore_index=True)
print(f'North region rows: {len(north_df):,}')
print(f'North total sales: {north_df["sales"].sum():,.0f}')การรวมค่าด้วย GroupBy แบบเพิ่มทีละส่วน
การรวมค่าด้วย GroupBy ข้ามส่วนข้อมูลทำได้ซับซ้อนกว่าการหาผลรวมอย่างง่าย เนื่องจากกลุ่มเดียวกันอาจกระจายอยู่ในหลายส่วน รูปแบบการทำงานคือ คำนวณผลรวมและจำนวนรายการระดับกลุ่มในแต่ละส่วน เชื่อมผลลัพธ์ย่อยเหล่านี้เข้าด้วยกัน แล้วทำ groupby ครั้งสุดท้ายกับผลลัพธ์ย่อยที่สะสมไว้ ห้ามเรียกใช้ groupby().mean() ในแต่ละส่วนแล้วนำค่าเฉลี่ยมาเฉลี่ยกันอีกครั้ง เพราะจะได้ผลลัพธ์ผิดเมื่อขนาดของกลุ่มในแต่ละส่วนไม่เท่ากัน
import pandas as pd
partials = []
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
# Compute sum and count per region in this chunk
partial = chunk.groupby('region')['sales'].agg(['sum', 'count'])
partials.append(partial)
# Combine all partial results
combined = pd.concat(partials).groupby(level=0).sum()
combined['mean'] = combined['sum'] / combined['count']
print('Regional aggregation (chunked):')
print(combined.round(2))การระบุชนิดข้อมูลอย่างประหยัดหน่วยความจำขณะโหลด
ลดการใช้หน่วยความจำระหว่างการโหลดข้อมูลเป็นส่วนย่อย โดยระบุ dtypes ของคอลัมน์ล่วงหน้าใน read_csv วิธีนี้ป้องกันไม่ให้ Pandas จัดสรรชนิดข้อมูลขนาดใหญ่สำหรับแต่ละส่วนแล้วทิ้งในภายหลัง ส่งพจนานุกรมเช่น dtype={'region': 'category', 'sales': 'int32'} ให้กับ read_csv() เมื่อใช้ร่วมกับการอ่านข้อมูลเป็นส่วนย่อย วิธีนี้อาจลดการใช้หน่วยความจำสูงสุดให้เหลือน้อยกว่า 10% ของการโหลดไฟล์ทั้งหมดแบบทั่วไป
import pandas as pd
specified_dtypes = {
'region': 'category',
'sales': 'int32'
}
total_sales = 0
for chunk in pd.read_csv(
'/tmp/large_sales.csv',
chunksize=25000,
dtype=specified_dtypes,
parse_dates=['date']
):
total_sales += chunk['sales'].sum()
# Only 25k rows * (category + int32 + datetime) in RAM at once
print(f'Total sales (memory-efficient): {total_sales:,.0f}')การเขียนผลลัพธ์ทีละส่วน
เมื่อจำเป็นต้องเขียนผลลัพธ์จากการประมวลผลแต่ละส่วนลงในไฟล์ ให้เขียนแต่ละส่วนที่ประมวลผลแล้วทันที แทนการสะสมทุกอย่างไว้ในหน่วยความจำ ใช้ mode='a' (เพิ่มต่อท้าย) และ header=False (หลังจากส่วนแรก) ร่วมกับ to_csv() วิธีนี้ทำให้การใช้หน่วยความจำของทั้งข้อมูลเข้าและข้อมูลออกอยู่ในระดับขนาดของส่วนข้อมูล และทำให้กระบวนการประมวลผลสามารถจัดการไฟล์ขนาดใดก็ได้บนเครื่องที่มีหน่วยความจำจำกัด
import pandas as pd
import os
output_path = '/tmp/filtered_output.csv'
# Remove previous output if it exists
if os.path.exists(output_path):
os.remove(output_path)
first_chunk = True
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=25000):
# Process: keep only high-value rows
processed = chunk[chunk['sales'] > 800].copy()
# Write: header only on first chunk, append thereafter
processed.to_csv(output_path,
mode='a',
header=first_chunk,
index=False)
first_chunk = False
result = pd.read_csv(output_path)
print(f'High-value rows written: {len(result):,}')
print(f'Average sales (>800): {result["sales"].mean():.1f}')แนะนำ Dask ในฐานะทางเลือกที่ใช้แทนได้ทันที
Dask มีส่วนเชื่อมต่อการใช้งานคล้าย Pandas ซึ่งดำเนินการแบบประเมินผลเมื่อจำเป็นและทำงานแบบขนานข้ามส่วนข้อมูลโดยอัตโนมัติ แทนที่จะเขียนลูปแบ่งข้อมูลด้วยตนเอง คุณสามารถเขียน dask_df = dd.read_csv('file.csv') แล้วใช้การดำเนินการแบบเดียวกับ Pandas เช่น groupby, filter และ merge เรียกใช้ .compute() เมื่อสิ้นสุดเพื่อเริ่มการประมวลผล Dask เป็นทางเลือกที่ใช้งานได้จริงที่สุดเมื่อกระบวนการประมวลผลของคุณมีการดำเนินการหลายขั้นตอนที่ซับซ้อนและเขียนด้วยการแบ่งข้อมูลเองได้ยาก
# pip install dask
# import dask.dataframe as dd
# Read the large CSV as a Dask DataFrame (lazy — no data loaded yet)
# ddf = dd.read_csv('/tmp/large_sales.csv')
# Operations are lazy — no computation happens until .compute()
# result = ddf.groupby('region')['sales'].mean().compute()
# print(result)
# Key Dask advantages:
# - Automatic chunking (no manual chunksize loops)
# - Parallel execution (multi-core)
# - Familiar Pandas API
# - Works with files larger than RAM
print('Dask extends Pandas to datasets larger than RAM with minimal API changes.')การเลือกขนาดส่วนข้อมูล
ขนาดส่วนข้อมูลที่เหมาะสมต้องสร้างสมดุลระหว่างปัจจัยที่ขัดแย้งกันสองด้าน: เล็กเกินไป (เช่น 1,000 แถว) ทำให้มีค่าใช้จ่ายส่วนเกินต่อส่วนสูง ทั้งการเตรียมการรับส่งข้อมูลเข้าออกและการสร้าง DataFrame ส่งผลให้ลูปใช้เวลานานขึ้น ส่วนที่ ใหญ่เกินไป (เช่น 10M แถว) จะขัดกับจุดประสงค์ของการแบ่งข้อมูล เพราะโหลดข้อมูลเข้าสู่ RAM มากเกินไปในครั้งเดียว จุดเริ่มต้นที่ใช้งานได้จริงคือกำหนดให้แต่ละส่วนมีขนาด 50–200 MB ในหน่วยความจำ สำหรับ DataFrame ที่มี 10 คอลัมน์และแต่ละคอลัมน์มีขนาดเฉลี่ย 8 ไบต์ 100,000 แถวจะมีขนาดประมาณ 8 MB ต่อส่วน ซึ่งเป็นค่าเริ่มต้นที่ปลอดภัยและยังเหลือพื้นที่หน่วยความจำสำรองมาก
import pandas as pd
import timeit
# Benchmark different chunk sizes
for chunksize in [10000, 50000, 100000]:
t = timeit.timeit(
lambda: sum(chunk['sales'].sum()
for chunk in pd.read_csv('/tmp/large_sales.csv', chunksize=chunksize)),
number=3
)
print(f'chunksize={chunksize:>7,}: {t/3:.3f}s per pass')Parquet กับ CSV สำหรับข้อมูลขนาดใหญ่
หากคุณเป็นผู้กำหนดรูปแบบไฟล์ ให้เลือกใช้ Parquet แทน CSV สำหรับชุดข้อมูลขนาดใหญ่ Parquet เป็นรูปแบบไบนารีแบบคอลัมน์ที่โหลดเฉพาะคอลัมน์ที่ร้องขอ (การตัดคอลัมน์ที่ไม่ต้องการ) บีบอัดได้ดีกว่า CSV มาก คงชนิดข้อมูลไว้ (ไม่ต้องอนุมานใหม่ขณะโหลด) และอ่านได้เร็วกว่า CSV ที่เทียบเท่ากัน 5–20 เท่า แปลง CSV ขนาดใหญ่เป็น Parquet ครั้งเดียวด้วย pd.read_csv('file.csv', chunksize=500000) + to_parquet จากนั้นใช้ pd.read_parquet(columns=['col1','col2']) สำหรับการอ่านครั้งต่อ ๆ ไปทั้งหมด
import pandas as pd
# Convert our CSV to Parquet (do this once)
df = pd.read_csv('/tmp/large_sales.csv', parse_dates=['date'])
df['region'] = df['region'].astype('category')
df['sales'] = df['sales'].astype('int32')
df.to_parquet('/tmp/large_sales.parquet', index=False)
# Now read only the columns needed — much faster than CSV
sales_by_region = pd.read_parquet(
'/tmp/large_sales.parquet',
columns=['region', 'sales']
)
print('Parquet read result:')
print(sales_by_region.groupby('region')['sales'].mean().round(1))
print('\ndtypes preserved:', sales_by_region.dtypes.to_dict())รูปแบบกระบวนการประมวลผลแบบแบ่งข้อมูลครบถ้วน
รูปแบบที่สมบูรณ์สำหรับการประมวลผลไฟล์ขนาดใหญ่: 1) ระบุชนิดข้อมูลขณะอ่านไฟล์ 2) กรองแถวให้เร็วที่สุดภายในลูป 3) คำนวณค่ารวมย่อยของแต่ละส่วน (ผลรวม + จำนวน ห้ามคำนวณค่าเฉลี่ยโดยตรง) 4) หลังจบลูป ให้รวมผลลัพธ์ย่อยและคำนวณสถิติสุดท้าย 5) เขียนผลลัพธ์ทีละส่วนหากผลลัพธ์มีขนาดใหญ่ รูปแบบนี้รองรับไฟล์ทุกขนาดบนเครื่องทุกประเภท โดยปรับ chunksize ให้เหมาะสม
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจเกี่ยวกับการอ่านข้อมูลเป็นส่วนย่อยจากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า chunksize ใน read_csv จะคืนค่าตัววนซ้ำที่ส่งคืน DataFrame หนึ่งรายการต่อหนึ่งส่วน ทำให้สามารถประมวลผลไฟล์ที่ใหญ่กว่า RAM ได้ทีละส่วน, การรวมค่าย่อย (ผลรวม + จำนวน) สามารถสะสมข้ามส่วนได้อย่างถูกต้อง ขณะที่ไม่สามารถนำค่าเฉลี่ยมาเฉลี่ยกันโดยตรง และ รูปแบบ Parquet เป็นทางเลือกในระยะยาวที่ดีที่สุดแทน CSV สำหรับชุดข้อมูลขนาดใหญ่ เนื่องจากการบีบอัด ความเร็ว และการคงชนิดข้อมูล หลักสูตรเคล็ดลับเพิ่มประสิทธิภาพ Pandas นี้จบลงแล้ว — คุณพร้อมสำหรับหลักสูตร B2 ระดับก้าวหน้าแล้ว
เรียนรู้ Python ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 30
- บทเรียน
- 120
คำถามที่พบบ่อย
บทเรียน “การอ่านไฟล์ขนาดใหญ่เป็นชิ้น” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การอ่านไฟล์ขนาดใหญ่เป็นชิ้น” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Pandas & NumPy Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การอ่านไฟล์ขนาดใหญ่เป็นชิ้น”
ประมวลผลไฟล์ที่มีขนาดเกิน RAM โดยอ่านเป็นชิ้นด้วย chunksize ใน read_csv และรวมผลลัพธ์ทีละส่วน คุณปฏิบัติ Pandas & NumPy Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Pandas & NumPy Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Pandas & NumPy Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การอ่านไฟล์ขนาดใหญ่เป็นชิ้น” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Pandas & NumPy Academy นี้ได้ไหม
ได้ บทเรียน Pandas & NumPy Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การวิเคราะห์ประสิทธิภาพด้วย timeit และ memory_profiler
- การหลีกเลี่ยง iterrows และลูป Python
- ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ
- การอ่านไฟล์ขนาดใหญ่เป็นชิ้น