Pandas & NumPy Academy · บทเรียน

การอ่านไฟล์ขนาดใหญ่เป็นชิ้น

ประมวลผลไฟล์ที่มีขนาดเกิน RAM โดยอ่านเป็นชิ้นด้วย chunksize ใน read_csv และรวมผลลัพธ์ทีละส่วน

บทเรียน 4 จาก 413 ขั้นตอน

การอ่านไฟล์ขนาดใหญ่เป็นชิ้น เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การวิเคราะห์ประสิทธิภาพด้วย timeit และ memory_profiler
  2. การหลีกเลี่ยง iterrows และลูป Python
  3. ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ
  4. การอ่านไฟล์ขนาดใหญ่เป็นชิ้น
← กลับไปที่ Pandas & NumPy Academy