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

ชนิดข้อมูลแบบหมวดหมู่

แปลงคอลัมน์สตริงที่มีจำนวนค่าที่แตกต่างกันน้อยเป็น pandas Categorical เพื่อลดการใช้หน่วยความจำและเร่งการดำเนินการแบบ groupby

ชนิดข้อมูลแบบหมวดหมู่ เป็นบทเรียน Pandas & NumPy Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Pandas & NumPy Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน

ชนิดข้อมูล Categorical คืออะไร

ชนิดข้อมูล Categorical ใน Pandas ออกแบบมาสำหรับคอลัมน์ที่มีค่าจำกัดและเป็นค่าที่แยกจากกันได้ (มีจำนวนค่าที่ไม่ซ้ำกันน้อย) เช่น เพศ สถานะ ภูมิภาค หรือหมวดหมู่สินค้า แทนที่จะจัดเก็บสตริงทั้งหมดซ้ำในทุกแถว Pandas จะจัดเก็บค่าที่ไม่ซ้ำกัน (เรียกว่า หมวดหมู่) เพียงครั้งเดียว และใช้รหัสจำนวนเต็มต่อแถวเพื่ออ้างอิงค่าเหล่านั้น แนวคิดนี้คล้ายกับการใช้ตารางค้นหาหรือชนิด enum ในฐานข้อมูล

import pandas as pd

df = pd.DataFrame({
    'region': ['North', 'South', 'East', 'North', 'East', 'South'] * 1000
})

# Without Categorical: object dtype stores every string
print('object dtype memory:', df['region'].memory_usage(deep=True))

# With Categorical: only stores 3 unique values + integer codes
df['region_cat'] = df['region'].astype('category')
print('category dtype memory:', df['region_cat'].memory_usage(deep=True))

การสร้าง Categorical Series

แปลงคอลัมน์เป็น Categorical โดยเรียกใช้ .astype('category') กับ Series ใดก็ได้ Categorical Series ที่ได้จะจัดเก็บค่าที่ไม่ซ้ำกันไว้ใน .cat.categories และจัดเก็บรหัสตำแหน่งจำนวนเต็มไว้ใน .cat.codes นอกจากนี้ คุณยังสร้าง Categorical ได้โดยตรงด้วย pd.Categorical() เพื่อระบุหมวดหมู่และลำดับของหมวดหมู่ไว้ล่วงหน้า

import pandas as pd

s = pd.Series(['low', 'high', 'med', 'high', 'low'])
s_cat = s.astype('category')

print(s_cat.cat.categories)  # Index(['high', 'low', 'med'], dtype='object')
print(s_cat.cat.codes)       # integer codes per row
# 0    1
# 1    0
# 2    2
# 3    0
# 4    1

การประหยัดหน่วยความจำด้วย Categorical

ปริมาณหน่วยความจำที่ประหยัดได้จากชนิดข้อมูล Categorical ขึ้นอยู่กับอัตราส่วนความหลากหลาย (จำนวนค่าที่ไม่ซ้ำกัน ÷ จำนวนแถวทั้งหมด) หากคอลัมน์มีสตริงที่ไม่ซ้ำกัน 5 ค่าและมี 1,000,000 แถว ชนิดข้อมูล object จะจัดเก็บตัวชี้ไปยังสตริง 1 ล้านรายการ (ประมาณ 50+ ไบต์ต่อรายการ) ขณะที่ Categorical จัดเก็บสตริงเพียง 5 ค่าและจำนวนเต็มขนาด 8 บิตอีก 1 ล้านค่า การประหยัดอาจอยู่ที่ 5–20 เท่า ทำให้คอลัมน์ขนาด 50 MB เหลือเพียง 2–5 MB

import pandas as pd
import numpy as np

n = 1_000_000
statuses = np.random.choice(['active', 'inactive', 'pending'], size=n)
df = pd.DataFrame({'status': statuses})

