Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

Pandas: เปลี่ยน Data Type ของ Column ใน DataFrame (astype, to_numeric, infer_objects)

การเปลี่ยน Data Type ของ Column ใน Pandas DataFrame เป็นงานประจำวันที่สำคัญสำหรับ Data Cleaning, Memory Optimization และการเตรียมข้อมูลสำหรับ Modeling

สร้าง DataFrame ตัวอย่าง

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'DayNo': [1, 2, 3, 4, 5, 6, 7],
    'Name': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    'Qty': [2.6, 5, 11.8, 2, 5.6, 0, 0.25],
    'Category': ['A', 'B', 'A', 'C', 'B', 'A', 'C']
})

print("Original dtypes:")
print(df.dtypes)
print("\nMemory Usage:")
print(df.memory_usage(deep=True))

วิธีที่ 1: astype() - วิธีมาตรฐานและยืดหยุ่นที่สุด

เปลี่ยน Column เดียว

# Float -> Int (Truncates!)
df['Qty_int'] = df['Qty'].astype('int32')
print(df[['Qty', 'Qty_int']])

# Int -> Float
df['DayNo_float'] = df['DayNo'].astype('float64')

# String -> Category (Memory Efficient สำหรับ Low Cardinality)
df['Category_cat'] = df['Category'].astype('category')
print(f"Category memory: {df['Category'].memory_usage(deep=True)} bytes")
print(f"Category_cat memory: {df['Category_cat'].memory_usage(deep=True)} bytes")

เปลี่ยนหลาย Columns พร้อมกัน (Dictionary Mapping)

# แปลงหลายคอลัมน์พร้อมกัน
df_converted = df.astype({
    'DayNo': 'int16',
    'Qty': 'float32',
    'Name': 'string'  # Pandas StringDtype (Nullable)
})

print(df_converted.dtypes)

แปลงทั้ง DataFrame

# ทุก Column -> String
df_str = df.astype(str)
print(df_str.dtypes)

# ทุก Column -> Numeric (Error ถ้าไม่ได้)
try:
    df_numeric = df.astype(float)
except ValueError as e:
    print(f"Error: {e}")  # Name column cannot convert

วิธีที่ 2: pd.to_numeric() - แปลงเป็น Numeric อย่างปลอดภัย

จัดการ Non-numeric Values ได้ด้วยพารามิเตอร์ errors

# สร้าง DataFrame ที่มีข้อมูล 'Dirty'
dirty_df = pd.DataFrame({
    'values': ['1', '2', '3.5', 'abc', '4', None, '5.5', '']
})

print("Original:", dirty_df['values'].tolist())

# errors='raise' (Default) - Error ถ้าแปลงไม่ได้
# errors='coerce' - แปลงไม่ได้ -> NaN
# errors='ignore' - คืนค่าเดิม

cleaned = pd.to_numeric(dirty_df['values'], errors='coerce')
print("Coerced:", cleaned.tolist())
# [1.0, 2.0, 3.5, nan, 4.0, nan, 5.5, nan]

# Downcast: ลดขนาด Integer/Float ให้เล็กที่สุดที่รองรับค่าได้
downcasted = pd.to_numeric(dirty_df['values'], errors='coerce', downcast='integer')
print(f"Downcasted dtype: {downcasted.dtype}")  # Int64 (Nullable) หรือ int32/int64

วิธีที่ 3: infer_objects() - Soft Conversion

ลองแปลง Object Columns ให้เป็น Type ที่เฉพาะเจาะจงมากขึ้น (Soft Conversion)

# DataFrame ที่สร้างด้วย dtype=object
df_obj = pd.DataFrame({
    'A': [1, 2, 3],
    'B': ['4', '5', '6']  # String Numbers
}, dtype='object')

print("Before infer_objects:")
print(df_obj.dtypes)

df_inferred = df_obj.infer_objects()
print("\nAfter infer_objects:")
print(df_inferred.dtypes)
# A -> int64, B -> object (ยังคงเป็น String)

วิธีที่ 4: convert_dtypes() - Modern Pandas (1.0+)

แปลงเป็น Nullable Dtypes (Int64, Float64, String, Boolean) อัตโนมัติ

df_nullable = df.convert_dtypes()
print(df_nullable.dtypes)
# DayNo     Int64
# Name     string
# Qty     Float64
# Category string

วิธีที่ 5: DateTime Conversion

df_dates = pd.DataFrame({
    'date_str': ['2024-01-15', '2024-02-20', '2024-03-10', 'invalid']
})

# แปลงเป็น Datetime
df_dates['date_parsed'] = pd.to_datetime(df_dates['date_str'], errors='coerce')
print(df_dates)

# Format กำหนดเอง
df_dates['date_custom'] = pd.to_datetime(df_dates['date_str'], format='%Y-%m-%d', errors='coerce')

Memory Optimization: Downcasting

def optimize_dtypes(df):
    """ลด Memory Usage ของ DataFrame"""
    df_opt = df.copy()
    
    for col in df_opt.columns:
        col_type = df_opt[col].dtype
        
        if col_type == 'object':
            # ลองแปลงเป็น Category ถ้า Unique Values น้อย
            num_unique = df_opt[col].nunique()
            num_total = len(df_opt[col])
            if num_unique / num_total < 0.5:  # < 50% Unique
                df_opt[col] = df_opt[col].astype('category')
        
        elif 'int' in str(col_type):
            # Downcast Integer
            df_opt[col] = pd.to_numeric(df_opt[col], downcast='integer')
        
        elif 'float' in str(col_type):
            # Downcast Float
            df_opt[col] = pd.to_numeric(df_opt[col], downcast='float')
    
    return df_opt

# ทดสอบ
large_df = pd.DataFrame({
    'id': range(100000),
    'category': np.random.choice(['A', 'B', 'C', 'D'], 100000),
    'value': np.random.randn(100000),
    'flag': np.random.choice([True, False], 100000)
})

print(f"Original Memory: {large_df.memory_usage(deep=True).sum() / 1e6:.2f} MB")
optimized = optimize_dtypes(large_df)
print(f"Optimized Memory: {optimized.memory_usage(deep=True).sum() / 1e6:.2f} MB")

Error Handling: astype() vs to_numeric()

Scenarioastype()to_numeric(errors='coerce')
Valid Number✅ Convert✅ Convert
String Number "123"✅ Convert✅ Convert
Non-numeric "abc"❌ ValueError✅ NaN
Empty String ""❌ ValueError✅ NaN
None/NaN✅ NaN (Float)✅ NaN

สรุป

  • astype(): วิธีหลัก เปลี่ยน Type ได้ทุกอย่าง ต้องแน่ใจว่า Data Clean
  • to_numeric(errors='coerce'): ปลอดภัยสำหรับ Dirty Data, ได้ NaN แทน Error
  • convert_dtypes(): Modern Way -> Nullable Types (Int64, String, Boolean)
  • Category Type: ประหยัด Memory มากสำหรับ Low Cardinality Columns
  • Downcast: ใช้ pd.to_numeric(..., downcast='integer/float')
  • DateTime: ใช้ pd.to_datetime() พร้อม format และ errors='coerce'