0Pricing
Pandas & NumPy Academy · درس

محاذاة الفهارس وإعادة فهرستها

حاذِ اثنين من DataFrame إلى فهرس مشترك باستخدام reindex()، واملأ التسميات المفقودة، وافهم كيفية محاذاة الفهارس في العمليات الحسابية.

محاذاة الفهارس وإعادة فهرستها درس مجاني في Pandas & NumPy Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Pandas & NumPy Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.

كيفية محاذاة Pandas للفهارس

من أقوى سلوكيات Pandas — وأكثرها إثارة للمفاجأة أحيانًا — المحاذاة التلقائية للفهرس. عند جمع أو طرح أو دمج Series أو DataFrame، يحاذيهما Pandas أولًا وفق تسميات الفهرس قبل تنفيذ العملية. تُجرى العملية على التسميات المتطابقة، بينما تنتج التسميات غير المتطابقة NaN. يمنع ذلك الأخطاء الصامتة الناتجة عن عدم محاذاة البيانات، ويجعل دمج البيانات من مصادر مختلفة آمنًا دون الحاجة إلى ترتيبها أو إعادة ترتيبها يدويًا أولًا.

import pandas as pd

s1 = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
s2 = pd.Series([1, 2, 3], index=['b', 'c', 'd'])

# Alignment in action: a→NaN, d→NaN
result = s1 + s2
print('s1 + s2 with automatic alignment:')
print(result)

NaN الناتجة عن عدم محاذاة الفهارس

عندما لا تتطابق تسميات الفهرس، يملأ Pandas الإدخالات المفقودة بقيمة NaN. وهذا مقصود، إذ يشير إلى عدم وجود قيمة مقابلة من أحد معاملي العملية. افحص دائمًا نتيجة العمليات الحسابية بين Series أو DataFrame بحثًا عن قيم NaN غير متوقعة — فهي علامة على عدم محاذاة الفهرس. استخدم fill_value=0 في الطريقة الحسابية (s1.add(s2, fill_value=0)) لمعاملة القيم المحاذاة المفقودة كأصفار بدلًا من تمرير NaN.

import pandas as pd

s1 = pd.Series({'a': 10, 'b': 20, 'c': 30})
s2 = pd.Series({'b': 1, 'c': 2, 'd': 3})

# Default: NaN where labels don't match
print('Default (NaN for unmatched):')
print(s1 + s2)

# fill_value=0: treat missing as zero
print('\nWith fill_value=0:')
print(s1.add(s2, fill_value=0))

محاذاة العمليات الحسابية في DataFrame

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

import pandas as pd

df1 = pd.DataFrame({'revenue': [100, 200], 'cost': [80, 150]}, index=['Jan', 'Feb'])
df2 = pd.DataFrame({'revenue': [120, 210], 'headcount': [5, 6]}, index=['Feb', 'Mar'])

result = df1 + df2
print('Combined DataFrame (union of index and columns):')
print(result)

الطريقة reindex()

تعيد df.reindex(new_index) DataFrame جديدًا متوافقًا مع قائمة التسميات new_index. تُحفظ التسميات الموجودة في الفهرس الأصلي والجديد معًا، بينما تحصل التسميات الموجودة في new_index وغير الموجودة في الفهرس الأصلي على NaN، وتُسقط التسميات الموجودة في الفهرس الأصلي وغير الموجودة في new_index. هذه هي الطريقة الصريحة لمحاذاة DataFrame مع مجموعة التسميات المستهدفة قبل تنفيذ العمليات.

import pandas as pd

s = pd.Series({'a': 10, 'b': 20, 'c': 30})

# Reindex to a new label set
reindexed = s.reindex(['b', 'c', 'd', 'e'])
print('Original:', s.index.tolist())
print('New index: [b, c, d, e]')
print('\nReindexed Series:')
print(reindexed)
# b, c preserved; d, e → NaN; a dropped

ملء القيم المفقودة بعد إعادة الفهرسة

تقبل reindex() معامل fill_value لاستبدال التسميات الجديدة بثابت، ومعامل method للملء التقدمي ('ffill') أو الملء التراجعي ('bfill'). وتكون طرق الملء هذه مفيدةً خصوصًا مع بيانات السلاسل الزمنية، عندما تعيد فهرسة Series إلى نطاق تواريخ كامل وتريد ملء الأيام المفقودة بآخر قيمة معروفة بدلًا من NaN.

import pandas as pd