object_mem = df['status'].memory_usage(deep=True) / 1e6
df['status'] = df['status'].astype('category')
cat_mem = df['status'].memory_usage(deep=True) / 1e6

print(f'Object dtype:    {object_mem:.1f} MB')
print(f'Category dtype:  {cat_mem:.1f} MB')
print(f'Reduction:       {object_mem/cat_mem:.1f}x')

Categorical แบบมีลำดับสำหรับการจัดอันดับ

บางครั้งหมวดหมู่มีลำดับตามธรรมชาติ เช่น 'low' < 'medium' < 'high' หรือขนาดเสื้อยืด XS < S < M < L < XL Categorical แบบมีลำดับจะเก็บลำดับนี้ไว้ ทำให้ใช้ตัวดำเนินการเปรียบเทียบที่มีความหมาย (<, >= และอื่น ๆ) ได้ และทำให้ผลลัพธ์ของ groupby เรียงตามลำดับเชิงความหมายที่ถูกต้องแทนการเรียงตามตัวอักษร

import pandas as pd

df = pd.DataFrame({
    'priority': ['high', 'low', 'medium', 'high', 'low']
})

priority_type = pd.CategoricalDtype(
    categories=['low', 'medium', 'high'],
    ordered=True
)
df['priority'] = df['priority'].astype(priority_type)

# Comparison now works correctly
print(df['priority'] >= 'medium')
# 0     True
# 1    False
# 2     True
# 3     True
# 4    False

การเรียงลำดับ Categorical แบบมีลำดับ

เมื่อคุณเรียงลำดับคอลัมน์ Categorical ที่มีการกำหนดลำดับไว้ Pandas จะใช้ลำดับหมวดหมู่ที่กำหนด แทนการเรียงตามตัวอักษร ซึ่งหมายความว่า 'low' จะอยู่ก่อน 'medium' และ 'medium' จะอยู่ก่อน 'high' ไม่ว่าค่าเหล่านี้จะถูกเรียงในฐานะสตริงอย่างไรก็ตาม เรื่องนี้สำคัญอย่างยิ่งต่อการสร้างตารางสรุปและแผนภูมิที่เรียงลำดับได้ถูกต้อง

import pandas as pd

df = pd.DataFrame({
    'severity': pd.Categorical(
        ['high', 'low', 'medium', 'low', 'high'],
        categories=['low', 'medium', 'high'],
        ordered=True
    ),
    'count': [5, 20, 8, 15, 3]
})

# sort_values respects the categorical order
print(df.sort_values('severity')[['severity', 'count']])
#   severity  count
# 1      low     20
# 3      low     15
# 2   medium      8
# 0     high      5
# 4     high      3

ความเร็วของ GroupBy ด้วย Categorical

Pandas สามารถเพิ่มประสิทธิภาพการทำงานของ groupby() กับคอลัมน์ Categorical ได้ เนื่องจากทราบกลุ่มที่เป็นไปได้ทั้งหมดล่วงหน้า ซึ่งอาจทำให้ groupby ทำงานเร็วขึ้น 2–5 เท่ากับ DataFrames ขนาดใหญ่ นอกจากนี้ groupby ที่ใช้ Categorical ยังส่งคืนทุกหมวดหมู่ รวมถึงหมวดหมู่ที่ไม่มีข้อมูลด้วย จึงช่วยให้ตารางสรุปมีแถวสำหรับทุกกลุ่มที่คาดไว้เสมอ แม้บางกลุ่มจะมีข้อมูลเป็นศูนย์ก็ตาม

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'region': pd.Categorical(
        np.random.choice(['North', 'South', 'East', 'West'], 10),
        categories=['North', 'South', 'East', 'West']
    ),
    'sales': np.random.randint(100, 1000, 10)
})

# observed=False shows ALL categories even empty ones
print(df.groupby('region', observed=False)['sales'].sum())

การเพิ่มและลบหมวดหมู่

