ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ
ลดขนาดชนิดข้อมูลของคอลัมน์ตัวเลขเป็น int32/float32 และแปลงคอลัมน์สตริงเป็น Categorical เพื่อลดหน่วยความจำของ DataFrame ได้สูงสุด 70%
ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ เป็นบทเรียน Pandas & NumPy Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Pandas & NumPy Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดชนิดข้อมูลจึงส่งผลต่อประสิทธิภาพ
ใน Pandas ทุกคอลัมน์มี dtype (ชนิดข้อมูล) ซึ่งกำหนดวิธีจัดเก็บค่าในหน่วยความจำและความเร็วในการดำเนินการ Pandas ใช้ชนิดข้อมูลขนาดกว้างเป็นค่าเริ่มต้นเมื่อโหลดข้อมูล: int64 (8 ไบต์ต่อค่า), float64 (8 ไบต์) และ object (ขนาดแปรผัน โดยสตริงมักใช้ 50–200 ไบต์) สำหรับข้อมูลหลายล้านแถว การเลือกชนิดข้อมูลที่เล็กลงสามารถลดการใช้หน่วยความจำได้ 50–80% และเร่งการดำเนินการได้ 2–5 เท่า เนื่องจากใช้แคชได้มีประสิทธิภาพยิ่งขึ้น
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'id': np.arange(1000000, dtype='int64'),
'score': np.random.uniform(0, 100, 1000000).astype('float64'),
'category': np.random.choice(['A','B','C','D'], 1000000)
})
mem = df.memory_usage(deep=True)
print('Memory per column:')
print(mem)
print(f'Total: {mem.sum() / 1e6:.1f} MB')ลดขนาดคอลัมน์จำนวนเต็ม
หากคอลัมน์มีค่าจำนวนเต็มที่อยู่ในช่วงเล็กกว่า ให้ลดขนาดจาก int64 เป็นชนิดจำนวนเต็มที่เล็กลง pd.to_numeric(series, downcast='integer') จะเลือกชนิดจำนวนเต็มที่เล็กที่สุดโดยอัตโนมัติซึ่งรองรับค่าทั้งหมดได้: int8 (–128 ถึง 127, 1 ไบต์), int16 (–32768 ถึง 32767, 2 ไบต์), int32 (±2 พันล้าน, 4 ไบต์) หรือคงเป็น int64 หากจำเป็น คอลัมน์ int8 ใช้หน่วยความจำน้อยกว่า int64 8 เท่า
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'age': np.random.randint(18, 90, 500000).astype('int64'),
'score': np.random.randint(0, 100, 500000).astype('int64'),
'large_id': np.random.randint(0, 2**31, 500000).astype('int64')
})
# Downcast integers
for col in df.select_dtypes('int64').columns:
df[col] = pd.to_numeric(df[col], downcast='integer')
print('Dtypes after downcasting:')
print(df.dtypes)
print(f'\nMemory: {df.memory_usage(deep=True).sum()/1e6:.2f} MB')ลดขนาดคอลัมน์ทศนิยม
pd.to_numeric(series, downcast='float') จะแปลง float64 เป็น float32 (4 ไบต์แทน 8 ไบต์) เมื่อความแม่นยำยังเพียงพอ float32 มีความแม่นยำประมาณ 7 หลักทศนิยม เทียบกับ 15 หลักสำหรับ float64 สำหรับงานวิเคราะห์ส่วนใหญ่ (เปอร์เซ็นต์ ราคา คุณลักษณะที่ปรับมาตรฐานแล้ว) ความแม่นยำของ float32 ก็เพียงพอ สำหรับการคำนวณทางวิทยาศาสตร์ที่ต้องการความแม่นยำสูง ให้ใช้ float64 ต่อไป การลดความแม่นยำของทศนิยมลงครึ่งหนึ่งจะลดการใช้หน่วยความจำลงครึ่งหนึ่งและปรับปรุงประสิทธิภาพแคช
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({'lat': np.random.uniform(-90, 90, 1000000),
'lon': np.random.uniform(-180, 180, 1000000),
'temp': np.random.uniform(-40, 50, 1000000)})
print('Before:', df.dtypes.unique())
print(f'Memory: {df.memory_usage(deep=True).sum()/1e6:.1f} MB')
for col in df.select_dtypes('float64').columns:
df[col] = pd.to_numeric(df[col], downcast='float')
print('After:', df.dtypes.unique())
print(f'Memory: {df.memory_usage(deep=True).sum()/1e6:.1f} MB')ชนิดข้อมูล Categorical
ชนิดข้อมูล Categorical ช่วยลดการใช้หน่วยความจำได้มากที่สุดสำหรับคอลัมน์สตริงที่มีค่าซ้ำกัน แทนที่จะจัดเก็บสตริงเดิม (เช่น 'Electronics') ซ้ำหลายพันครั้ง Pandas จะจัดเก็บ พจนานุกรมของค่าที่ไม่ซ้ำกัน พร้อมรหัสจำนวนเต็มขนาดกะทัดรัดสำหรับแต่ละแถว คอลัมน์ที่มี 1 ล้านแถวและมีหมวดหมู่ไม่ซ้ำกันเพียง 50 ค่า จะลดจากประมาณ 50 MB (ชนิดข้อมูล object) เหลือประมาณ 1 MB (Categorical) หรือลดการใช้หน่วยความจำได้ 50 เท่า
import pandas as pd
import numpy as np
np.random.seed(0)
categories = ['Electronics', 'Clothing', 'Books', 'Food', 'Furniture',
'Sports', 'Toys', 'Health', 'Garden', 'Automotive']
df = pd.DataFrame({'product_cat': np.random.choice(categories, 1000000)})
mem_before = df.memory_usage(deep=True).sum()
df['product_cat'] = df['product_cat'].astype('category')
mem_after = df.memory_usage(deep=True).sum()
print(f'Object dtype: {mem_before/1e6:.1f} MB')
print(f'Categorical: {mem_after/1e6:.2f} MB')
print(f'Reduction: {mem_before/mem_after:.0f}x')เมื่อ Categorical ช่วยประหยัดหน่วยความจำ
ชนิดข้อมูล Categorical จะช่วยประหยัดหน่วยความจำเมื่อคอลัมน์มี ค่าที่ไม่ซ้ำกันน้อยกว่าจำนวนแถวทั้งหมดมาก จุดคุ้มทุนอยู่ที่อัตราส่วนค่าที่ไม่ซ้ำกันต่อจำนวนแถวทั้งหมด (คาร์ดินาลิตี) สูงกว่าประมาณ 50%: หากทุกแถวมีสตริงไม่ซ้ำกัน Categorical จะใช้หน่วยความจำ มากกว่าชนิดข้อมูล object จริง เพราะต้องจัดเก็บทั้งอาร์เรย์รหัสและอาร์เรย์หมวดหมู่ ควรตรวจสอบ df['col'].nunique() / len(df) ก่อนแปลงเสมอ — อัตราส่วนต่ำกว่า 0.5 (และควรต่ำกว่า 0.05) หมายความว่า Categorical จะช่วยได้
import pandas as pd
import numpy as np
def memory_gain(df, col):
n = len(df)
n_unique = df[col].nunique()
ratio = n_unique / n
mem_obj = df[col].memory_usage(deep=True)
df2 = df.copy()
df2[col] = df2[col].astype('category')
mem_cat = df2[col].memory_usage(deep=True)
print(f'{col}: {n_unique} unique / {n} total (ratio={ratio:.3f})')
print(f' Object: {mem_obj/1e6:.2f} MB → Categorical: {mem_cat/1e6:.2f} MB')
print(f' {"Saves" if mem_cat < mem_obj else "Wastes"} {abs(mem_obj-mem_cat)/1e6:.2f} MB')
np.random.seed(0)
df = pd.DataFrame({
'low_card': np.random.choice(['A','B','C'], 500000), # low cardinality
'high_card': [f'id_{i}' for i in range(500000)] # high cardinality
})
memory_gain(df, 'low_card')
memory_gain(df, 'high_card')Categorical เพิ่มประสิทธิภาพ GroupBy
นอกจากประหยัดหน่วยความจำแล้ว ชนิดข้อมูล Categorical ยัง เร่งการดำเนินการ groupby เนื่องจาก Pandas สามารถใช้รหัสจำนวนเต็มได้โดยตรง แทนการแฮชสตริงเพื่อค้นหาขอบเขตของกลุ่ม สำหรับ DataFrame ที่มีหลายล้านแถวและจัดกลุ่มตามคอลัมน์สตริงที่มีคาร์ดินาลิตีต่ำ (เช่น ภูมิภาค หมวดหมู่สินค้า หรือสถานะ) การแปลงคอลัมน์เหล่านั้นเป็น Categorical ก่อนใช้ groupby อาจเพิ่มความเร็วได้ 2–5 เท่า
import pandas as pd
import numpy as np
import timeit
np.random.seed(0)
df = pd.DataFrame({
'region': np.random.choice(['North','South','East','West'], 1000000),
'sales': np.random.randn(1000000)
})
# Object dtype groupby
t1 = timeit.timeit(lambda: df.groupby('region')['sales'].mean(), number=50)
# Categorical dtype groupby
df2 = df.copy()
df2['region'] = df2['region'].astype('category')
t2 = timeit.timeit(lambda: df2.groupby('region')['sales'].mean(), number=50)
print(f'Object dtype groupby: {t1/50*1000:.2f} ms')
print(f'Categorical groupby: {t2/50*1000:.2f} ms')
print(f'Speedup: {t1/t2:.1f}x')ใช้ชนิดข้อมูลบูลีนสำหรับคอลัมน์สถานะ
คอลัมน์แบบไบนารี (True/False, 0/1, ใช่/ไม่ใช่) มักจัดเก็บเป็น object หรือ int64 การแปลงเป็นชนิดข้อมูล bool ใช้เพียง 1 ไบต์ต่อค่า (เทียบกับ 8 ไบต์สำหรับ int64 หรือมากกว่า 50 ไบต์สำหรับสตริง 'yes'/'no') ใช้ astype(bool) หลังจากตรวจสอบแล้วว่าคอลัมน์มีเฉพาะค่า 0/1 หรือ True/False คอลัมน์บูลีนยังช่วยให้การกรองเร็วขึ้น เพราะภายใน Pandas สามารถใช้การดำเนินการระดับบิตได้
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'is_premium': np.random.choice([0, 1], 1000000).astype('int64'),
'has_discount': np.random.choice(['yes', 'no'], 1000000)
})
print('Before:')
print(df.dtypes)
print(f'Memory: {df.memory_usage(deep=True).sum()/1e6:.1f} MB')
df['is_premium'] = df['is_premium'].astype(bool)
df['has_discount'] = df['has_discount'].map({'yes': True, 'no': False}).astype(bool)
print('\nAfter:')
print(df.dtypes)
print(f'Memory: {df.memory_usage(deep=True).sum()/1e6:.2f} MB')ทำให้การปรับชนิดข้อมูลเหมาะสมทำงานโดยอัตโนมัติ
เขียนฟังก์ชัน optimise_dtypes(df) ที่นำกลับมาใช้ซ้ำได้และใช้การปรับปรุงทั้งหมดโดยอัตโนมัติ ได้แก่ ลดขนาดชนิดข้อมูลจำนวนเต็ม ลดขนาดชนิดข้อมูลจำนวนทศนิยม แปลงออบเจ็กต์ที่มีค่าจำนวนน้อยให้เป็น Categorical และแปลงจำนวนเต็ม 0/1 ให้เป็นบูลีน เรียกใช้ฟังก์ชันนี้ขณะโหลดข้อมูลเพื่อลดการใช้หน่วยความจำตั้งแต่เริ่มต้น วิธีนี้ช่วยให้สมาชิกทุกคนในทีมที่โหลดชุดข้อมูลเดียวกันได้รับข้อมูลรุ่นที่ปรับปรุงแล้ว โดยไม่ต้องจำการใช้แต่ละขั้นตอนด้วยตนเอง
import pandas as pd
import numpy as np
def optimise_dtypes(df, cat_threshold=0.5):
'''Reduce DataFrame memory by choosing smaller dtypes.'''
for col in df.columns:
col_type = df[col].dtype
if col_type == 'int64':
df[col] = pd.to_numeric(df[col], downcast='integer')
elif col_type == 'float64':
df[col] = pd.to_numeric(df[col], downcast='float')
elif col_type == 'object':
cardinality = df[col].nunique() / len(df)
if cardinality < cat_threshold:
df[col] = df[col].astype('category')
return df
# Test
np.random.seed(0)
df = pd.DataFrame({'a': np.random.randint(0,100,500000),
'b': np.random.randn(500000),
'c': np.random.choice(['X','Y','Z'],500000)})
before = df.memory_usage(deep=True).sum()
df = optimise_dtypes(df)
after = df.memory_usage(deep=True).sum()
print(f'Before: {before/1e6:.2f} MB → After: {after/1e6:.2f} MB ({before/after:.1f}x reduction)')ชนิดข้อมูลจำนวนเต็มไม่เป็นลบ
เมื่อคอลัมน์มีเฉพาะจำนวนเต็มที่ไม่เป็นลบ เช่น ID จำนวนครั้ง หรือปริมาณ ให้ใช้ ชนิดข้อมูลจำนวนเต็มแบบไม่มีเครื่องหมาย: uint8 (0–255), uint16 (0–65535), uint32 (0–4 พันล้าน) หรือ uint64 ชนิดข้อมูลแบบไม่มีเครื่องหมายใช้จำนวนไบต์เท่ากับชนิดข้อมูลแบบมีเครื่องหมายที่มีขนาดเท่ากัน แต่เก็บค่าบวกได้มากกว่าถึงสองเท่า ตัวอย่างเช่น ID สินค้าที่มีค่าตั้งแต่ 0 ถึง 60,000 สามารถใช้ uint16 (2 ไบต์) แทน int32 (4 ไบต์) ได้ ให้ใช้ astype('uint16') หลังจากตรวจสอบแล้วว่าคอลัมน์ไม่มีค่าติดลบ
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'product_id': np.random.randint(0, 50000, 1000000).astype('int64'),
'quantity': np.random.randint(0, 255, 1000000).astype('int64')
})
print('Before (int64):', df.memory_usage(deep=True).sum() / 1e6, 'MB')
# product_id fits in uint16 (0-65535)
df['product_id'] = df['product_id'].astype('uint16')
# quantity fits in uint8 (0-255)
df['quantity'] = df['quantity'].astype('uint8')
print('After (uint16+uint8):', df.memory_usage(deep=True).sum() / 1e6, 'MB')
print('\nDtypes:', df.dtypes.to_dict())ตรวจสอบหน่วยความจำหลังการปรับปรุงแต่ละขั้นตอน
ใช้การปรับปรุงทีละรายการและตรวจสอบหน่วยความจำหลังแต่ละขั้นตอน วิธีนี้จะแสดงให้เห็นอย่างชัดเจนว่าแต่ละเทคนิคช่วยลดการใช้หน่วยความจำได้มากเพียงใด DataFrame ขนาดใหญ่มักลดจาก 2 GB (ชนิดข้อมูลเริ่มต้นทั้งหมด) เหลือ 600 MB (การลดขนาดจำนวนเต็ม) จากนั้นเหลือ 300 MB (การลดขนาดจำนวนทศนิยม) และเหลือ 200 MB (ใช้ Categorical กับคอลัมน์ข้อความ) การบันทึกรายละเอียดการลดลงนี้ช่วยอธิบายและสนับสนุนความซับซ้อนที่เพิ่มขึ้นให้สมาชิกในทีมที่พบชนิดข้อมูลที่ไม่คุ้นเคยในโค้ดได้
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'user_id': np.random.randint(0, 99999, 1000000).astype('int64'),
'age': np.random.randint(18, 80, 1000000).astype('int64'),
'revenue': np.random.uniform(0, 5000, 1000000).astype('float64'),
'country': np.random.choice(['US','UK','DE','FR','JP'], 1000000),
'tier': np.random.choice(['free','basic','pro','enterprise'], 1000000)
})
def mem_mb(df):
return df.memory_usage(deep=True).sum() / 1e6
print(f'Start: {mem_mb(df):.1f} MB')
for c in ['user_id','age']:
df[c] = pd.to_numeric(df[c], downcast='integer')
print(f'After int downcast: {mem_mb(df):.1f} MB')
df['revenue'] = pd.to_numeric(df['revenue'], downcast='float')
print(f'After float downcast: {mem_mb(df):.1f} MB')
for c in ['country','tier']:
df[c] = df[c].astype('category')
print(f'After Categorical: {mem_mb(df):.1f} MB')การบันทึกและโหลดชนิดข้อมูลที่ปรับปรุงแล้วใหม่
ชนิดข้อมูลที่ปรับปรุงแล้วจะหายไปหากบันทึกเป็น CSV (เนื่องจากทุกอย่างจะถูกแปลงกลับเป็นข้อความ) หากต้องการคงชนิดข้อมูลไว้ ให้บันทึกเป็นรูปแบบ Parquet ด้วย df.to_parquet('file.parquet') — Parquet จะคงชนิดข้อมูล int32, float32 และ Categorical ไว้ เมื่อนำกลับมาโหลดด้วย pd.read_parquet DataFrame จะมีชนิดข้อมูลที่ใช้หน่วยความจำอย่างมีประสิทธิภาพเหมือนเดิม โดยไม่ต้องเรียกใช้ฟังก์ชันปรับปรุงซ้ำ Parquet ยังโหลดได้เร็วกว่า CSV อย่างมากเมื่อใช้กับไฟล์ขนาดใหญ่
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'age': np.random.randint(18, 80, 100000).astype('int8'), # already optimised
'score': np.random.randn(100000).astype('float32'),
'tier': pd.Categorical(np.random.choice(['free','pro'], 100000))
})
print('Dtypes:', df.dtypes.to_dict())
# Save to Parquet (preserves dtypes)
df.to_parquet('/tmp/optimised.parquet', index=False)
# Reload — dtypes preserved!
df_loaded = pd.read_parquet('/tmp/optimised.parquet')
print('\nLoaded dtypes:', df_loaded.dtypes.to_dict())
print(f'Memory: {df_loaded.memory_usage(deep=True).sum()/1e6:.2f} MB')ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจเกี่ยวกับชนิดข้อมูลที่ใช้หน่วยความจำอย่างมีประสิทธิภาพจากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า pd.to_numeric(downcast='integer') ลดขนาดคอลัมน์จำนวนเต็มจาก 8 ไบต์เหลือ 1–4 ไบต์, pd.to_numeric(downcast='float') ลดการใช้หน่วยความจำของจำนวนทศนิยมลงครึ่งหนึ่ง, Categorical dtype ลดขนาดคอลัมน์ข้อความที่มีค่าจำนวนน้อยได้สูงสุด 50 เท่า พร้อมทั้งทำให้ groupby เร็วขึ้น และ รูปแบบ Parquet คงชนิดข้อมูลที่ปรับปรุงแล้วไว้ตลอดการบันทึกและโหลดไฟล์ บทถัดไปเราจะสำรวจการอ่านข้อมูลเป็นส่วนย่อย ซึ่งช่วยประมวลผลไฟล์ที่มีขนาดใหญ่กว่าหน่วยความจำ RAM ที่มีอยู่
เรียนรู้ Python ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 30
- บทเรียน
- 120
คำถามที่พบบ่อย
บทเรียน “ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Pandas & NumPy Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ”
ลดขนาดชนิดข้อมูลของคอลัมน์ตัวเลขเป็น int32/float32 และแปลงคอลัมน์สตริงเป็น Categorical เพื่อลดหน่วยความจำของ DataFrame ได้สูงสุด 70% คุณปฏิบัติ Pandas & NumPy Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Pandas & NumPy Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Pandas & NumPy Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Pandas & NumPy Academy นี้ได้ไหม
ได้ บทเรียน Pandas & NumPy Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การวิเคราะห์ประสิทธิภาพด้วย timeit และ memory_profiler
- การหลีกเลี่ยง iterrows และลูป Python
- ชนิดข้อมูลที่มีประสิทธิภาพเพื่อลดการใช้หน่วยความจำ
- การอ่านไฟล์ขนาดใหญ่เป็นชิ้น