Pandas: فهرس متعدد
آخر تحديث: 2026-08-26
عندما تمتلك البيانات بنية هرمية — مُجمّعة حسب المدينة ثم حسب المنتج، أو حسب السنة ثم حسب الربع — فإن الفهرس ذو المستوى الواحد لا يكفي. يوفر الفهرس المتعدد (التهرئيس الهرمي) فهرسًا متعدد الطبقات للبيانات المتعددة الطبقات: اختر المستوى الأول، ثم انتقل إلى الثاني، تمامًا كتصفح بنية المجلدات يغطي هذا القسم إنشاء الفهرس المتعدد، والاختيار، وإعادة الترتيب، والجمع بينه وبين عمليتي الدمج والفك.
1. ماذا ستتعلم
- ❶ مفاهيم الفهرس المتعدد (MultiIndex)
- ❷ طرق الإنشاء (from_tuples / from_product / from_frame)
- ❸ التسلسل الهرمي للتحديد (loc / xs)
- ❹ swaplevel / reorder_levels
- ❺ ترتيب الفهرس (sort_index) والتسوية (flattening)
2. بيانات المبيعات متعددة المدن والفئات لـ بوب
(1) نقطة الألم: من الصعب تحديد النتائج المجمّعة على مستويين
بعد التجميع حسب المدينة + الفئة، ينتهي بوب بفهرس من مستويين ولا يعرف كيفية تحديد مدينة معينة:
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA', 'Chicago', 'Chicago'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing', 'Electronics', 'Clothing'],
'sales': [5000, 800, 3000, 600, 2000, 400]
})
result = df.groupby(['city', 'category'])['sales'].sum()
print(result)
# city category
# Chicago Clothing 400
# Electronics 2000
# LA Clothing 600
# Electronics 3000
# NYC Clothing 800
# Electronics 5000
# How to select just NYC?
> **المخرجات:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) واتبع الخطوات. قد تختلف القيم الفعلية قليً ا حسب إصدار pandas الخاص بك.
(2) الحل: اختيار الفهرس المتعدد (MultiIndex)
▶ مثال
> **المخرجات:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) واتبع الخطوات. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
: اختيار الفهرس المتعدد (صعوبة ⭐)
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA', 'Chicago', 'Chicago'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing', 'Electronics', 'Clothing'],
'sales': [5000, 800, 3000, 600, 2000, 400]
})
result = df.groupby(['city', 'category'])['sales'].sum()
# Select by MultiIndex label
print(result.loc['NYC'])
# category
# Clothing 800
# Electronics 5000
# Select specific (city, category)
print(result.loc[('NYC', 'Electronics')]) # 5000
# xs: cross-section selection
print(result.xs('Electronics', level='category'))
# city
# Chicago 2000
# LA 3000
# NYC 5000
> **المخرجات:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) واتبع الخطوات. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.
3. إنشاء MultiIndex
(1) ثلاث طرق للإنشاء
▶ مثال
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع التعليمات. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas لديك.
: طرق إنشاء MultiIndex (الصعوبة ⭐⭐)
import pandas as pd
# 1. from_tuples — أزواج صريحة
index = pd.MultiIndex.from_tuples([
('NYC', 'Electronics'), ('NYC', 'Clothing'),
('LA', 'Electronics'), ('LA', 'Clothing')
], names=['city', 'category'])
df1 = pd.DataFrame({'sales': [5000, 800, 3000, 600]}, index=index)
print("from_tuples:")
print(df1)
# 2. from_product — المنتج الديكارتي (الأكثر شيوعاً)
index2 = pd.MultiIndex.from_product(
[['NYC', 'LA', 'Chicago'], ['Electronics', 'Clothing']],
names=['city', 'category']
)
df2 = pd.DataFrame({
'sales': [5000, 800, 3000, 600, 2000, 400]
}, index=index2)
print("\nfrom_product:")
print(df2)
# 3. from_frame — من DataFrame
df_index = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing']
})
index3 = pd.MultiIndex.from_frame(df_index)
df3 = pd.DataFrame({'sales': [5000, 800, 3000, 600]}, index=index3)
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع التعليمات. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas لديك.
4. اختيار هرمي
(1) loc مقابل xs
▶ مثال
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: طرق الاختيار الهرمي (الصعوبة ⭐⭐)
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Electronics', 'Clothing', 'Home']],
names=['city', 'category']
)
df = pd.DataFrame({
'sales': [5000, 800, 300, 3000, 600, 200],
'profit': [1500, 200, 50, 800, 100, 30]
}, index=index)
# loc: select first level
print(df.loc['NYC'])
# loc: select both levels
print(df.loc[('NYC', 'Electronics')])
# xs: cross-section (select at ANY level)
print(df.xs('Electronics', level='category'))
# Returns all cities' Electronics data
# xs with drop_level=False
print(df.xs('NYC', level='city', drop_level=False))
# Partial selection with slice
print(df.loc[('NYC', slice(None)), :]) # all categories in NYC
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
(2) مقارنة loc و xs
| الميزة | loc | xs |
|---|---|---|
| تحديد مستوى واحد | ✅ loc['NYC'] |
✅ xs('NYC', level=0) |
| تحديد المستوى الثاني | معقد loc[(slice(None),'Elec')] |
بسيط xs('Elec', level=1) |
| مستويات متعددة دفعة واحدة | ✅ loc[('NYC','Elec')] |
❌ مستوى واحد فقط في كل مرة |
| الحفاظ على المستوى | محفوظ بشكل افتراضي | تحكم بواسطة drop_level |
5. تبديل المستوى / فهرس الترتيب
(1) تبديل المستويات
▶ مثال
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: swaplevel لإعادة ترتيب المستويات (الصعوبة ⭐⭐)
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Q1', 'Q2', 'Q3', 'Q4']],
names=['city', 'quarter']
)
df = pd.DataFrame({
'sales': [1200, 1300, 1100, 1400, 800, 900, 700, 1000]
}, index=index)
# Swap levels
df_swapped = df.swaplevel()
print(df_swapped.head(4))
# city quarter
# Q1 NYC 1200
# Q2 NYC 1300
# Q3 NYC 1100
# Q4 NYC 1400
# sort_index after swap (important!)
df_sorted = df_swapped.sort_index()
print(df_sorted.head(4))
# reorder_levels: arbitrary reordering
df_reordered = df.reorder_levels(['quarter', 'city'])
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
6. مؤشر متعدد مع stack/unstack
▶ مثال
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: stack/unstack مع مؤشر متعدد (الصعوبة ⭐⭐)
import pandas as pd
df = pd.DataFrame({
'city': ['NYC', 'NYC', 'LA', 'LA'],
'category': ['Electronics', 'Clothing', 'Electronics', 'Clothing'],
'Q1': [1200, 400, 800, 200],
'Q2': [1300, 500, 900, 300]
})
# set_index → MultiIndex DataFrame
multi_df = df.set_index(['city', 'category'])
print(multi_df)
# unstack: category level → columns
wide = multi_df.unstack(level='category')
print(wide)
# Q1 Q2
# category Clothing Electronics Clothing Electronics
# city
# LA 200 800 300 900
# NYC 400 1200 500 1300
# stack: columns → index level (reverse)
back = wide.stack(level='category')
print(back)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
7. التسطيح باستخدام reset_index
▶ مثال
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
: تسطيح الفهرس المتعدد (الصعوبة ⭐)
import pandas as pd
index = pd.MultiIndex.from_product(
[['NYC', 'LA'], ['Q1', 'Q2']],
names=['city', 'quarter']
)
df = pd.DataFrame({'sales': [1200, 1300, 800, 900]}, index=index)
# reset_index: MultiIndex → regular columns
flat = df.reset_index()
print(flat)
# city quarter sales
# 0 NYC Q1 1200
# 1 NYC Q2 1300
# 2 LA Q1 800
# 3 LA Q2 900
# Flatten column MultiIndex
wide = df.unstack()
flat_cols = wide.copy()
flat_cols.columns = [f'{a}_{b}' for a, b in flat_cols.columns]
print(flat_cols)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on your pandas version.
8. مثال كامل: تحليل مبيعات متعدد المدن والفئات
(6) ▶ البنية الهرمية للمؤشر المتعدد (MultiIndex)
graph TB
A[DataFrame] --> B[المؤشر الخارجي: مدينة]
B --> C[نيويورك]
B --> D[لوس أنجلوس]
B --> E[شيكاغو]
C --> F[المؤشر الداخلي: فئة]
F --> G[إلكترونيات]
F --> H[ملابس]
F --> I[منزل]
D --> J[إلكترونيات]
D --> K[ملابس]
E --> L[إلكترونيات]
E --> M[ملابس]
> **الإخراج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً — قم بتثبيتها محلياً (`pip install pandas`) وتابع التنفيذ. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas لديك.
▶ المثال
> **الإخراج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً — قم بتثبيتها محلياً (`pip install pandas`) وتابع التنفيذ. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas لديك.
: تحليل سير عمل كامل للمؤشر المتعدد (الصعوبة ⭐⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# مثال شامل: تحليل مبيعات متعدد المدن والفئات
# باستخدام المؤشر المتعدد
# ============================================
# 1. إنشاء بيانات هرمية
np.random.seed(42)
index = pd.MultiIndex.from_product(
[['NYC', 'LA', 'Chicago', 'Houston'],
['Electronics', 'Clothing', 'Home'],
['Q1', 'Q2', 'Q3', 'Q4']],
names=['city', 'category', 'quarter']
)
df = pd.DataFrame({
'sales': np.random.randint(100, 5000, 48),
'profit': np.random.randint(10, 1500, 48)
}, index=index)
# 2. عرض البنية
print(f"المستويات: {df.index.names}")
print(f"الشكل: {df.shape}")
# 3. التحديد حسب المدينة
print("\n=== مبيعات نيويورك ===")
print(df.loc['NYC', 'sales'].unstack(level='quarter'))
# 4. xs: جميع الإلكترونيات عبر المدن
print("\n=== الإلكترونيات حسب المدينة ===")
elec = df.xs('Electronics', level='category')[['sales']]
print(elec.unstack(level='quarter'))
# 5. تبديل المستويات → ترتيب → تحديد
df2 = df.swaplevel('city', 'quarter').sort_index()
print("\n=== الربع الأول عبر المدن ===")
print(df2.loc['Q1'])
# 6. فك التحديد للتقرير الواسع
report = df['sales'].unstack(level=['category', 'quarter'])
print("\n=== تقرير واسع (أول مدينتين) ===")
print(report.head(2))
# 7. إعادة التعيين للتصدير
flat = df.reset_index()
print(f"\nالشكل المسطّح: {flat.shape}")
> **الإخراج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً — قم بتثبيتها محلياً (`pip install pandas`) وتابع التنفيذ. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas لديك.
❓ أسئلة شائعة
set_index بأعمدة متعددة يفعل ذلك أيضًا. للبيانات البسيطة، يكون مؤشر المستوى الواحد أكثر ملاءمة — لا تفرض مؤشرًا متعدد المستويات حيث لا يكون هناك حاجة.xs و loc؟xs يمكنه التحديد على أي مستوى (مثلًا، تحديد مباشرة category='Electronics' دون تحديد المدينة أولًا). loc يمكنه التحديد فقط من المستوى الخارجي إلى الداخل (المدينة أولًا، ثم الفئة). xs أكثر مرونة لكنه لا يمكنه تحديد مستويات متعددة في آنٍ واحد؛ loc يدعم loc[('NYC','Electronics')] لكن صيغة تحديد المستوى الثاني أكثر تعقيدًا.swaplevel يغير البيانات؟swaplevel يُبدّل فقط ترتيب مستويات المؤشر؛ البيانات نفسها تبقى دون تغيير. ومع ذلك، يجب استدعاء sort_index() بعد التبديل، وإلا قد تُنتج التحديدات اللاحقة أخطاء أو أداءً ضعيفًا. سير العمل الموصى به: swaplevel → sort_index → select.df.reset_index() لتحويل المستويات إلى أعمدة عادية. للمؤشر المتعدد المستويات على مستوى العمود → أعد تسمية الأعمدة بدمج قيم المستويات: ['_'.join(col) for col in df.columns]. بعد groupby، استخدم as_index=False لتجنب إنشاء مؤشر متعدد المستويات (لكنك تفقد القدرة على التحديد الهرمي).O(1) (مبنية على الهاش تحت الغطاء)، لذا الأداء ممتاز. ومع ذلك، الكفاءة مضمونة فقط بعد sort_index() — تحديد مؤشر متعدد مستويات غير مرتب قد يتدهور إلى O(n). لمجموعات البيانات الكبيرة: استدعِ sort_index() فور الإنشاء، واستخدم is_unique للتحقق من التفرد.get_level_values؟df.index.get_level_values('city') يُعيد جميع القيم على مستوى معين (مثلًا، ['NYC','NYC','LA','LA',...]). مفيد للتحديد الشرطي: df[df.index.get_level_values('city') == 'NYC']. يعمل أيضًا مع groupby: df.groupby(df.index.get_level_values(0)).sum().from_product و from_tuples؟from_product يُنشئ الجداء الديكارتي (جميع التركيبات) — مثالي لبيانات الشبكة الكاملة (3 مدن × 4 أرباع = 12 صفًا). from_tuples يحدد التركيبات الدقيقة — مثالي للبيانات غير المكتملة (بعض المدن تفتقد بعض الفئات). from_product أكثر إيجازًا؛ from_tuples أكثر دقة.📖 ملخص
- يوفر الفهرس المتعدد (MultiIndex) فهرسة هرمية للبيانات متعددة الطبقات، مما يتيح تصفحًا شبيهًا بالملفات.
- الإنشاء:
from_product(شبكة كاملة)،from_tuples(مbinations محددة)،from_frame(تحويل من DataFrame). - التحديد:
locينتقل من الخارجي إلى الداخلي؛xsيحدد عناصر متقاطعة على أي مستوى. swaplevel/reorder_levelsيبدلان ترتيب المستويات - استدعِ دائمًاsort_index()بعده.unstackينقل مستويات الصفوف إلى الأعمدة؛stackينقل مستويات الأعمدة إلى الصفوف - كلاهما يعمل بشكل طبيعي مع الفهرس المتعدد.reset_indexيُسطح الفهرس المتعدد للمستوى الصفّي؛ إعادة تسمية الأعمدة تُسطح الفهرس المتعدد لمستوى العمود.
📝 تمارين
- أساسي (الصعوبة ⭐): استخدم
from_productلإنشاء DataFrame متعدد الفهارس بـ 3 مدن × 2 فئات، ثم استخدمlocوxsلاختيار مدينة محدّدة وفئة محدّدة على التوالي. - متوسط (الصعوبة ⭐⭐): أنشئ DataFrame متعدد الفهارس (مدن × أرباع)، استخدم
swaplevelلتبديل المستويات →sort_index→unstackلتحويله إلى جدول عريض →stackللعودة وتحقّق من عملية الذهاب والعودة. - تحدّي (الصعوبة ⭐⭐⭐): قم بمحاكاة 48 صفًا من البيانات بـ 4 مناطق × 3 فئات × 4 أرباع، ثم أكمل: اختيار
xsمتعدد المستويات → إعادة ترتيبswaplevel→unstackمتعدد المستويات إلى التنسيق العريض →reset_indexللتصدير →groupbyلإعادة التجميع.