# Sparse daily data with gaps
s = pd.Series(
    [10, 20, 30],
    index=pd.to_datetime(['2024-01-01', '2024-01-03', '2024-01-07'])
)

# Reindex to complete date range
full_range = pd.date_range('2024-01-01', '2024-01-07')
print('Forward fill (carry last known value):')
print(s.reindex(full_range, method='ffill'))

print('\nFill with 0:')
print(s.reindex(full_range, fill_value=0))

إعادة فهرسة الأعمدة

تعمل reindex مع الأعمدة أيضًا: df.reindex(columns=['col1', 'col2', 'new_col']). تُضاف الأعمدة الجديدة غير الموجودة في DataFrame الأصلي بقيمة NaN. وتُسقط الأعمدة الموجودة في DataFrame الأصلي وغير الموجودة في القائمة الجديدة. يفيد ذلك في فرض مخطط أعمدة موحّد عبر عدة DataFrame قادمة من مصادر مختلفة وقد تحتوي على مجموعات غير متسقة من الأعمدة.

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'age': [30, 25],
    'city': ['NY', 'LA']
})

# Enforce a canonical column order and add missing columns
canonical_cols = ['name', 'age', 'email', 'city', 'country']
df_reindexed = df.reindex(columns=canonical_cols)
print(df_reindexed)
# 'email' and 'country' are new → NaN

استخدام align() لمزامنة كائنين

تعيد s1.align(s2) كائنين جديدين لهما الفهرس نفسه (الاتحاد أو التقاطع وفقًا لمعامل join)، مما يجعلهما جاهزين للعمليات على أساس كل عنصر. ويعادل ذلك إعادة فهرسة الكائنين في الوقت نفسه إلى مجموعة التسميات نفسها. استخدم join='inner' للاحتفاظ بالتسميات المشتركة فقط، أو join='outer' (الافتراضي) للاحتفاظ بجميع التسميات من الكائنين مع استخدام NaN عند عدم التطابق.

import pandas as pd

s1 = pd.Series({'a': 10, 'b': 20, 'c': 30})
s2 = pd.Series({'b': 1, 'c': 2, 'd': 3})

# Align to common labels only (inner join)
s1_aligned, s2_aligned = s1.align(s2, join='inner')
print('Inner-aligned s1:')
print(s1_aligned)
print('Inner-aligned s2:')
print(s2_aligned)

print('\nProduct on common labels:')
print(s1_aligned * s2_aligned)

المحاذاة في merge مقارنةً بالعمليات الحسابية

يوفر Pandas فلسفتين لدمج DataFrame: merge/join (مطابقة صريحة للمفاتيح بأسلوب SQL) والعمليات الحسابية مع محاذاة الفهرس (مطابقة ضمنية قائمة على التسميات). استخدم merge عند دمج جداول منظمة تحتوي على أعمدة مفاتيح صريحة. واستخدم المحاذاة الحسابية عند إجراء حسابات بين DataFrame يُفترض أن تشترك طبيعيًا في فهرس — مثل طرح DataFrame للتكاليف من DataFrame للإيرادات، وكلاهما مفهرس حسب (المنطقة، الشهر).

import pandas as pd

# Two DataFrames sharing the same index
revenue = pd.Series({'North': 500, 'South': 300, 'East': 450})
costs = pd.Series({'North': 350, 'South': 280, 'West': 400})

# Automatic alignment: 'East' and 'West' don't match → NaN
profit = revenue - costs
print('Profit by region:')
print(profit)

# If you want only common regions
profit_inner = revenue.align(costs, join='inner')[0] - revenue.align(costs, join='inner')[1]
print('\nProfit (common regions only):')
print(profit_inner)

التحقق من تساوي الفهارس قبل العمليات

قبل إجراء العمليات على اثنين من DataFrame، تحقّق من تطابق فهارسهما باستخدام (df1.index == df2.index).all(). إذا اختلف الفهرسان في الترتيب فقط، فرتّبهما قبل المقارنة. وبالنسبة إلى DataFrame المحمّلة من مصادر مختلفة، من الممارسات الجيدة إجراء محاذاة أو إعادة فهرسة صريحة قبل العمليات الحسابية لتجنب انتشار NaN غير المقصود. وثّق أي افتراضات تتعلق بالمحاذاة في التعليقات أو سجلات مسار المعالجة.

import pandas as pd

df1 = pd.DataFrame({'sales': [100, 200, 300]}, index=['Q1', 'Q2', 'Q3'])
df2 = pd.DataFrame({'cost': [80, 160, 240]}, index=['Q1', 'Q2', 'Q3'])