ใช้ตัวเข้าถึง .cat เพื่อจัดการรายการหมวดหมู่ โดย .cat.add_categories() จะเพิ่มค่าที่อนุญาตใหม่ ซึ่งมีประโยชน์ก่อนแทรกข้อมูลใหม่ และ .cat.remove_unused_categories() จะลบป้ายกำกับหมวดหมู่ที่ไม่มีแถวสอดคล้องกัน เหมาะสำหรับใช้หลังการกรองข้อมูล คุณไม่สามารถกำหนดค่าที่ไม่มีอยู่ในหมวดหมู่ได้จนกว่าจะเพิ่มค่านั้นก่อน

import pandas as pd

s = pd.Categorical(['a', 'b', 'a'], categories=['a', 'b', 'c'])
s = pd.Series(s)

print(s.cat.categories)  # Index(['a', 'b', 'c'], dtype='object')
print(s.value_counts())  # a:2, b:1, c:0

# Remove unused category 'c'
s = s.cat.remove_unused_categories()
print(s.cat.categories)  # Index(['a', 'b'], dtype='object')

การเปลี่ยนชื่อป้ายกำกับหมวดหมู่

.cat.rename_categories() ช่วยให้คุณเปลี่ยนป้ายกำกับหมวดหมู่ได้โดยไม่เปลี่ยนรหัสพื้นฐาน เหมาะเมื่อคุณต้องการแสดงชื่อที่เข้าใจง่ายขึ้น เช่น 'M' → 'Male' หรือแก้ไขการใช้ตัวพิมพ์ใหญ่เล็กของป้ายกำกับที่ไม่สอดคล้องกัน โดยไม่ต้องแปลงกลับเป็น object และทำการจับคู่ใหม่ เมธอดนี้รับรายการ (ตามตำแหน่ง) หรือพจนานุกรม (ระบุรายการเป้าหมาย) ได้

import pandas as pd

s = pd.Series(pd.Categorical(['M', 'F', 'M', 'F'], categories=['M', 'F']))

# Rename using a dict
s = s.cat.rename_categories({'M': 'Male', 'F': 'Female'})
print(s)
# 0      Male
# 1    Female
# 2      Male
# 3    Female
print(s.cat.categories)  # Index(['Male', 'Female'], dtype='object')

กรณีที่ไม่ควรใช้ Categorical

dtype แบบ Categorical ไม่เหมาะเมื่อมีจำนวนค่าที่แตกต่างกันสูง กล่าวคือ หากเกือบทุกแถวมีค่าไม่ซ้ำกัน เช่น รหัสผู้ใช้ ความคิดเห็นแบบข้อความอิสระ หรือ UUID ดัชนีหมวดหมู่จะมีขนาดเกือบเท่ากับข้อมูลเดิม จึงไม่ช่วยประหยัดหน่วยความจำ หลักง่าย ๆ คือ ให้ใช้ Categorical เมื่อจำนวนค่าที่ไม่ซ้ำกันน้อยกว่าประมาณ 50% ของจำนวนแถวทั้งหมด และเหมาะเป็นพิเศษเมื่อมีค่าที่ไม่ซ้ำกันอยู่ในระดับหลักสิบหรือหลักร้อย

import pandas as pd
import numpy as np

df = pd.DataFrame({'user_id': range(1_000_000)})

# High-cardinality: every value is unique — don't use Categorical
object_mem = df['user_id'].astype(str).memory_usage(deep=True) / 1e6
cat_mem = df['user_id'].astype(str).astype('category').memory_usage(deep=True) / 1e6

print(f'String: {object_mem:.1f} MB')
print(f'Category: {cat_mem:.1f} MB')
# Category is WORSE for high-cardinality data!

Categorical ในตาราง Pivot และ Groupby

