Pandas: فهرس متعدد

آخر تحديث: 2026-08-26

عندما تمتلك البيانات بنية هرمية — مُجمّعة حسب المدينة ثم حسب المنتج، أو حسب السنة ثم حسب الربع — فإن الفهرس ذو المستوى الواحد لا يكفي. يوفر الفهرس المتعدد (التهرئيس الهرمي) فهرسًا متعدد الطبقات للبيانات المتعددة الطبقات: اختر المستوى الأول، ثم انتقل إلى الثاني، تمامًا كتصفح بنية المجلدات يغطي هذا القسم إنشاء الفهرس المتعدد، والاختيار، وإعادة الترتيب، والجمع بينه وبين عمليتي الدمج والفك.

⚠️ ملاحظة: يتطلب الكود التالي بيئة Python محلية للتشغيل.

1. ماذا ستتعلم


2. بيانات المبيعات متعددة المدن والفئات لـ بوب

(1) نقطة الألم: من الصعب تحديد النتائج المجمّعة على مستويين

بعد التجميع حسب المدينة + الفئة، ينتهي بوب بفهرس من مستويين ولا يعرف كيفية تحديد مدينة معينة:

PYTHON
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?
TEXT 📖 للعرض فقط
> **المخرجات:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) واتبع الخطوات. قد تختلف القيم الفعلية قليً ا حسب إصدار pandas الخاص بك.

(2) الحل: اختيار الفهرس المتعدد (MultiIndex)

▶ مثال

TEXT 📖 للعرض فقط
> **المخرجات:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) واتبع الخطوات. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.

: اختيار الفهرس المتعدد (صعوبة ⭐)

PYTHON
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
TEXT 📖 للعرض فقط
> **المخرجات:** قم بتشغيل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبّتًا مسبقًا — قم بتثبيته محليًا (`pip install pandas`) واتبع الخطوات. قد تختلف القيم الفعلية قليلاً حسب إصدار pandas الخاص بك.

3. إنشاء MultiIndex

(1) ثلاث طرق للإنشاء

▶ مثال

TEXT 📖 للعرض فقط
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع التعليمات. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas لديك.

: طرق إنشاء MultiIndex (الصعوبة ⭐⭐)

PYTHON
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)
TEXT 📖 للعرض فقط
> **المخرجات:** قم بتشغيل هذا في بيئة Python محلية (pandas 2.x). خادم Piston لا يحتوي على pandas مثبته مسبقاً — قم بتثبيته محلياً (`pip install pandas`) واتبع التعليمات. القيم الفعلية قد تختلف قليلاً حسب إصدار pandas لديك.

4. اختيار هرمي

(1) loc مقابل xs

▶ مثال

TEXT 📖 للعرض فقط
> **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.

: طرق الاختيار الهرمي (الصعوبة ⭐⭐)

PYTHON
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
TEXT 📖 للعرض فقط
> **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) تبديل المستويات

▶ مثال

TEXT 📖 للعرض فقط
> **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 لإعادة ترتيب المستويات (الصعوبة ⭐⭐)

PYTHON
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'])
TEXT 📖 للعرض فقط
> **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 أو reorder_levels، يجب استدعاء sort_index() — وإلا قد تحدث أخطاء في عمليات التselection اللاحقة أو تبطئ التشغيل.


6. مؤشر متعدد مع stack/unstack

▶ مثال

TEXT 📖 للعرض فقط
> **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 مع مؤشر متعدد (الصعوبة ⭐⭐)

PYTHON
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)
TEXT 📖 للعرض فقط
> **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

▶ مثال

TEXT 📖 للعرض فقط
> **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.

: تسطيح الفهرس المتعدد (الصعوبة ⭐)

PYTHON
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)
TEXT 📖 للعرض فقط
> **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)

100%
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[ملابس]
TEXT 📖 للعرض فقط
> **الإخراج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً — قم بتثبيتها محلياً (`pip install pandas`) وتابع التنفيذ. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas لديك.

▶ المثال

TEXT 📖 للعرض فقط
> **الإخراج:** شغّل هذا في بيئة بايثون محلية (pandas 2.x). خادم Piston لا يحتوي على مكتبة pandas مثبتة مسبقاً — قم بتثبيتها محلياً (`pip install pandas`) وتابع التنفيذ. قد تختلف القيم الفعليّة قليلاً حسب إصدار pandas لديك.

: تحليل سير عمل كامل للمؤشر المتعدد (الصعوبة ⭐⭐⭐)

PYTHON
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}")
TEXT 📖 للعرض فقط
> **الإخراج:** شغّل هذا في بيئة بايثون محلية (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() بعد التبديل، وإلا قد تُنتج التحديدات اللاحقة أخطاء أو أداءً ضعيفًا. سير العمل الموصى به: swaplevelsort_indexselect.
س كيف أُسطّح مؤشرًا متعدد المستويات؟
ج للمؤشر المتعدد المستويات على مستوى الصف → استخدم 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 أكثر دقة.

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): استخدم from_product لإنشاء DataFrame متعدد الفهارس بـ 3 مدن × 2 فئات، ثم استخدم loc و xs لاختيار مدينة محدّدة وفئة محدّدة على التوالي.
  2. متوسط (الصعوبة ⭐⭐): أنشئ DataFrame متعدد الفهارس (مدن × أرباع)، استخدم swaplevel لتبديل المستويات → sort_indexunstack لتحويله إلى جدول عريض → stack للعودة وتحقّق من عملية الذهاب والعودة.
  3. تحدّي (الصعوبة ⭐⭐⭐): قم بمحاكاة 48 صفًا من البيانات بـ 4 مناطق × 3 فئات × 4 أرباع، ثم أكمل: اختيار xs متعدد المستويات → إعادة ترتيب swaplevelunstack متعدد المستويات إلى التنسيق العريض → reset_index للتصدير → groupby لإعادة التجميع.

← Previous: Advanced Time Series · Next: Window Functions →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%