# Check index equality
if (df1.index == df2.index).all():
    print('Indices match — safe to combine.')
    combined = pd.concat([df1, df2], axis=1)
    combined['profit'] = combined['sales'] - combined['cost']
    print(combined)
else:
    print('Indices differ — align before combining!')

نمط عملي: توحيد الأشكال

من الأنماط الشائعة عند تحميل البيانات من ملفات متعددة أو استدعاءات API أن تغطي كل دفعة مجموعة فرعية مختلفة من المفاتيح. قبل الدمج أو التجميع، أعد فهرسة جميع الدفعات إلى نطاق المفاتيح الكامل المعروف باستخدام fill_value=0. يضمن ذلك امتلاك كل دفعة الشكل نفسه (الفهرس نفسه والأعمدة نفسها) قبل إجراء العمليات، مما يمنع عدم تطابق الأشكال الصامت الذي يؤدي إلى نتائج تجميع خاطئة.

import pandas as pd

# Two sales batches with different product sets
batch1 = pd.Series({'laptop': 50, 'phone': 80, 'tablet': 30})
batch2 = pd.Series({'phone': 90, 'tablet': 40, 'headphones': 60})

# Full product catalogue
all_products = ['laptop', 'phone', 'tablet', 'headphones']

# Standardise both to the full catalogue
b1 = batch1.reindex(all_products, fill_value=0)
b2 = batch2.reindex(all_products, fill_value=0)

total = b1 + b2
print('Total sales per product:')
print(total)

الحالات الطرفية لمحاذاة الفهارس

هناك حالتان طرفيتان مهمتان: التسميات المكررة — إذا كانت لدى كلتا Series تسميات فهرس مكررة، فإن المحاذاة تنتج توسعًا شبيهًا بالجداء الديكارتي مع العديد من قيم NaN (إذ لا تفترض Pandas أيًّا من التسميات المكررة تقابل الأخرى). احرص دائمًا على أن تكون الفهارس فريدة قبل إجراء المحاذاة الحسابية. الفهارس الصحيحة مقابل فهارس object — قد لا تتم محاذاة RangeIndex(0,5) وInt64Index([0,1,2,3,4]) بشكل مثالي بعد العمليات، لأن أحدهما مُستنتج والآخر مُحدَّد صراحةً. استخدم reset_index(drop=True) لتوحيدهما.

import pandas as pd

# Duplicate label alignment — produces unexpected expansion
s1 = pd.Series([1, 2, 3], index=['a', 'a', 'b'])
s2 = pd.Series([10, 20], index=['a', 'b'])

result = s1 + s2
print('Result with duplicate indices (unexpected expansion):')
print(result)
print('\nAlways ensure unique indices before arithmetic!')

تحقق سريع

اختبر مدى فهمك لمحاذاة الفهارس وإعادة فهرستها مما تعلمته في هذا الدرس.

مراجعة الدرس

تعلمت في هذا الدرس أن Pandas تُجري محاذاة تلقائية للفهرس عند إجراء العمليات الحسابية، فتنتج NaN للتسميات غير المتطابقة، وأن reindex() توائم DataFrame مع مجموعة مستهدفة من التسميات، مع خيارات لملء التسميات المفقودة، وأن align() تزامن كائنين مع الفهرس نفسه في آن واحد. بعد ذلك سنستكشف فوائد الفهارس المرتبة من حيث الأداء، وكيفية قياسها باستخدام timeit.

الأسئلة الشائعة

هل درس «محاذاة الفهارس وإعادة فهرستها» مجاني؟

نعم — نص درس «محاذاة الفهارس وإعادة فهرستها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Pandas & NumPy Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.

ماذا ستتعلم في «محاذاة الفهارس وإعادة فهرستها»؟

حاذِ اثنين من DataFrame إلى فهرس مشترك باستخدام reindex()، واملأ التسميات المفقودة، وافهم كيفية محاذاة الفهارس في العمليات الحسابية. تتمرن على Pandas & NumPy Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Pandas & NumPy Academy؟

لا تُشترط خبرة سابقة. Pandas & NumPy Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «محاذاة الفهارس وإعادة فهرستها»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Pandas & NumPy Academy هذا؟

نعم. كل درس في Pandas & NumPy Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. إنشاء MultiIndex
  2. تحديد البيانات من MultiIndex
  3. محاذاة الفهارس وإعادة فهرستها
  4. فوائد الأداء للفهرس المرتب
← العودة إلى Pandas & NumPy Academy