การใช้ Categorical ช่วยให้ รูปแบบผลลัพธ์สม่ำเสมอ ในผลลัพธ์จาก groupby และตาราง Pivot หากไม่ใช้ Categorical กลุ่มที่บังเอิญไม่มีข้อมูลจะถูกละเว้นจากผลลัพธ์โดยไม่มีการแจ้งให้ทราบ ซึ่งอาจทำให้ผลลัพธ์ไม่สอดคล้องกันเมื่อเปรียบเทียบส่วนย่อยของข้อมูลต่าง ๆ เมื่อใช้ Categorical ร่วมกับ observed=False ทุกกลุ่มจะแสดงอยู่ในผลลัพธ์เสมอ

import pandas as pd

df = pd.DataFrame({
    'quarter': pd.Categorical(
        ['Q1', 'Q1', 'Q3'],
        categories=['Q1', 'Q2', 'Q3', 'Q4']
    ),
    'revenue': [100, 200, 300]
})

# observed=False includes Q2 and Q4 even though they have no rows
result = df.groupby('quarter', observed=False)['revenue'].sum()
print(result)
# quarter
# Q1    300
# Q2      0
# Q3    300
# Q4      0

Categorical และการเรียนรู้ของเครื่อง

ตัวประมาณค่าจำนวนมากของ scikit-learn ต้องการข้อมูลนำเข้าเป็นตัวเลข หากต้องการแปลงคอลัมน์ Categorical เป็นตัวเลขสำหรับโมเดล ให้ใช้ .cat.codes (การเข้ารหัสป้ายกำกับ) หรือ pd.get_dummies() (การเข้ารหัสแบบ one-hot) การเข้ารหัสแบบ one-hot จะหลีกเลี่ยงการสื่อเป็นนัยว่าหมวดหมู่มีความสัมพันธ์ตามลำดับ สำหรับ Categorical ที่มีลำดับ เช่น ระดับความสำคัญ การเข้ารหัสป้ายกำกับ (ใช้รหัสโดยตรง) เป็นวิธีที่เหมาะสมและเก็บข้อมูลลำดับไว้

import pandas as pd

df = pd.DataFrame({
    'colour': pd.Categorical(['red', 'blue', 'green', 'red'])
})

# One-hot encode for unordered categories
one_hot = pd.get_dummies(df['colour'], prefix='colour')
print(one_hot)
#    colour_blue  colour_green  colour_red
# 0            0             0           1
# 1            1             0           0
# 2            0             1           0
# 3            0             0           1

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจของคุณเกี่ยวกับ dtype แบบ Categorical

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า dtype แบบ Categorical จัดเก็บค่าที่ไม่ซ้ำกันเพียงครั้งเดียวและใช้รหัสจำนวนเต็มในแต่ละแถว จึงช่วยประหยัดหน่วยความจำได้มากสำหรับคอลัมน์ที่มีค่าหลากหลายน้อย, ordered Categorical รองรับการเปรียบเทียบที่มีความหมายและการเรียงลำดับที่ถูกต้อง และ groupby with observed=False จะรวมทุกหมวดหมู่ แม้แต่หมวดหมู่ที่ไม่มีข้อมูล ควรหลีกเลี่ยงการใช้ Categorical กับคอลัมน์ที่มีค่าหลากหลายมาก บทถัดไป เราจะเรียนรู้การแยกวิเคราะห์สตริงวันที่อย่างถูกต้องด้วย pd.to_datetime()

คำถามที่พบบ่อย

บทเรียน “ชนิดข้อมูลแบบหมวดหมู่” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ชนิดข้อมูลแบบหมวดหมู่” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Pandas & NumPy Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Pandas & NumPy Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ชนิดข้อมูลแบบหมวดหมู่”

แปลงคอลัมน์สตริงที่มีจำนวนค่าที่แตกต่างกันน้อยเป็น pandas Categorical เพื่อลดการใช้หน่วยความจำและเร่งการดำเนินการแบบ groupby คุณปฏิบัติ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. การตรวจสอบชนิดข้อมูลของคอลัมน์
  2. การแปลงชนิดด้วย astype()
  3. ชนิดข้อมูลแบบหมวดหมู่
  4. การแยกวิเคราะห์วันที่อย่างถูกต้อง
← กลับไปที่ Pandas & NumPy Academy