نوع البيانات الفئوي
حوّل أعمدة السلاسل النصية ذات عدد القيم المميزة المنخفض إلى pandas Categorical لتقليل استهلاك الذاكرة وتسريع عمليات groupby.
نوع البيانات الفئوي درس مجاني في Pandas & NumPy Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Pandas & NumPy Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.
ما نوع البيانات Categorical؟
صُمم نوع البيانات Categorical في Pandas للأعمدة التي تحتوي على مجموعة محدودة من القيم المنفصلة (أي منخفضة التعدد)، مثل الجنس أو الحالة أو المنطقة أو فئة المنتج. فبدلًا من تخزين السلسلة النصية الكاملة لكل صف، يخزن Pandas القيم الفريدة (المسماة categories) مرة واحدة، ويستخدم رمزًا صحيحًا لكل صف للإشارة إليها. ويشبه ذلك طريقة استخدام قواعد البيانات لجداول البحث أو أنواع 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))إنشاء Series من النوع Categorical
حوّل عمودًا إلى 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 مليون مؤشر إلى سلاسل نصية (نحو 50 بايتًا أو أكثر لكل منها)، بينما يخزن Categorical خمس سلاسل نصية فقط بالإضافة إلى مليون عدد صحيح بعرض 8 بتات. وقد يصل التوفير إلى 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' التي تسبق '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 أسرع بمقدار يتراوح بين ضعفين وخمسة أضعاف في DataFrame الكبيرة. إضافةً إلى ذلك، يعيد 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
يكون نوع البيانات Categorical خيارًا غير مناسب عندما تكون التعددية عالية؛ فإذا كانت لكل صف تقريبًا قيمة فريدة، مثل معرّفات المستخدمين أو التعليقات النصية الحرة أو UUIDs، فسيكون فهرس الفئات كبيرًا تقريبًا بقدر البيانات الأصلية، ولن يتحقق أي توفير في الذاكرة. والقاعدة التقريبية هي استخدام 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 في الجداول المحورية وGroupby
يضمن استخدام Categorical ثبات شكل الناتج في نتائج groupby والجداول المحورية. ومن دون 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 0Categorical وتعلّم الآلة
تتوقع العديد من مقدّرات scikit-learn إدخالًا رقميًا. لتحويل عمود Categorical إلى أرقام لاستخدامه في نموذج، استخدم .cat.codes (ترميز التسميات) أو pd.get_dummies() (الترميز one-hot). يتجنب الترميز one-hot الإيحاء بوجود علاقة ترتيبية بين الفئات. أما بالنسبة إلى الفئات المرتبة، مثل مستويات الأولوية، فيناسبها ترميز التسميات، أي استخدام الرموز مباشرةً، لأنه يحافظ على معلومات الترتيب.
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تحقق سريع
اختبر مدى فهمك لنوع البيانات Categorical.
مراجعة الدرس
تعلمت في هذا الدرس أن نوع البيانات Categorical يخزّن القيم الفريدة مرة واحدة ويستخدم رموزًا صحيحة لكل صف، مما يوفر قدرًا كبيرًا من الذاكرة للأعمدة منخفضة التعددية، وأن Categorical المرتب يدعم المقارنات ذات المعنى والفرز الصحيح، وأن groupby مع observed=False يضم جميع الفئات، بما فيها الفئات الفارغة. تجنب استخدام Categorical للأعمدة عالية التعددية. في الدرس التالي، سنتعلم تحليل سلاسل التواريخ بطريقة صحيحة باستخدام pd.to_datetime().
الأسئلة الشائعة
هل درس «نوع البيانات الفئوي» مجاني؟
نعم — نص درس «نوع البيانات الفئوي» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Pandas & NumPy Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.
ماذا ستتعلم في «نوع البيانات الفئوي»؟
حوّل أعمدة السلاسل النصية ذات عدد القيم المميزة المنخفض إلى pandas Categorical لتقليل استهلاك الذاكرة وتسريع عمليات groupby. تتمرن على Pandas & NumPy Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Pandas & NumPy Academy؟
لا تُشترط خبرة سابقة. Pandas & NumPy Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «نوع البيانات الفئوي»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Pandas & NumPy Academy هذا؟
نعم. كل درس في Pandas & NumPy Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- فحص أنواع بيانات الأعمدة
- التحويل باستخدام astype()
- نوع البيانات الفئوي
- تحليل التواريخ بطريقة صحيحة