فوائد الأداء للفهرس المرتب
افرز MultiIndex باستخدام sort_index()، وقِس أداء الشرائح باستخدام timeit، واستخدم is_monotonic_increasing كحارس.
فوائد الأداء للفهرس المرتب درس مجاني في Pandas & NumPy Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Pandas & NumPy Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.
لماذا يهم ترتيب الفهرس للأداء
يتيح الفهرس المرتب لـ Pandas استخدام البحث الثنائي (O(log n)) بدلًا من الفحص الخطي الكامل (O(n)) عند البحث عن نطاقات التسميات. في DataFrame يضم مليون صف، يعثر البحث الثنائي على النطاق المستهدف في نحو 20 مقارنة، مقابل ما يصل إلى مليون مقارنة في الفحص الخطي. وهذا يجعل عمليات التقطيع على MultiIndexes المرتبة أسرع بفارق كبير جدًا من نظيراتها على الفهارس غير المرتبة — ويصبح هذا الفرق حاسمًا في مسارات الإنتاج التي تعالج ملايين الصفوف.
import pandas as pd
import numpy as np
np.random.seed(42)
# Create a large DataFrame with a MultiIndex
countries = ['DE', 'UK', 'USA', 'FR', 'JP']
dates = pd.date_range('2020-01-01', periods=200)
mi = pd.MultiIndex.from_product([countries, dates], names=['country', 'date'])
df = pd.DataFrame({'value': np.random.randn(len(mi))}, index=mi)
print(f'DataFrame shape: {df.shape}')
print(f'Index is sorted: {df.index.is_monotonic_increasing}')التحقق من ترتيب الفهرس
استخدم df.index.is_monotonic_increasing للتحقق مما إذا كان الفهرس مرتبًا ترتيبًا تصاعديًا. وتعيد هذه الخاصية قيمة منطقية. بالنسبة إلى MultiIndex، تتحقق Pandas من الترتيب المعجمي عبر جميع المستويات. احرص دائمًا على إجراء هذا التحقق قبل تنفيذ عمليات التقطيع باستخدام .loc[start:end] على MultiIndex — فقد يرفع الفهرس غير المرتب UnsortedIndexError أو يعيد نتائج غير صحيحة بصمت، بحسب إصدار Pandas.
import pandas as pd
# Sorted MultiIndex
tuples_sorted = [('A', 1), ('A', 2), ('B', 1), ('B', 2)]
mi_sorted = pd.MultiIndex.from_tuples(tuples_sorted)
df_sorted = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi_sorted)
# Unsorted MultiIndex
tuples_unsorted = [('B', 2), ('A', 1), ('B', 1), ('A', 2)]
mi_unsorted = pd.MultiIndex.from_tuples(tuples_unsorted)
df_unsorted = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi_unsorted)
print('Sorted index is_monotonic_increasing:', df_sorted.index.is_monotonic_increasing)
print('Unsorted index is_monotonic_increasing:', df_unsorted.index.is_monotonic_increasing)الترتيب باستخدام sort_index()
تعيد df.sort_index() DataFrame جديدة مرتبة حسب تسمية الفهرس ترتيبًا تصاعديًا. استخدم ascending=False للترتيب التنازلي. بالنسبة إلى MultiIndex، يكون الترتيب معجميًا: إذ يُرتب حسب المستوى الخارجي أولًا، ثم حسب المستويات الداخلية ضمن كل مجموعة خارجية. احرص دائمًا على الترتيب بعد أي عملية قد تخل بترتيب الفهرس، مثل pd.concat أو التصفية أو إلحاق صفوف جديدة.
import pandas as pd
import numpy as np
np.random.seed(0)
countries = ['USA', 'UK', 'DE']
years = [2021, 2022, 2023]
mi = pd.MultiIndex.from_product([countries, years], names=['country', 'year'])
df = pd.DataFrame({'gdp': np.random.randint(3000, 26000, 9)}, index=mi)
print('Before sort_index():')
print(df.head(4))
df_sorted = df.sort_index()
print('\nAfter sort_index():')
print(df_sorted.head(4))
print('Is sorted:', df_sorted.index.is_monotonic_increasing)قياس زمن البحث باستخدام timeit
تقيس وحدة timeit في Python المدة التي يستغرقها تنفيذ عبارة ما، وذلك بتشغيلها مرات عديدة وحساب متوسط الزمن. استخدمها لقياس أداء عمليات البحث في الفهارس المرتبة وغير المرتبة. في IPython/Jupyter، توفر التعليمة السحرية %timeit الوظيفة نفسها مع مخرجات أوضح. وتُعد المقارنة المعيارية الطريقة الموثوقة الوحيدة للتأكد من أن تحسينًا في الأداء قد أفاد فعلًا — فلا تفترض أبدًا أن تغييرًا ما أسرع من دون قياسه.
import pandas as pd
import numpy as np
import timeit
np.random.seed(0)
N = 500000
idx = np.random.choice(['A','B','C','D','E'], N)
df_unsorted = pd.DataFrame({'v': np.random.randn(N)}, index=idx)
df_sorted = df_unsorted.sort_index()
# Time label lookup: sorted vs unsorted
t_unsorted = timeit.timeit(lambda: df_unsorted.loc['C'], number=100)
t_sorted = timeit.timeit(lambda: df_sorted.loc['C'], number=100)
print(f'Unsorted lookup (100 runs): {t_unsorted:.3f}s')
print(f'Sorted lookup (100 runs): {t_sorted:.3f}s')
print(f'Speedup: {t_unsorted/t_sorted:.1f}x')تحذير PerformanceWarning من MultiIndex غير مرتب
تصدر Pandas تحذيرًا من النوع PerformanceWarning عند تقطيع MultiIndex غير المرتب معجميًا: 'indexing past lexsort depth may impact performance'. يعني هذا التحذير أن Pandas اضطرت إلى الرجوع إلى الفحص الخطي بدلًا من البحث الثنائي. ومع أنه يعيد نتائج صحيحة في الحالات البسيطة، فإنه قد يعيد نتائج غير صحيحة عند تقطيع المستويات الداخلية لفهرس متعدد المستويات غير مرتب. تعامل مع هذا التحذير كأنه خطأ، وأصلح السبب الجذري بترتيب الفهرس.
import pandas as pd
import warnings
# Create an unsorted MultiIndex and trigger the warning
tuples = [('B', 2), ('A', 1), ('B', 1), ('A', 2)]
mi = pd.MultiIndex.from_tuples(tuples, names=['letter', 'num'])
df = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi)
print('Index sorted?', df.index.is_monotonic_increasing)
# This may trigger PerformanceWarning in some Pandas versions
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
try:
result = df.loc['A':'B', :]
print('Result:', result)
if w:
print('Warning:', str(w[0].message))
except Exception as e:
print('Error (common with newer Pandas):', type(e).__name__)قياس أداء تقطيع MultiIndex المرتب وغير المرتب
يكون تقطيع MultiIndex المرتب أسرع بكثير، لأن Pandas تستطيع إجراء بحث ثنائي في مصفوفتَي المستوى الخارجي والمستوى الداخلي. لنقِس أداء تقطيع MultiIndex كبير يضم مليون صف — وهو حجم شائع في مسارات تحليلات الإنتاج. يتجنب الإصدار المرتب الفحص الخطي، ويحقق باستمرار تسارعًا يتراوح بين 5 و50 ضعفًا، بحسب انتقائية التقطيع.
import pandas as pd
import numpy as np
import timeit
np.random.seed(42)
countries = ['DE', 'UK', 'USA', 'FR', 'JP']
dates = pd.date_range('2010-01-01', periods=200000)
# Sample a random subset for timing test
sample_countries = np.random.choice(countries, 100000)
sample_dates = np.random.choice(dates, 100000)
df = pd.DataFrame({
'country': sample_countries,
'date': sample_dates,
'value': np.random.randn(100000)
}).set_index(['country', 'date'])
df_sorted = df.sort_index()
print('Dataset size:', len(df))
print('Sorted:', df_sorted.index.is_monotonic_increasing)
t = timeit.timeit(lambda: df_sorted.loc['USA'], number=50)
print(f'Sorted lookup (50 runs): {t:.3f}s')sort_index مع المعامل level
في MultiIndex، يمكنك الترتيب حسب مستوى محدد بدلًا من ترتيب جميع المستويات باستخدام المعامل level: df.sort_index(level='year'). ويفيد ذلك عندما تريد الحفاظ على التجميع حسب المستوى الخارجي، مع إعادة ترتيب الصفوف داخل كل مجموعة خارجية. كما أن الوسيط sort_remaining=True (وهو الافتراضي) يرتب أي مستويات غير مرتبة تتجاوز المستوى المحدد، مما يضمن ترتيبًا معجميًا كاملًا.
import pandas as pd
import numpy as np
countries = ['USA', 'UK']
years = [2023, 2021, 2022] # deliberately unordered
mi = pd.MultiIndex.from_product([countries, years], names=['country', 'year'])
df = pd.DataFrame({'v': range(6)}, index=mi)
print('Before sorting by year level:')
print(df)
# Sort by the inner level (year) only
df_ysorted = df.sort_index(level='year')
print('\nAfter sort_index(level="year"):')
print(df_ysorted)is_monotonic_increasing كحاجز حماية لمسار المعالجة
في مسارات معالجة الإنتاج، أضف حاجز تحقق من الترتيب في بداية أي دالة تستقبل DataFrame مع MultiIndex وتنفذ عمليات تقطيع. إذا لم يكن الفهرس مرتبًا، فرتبه تلقائيًا وسجل تحذيرًا. يمنع ذلك تدهور الأداء بصمت أو ظهور نتائج غير صحيحة عند تغيير ترتيب DataFrame في الشيفرة السابقة. ويكون وضع حاجز التحقق عند حدود الدالة أكثر موثوقية من افتراض أن المستدعين يمررون بيانات مرتبة دائمًا.
import pandas as pd
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def safe_slice(df, key):
'''Slice a MultiIndex DataFrame, sorting if necessary.'''
if not df.index.is_monotonic_increasing:
logger.warning('Index not sorted — sorting now. This is a performance cost.')
df = df.sort_index()
return df.loc[key]
# Test with an unsorted DataFrame
tuples = [('B', 2), ('A', 1), ('B', 1), ('A', 2)]
mi = pd.MultiIndex.from_tuples(tuples, names=['letter', 'num'])
df = pd.DataFrame({'v': [10, 20, 30, 40]}, index=mi)
result = safe_slice(df, 'A')
print('Slice result for A:')
print(result)الفهرس المرتب للبحث الثنائي في الفهارس البسيطة
تنطبق فوائد الترتيب المتعلقة بالأداء أيضًا على الفهارس العادية (غير متعددة المستويات). فعند استخدام DatetimeIndex في تحليل السلاسل الزمنية، تصبح عمليات تقطيع نطاقات التواريخ أسرع بكثير عندما يكون الفهرس مرتبًا. كما يتيح الفهرس النصي المرتب أبجديًا إجراء بحث ثنائي عن التسميات. وبالنسبة إلى Series كبيرة لأسعار الأسهم مفهرسة بالطوابع الزمنية، قد يؤدي ترتيب DatetimeIndex إلى تحويل عملية تقطيع تستغرق 100ms إلى عملية تستغرق أقل من ميلي ثانية.
import pandas as pd
import numpy as np
import timeit
np.random.seed(0)
# Random timestamps — unsorted
timestamps = pd.date_range('2020-01-01', periods=500000, freq='min')
shuffled = np.random.permutation(timestamps)
prices = pd.Series(np.random.randn(500000), index=shuffled)
prices_sorted = prices.sort_index()
# Time a date range slice
t_unsorted = timeit.timeit(lambda: prices['2020-06-01':'2020-06-30'], number=20)
t_sorted = timeit.timeit(lambda: prices_sorted['2020-06-01':'2020-06-30'], number=20)
print(f'Unsorted: {t_unsorted:.3f}s')
print(f'Sorted: {t_sorted:.3f}s')
print(f'Speedup: {t_unsorted/t_sorted:.0f}x')كلفة الترتيب من حيث الذاكرة
الترتيب ليس مجانيًا — إذ تنشئ sort_index() نسخة جديدة من DataFrame (ما لم تستخدم inplace=True، الذي يعدّلها في مكانها). وبالنسبة إلى DataFrame كبيرة جدًا، يؤدي ذلك مؤقتًا إلى مضاعفة الاستخدام الأقصى للذاكرة. تتمثل الاستراتيجية العملية في الترتيب مرة واحدة عند التحميل والاحتفاظ بالنسخة المرتبة طوال مسار المعالجة، بدلًا من الترتيب مرارًا. وإذا كانت الذاكرة محدودة، فاستخدم الترتيب في المكان مع df.sort_index(inplace=True) لتجنب النسخة المؤقتة.
import pandas as pd
import numpy as np
np.random.seed(0)
countries = ['DE', 'UK', 'USA']
years = [2021, 2022, 2023]
mi = pd.MultiIndex.from_product([countries, years], names=['country', 'year'])
df = pd.DataFrame({'v': np.random.randn(9)}, index=mi)
# Sort once at load time — best practice
df.sort_index(inplace=True) # no temporary copy
assert df.index.is_monotonic_increasing, 'Index must be sorted!'
print('Pipeline-ready DataFrame (sorted in place):')
print(df)ملخص أفضل ممارسات الفهرس المرتب
القواعد الأساسية لأداء الفهرس: 1) استدعِ sort_index() دائمًا بعد أي عملية قد تخل بترتيب الفهرس (الضم، الدمج، الإلحاق، التصفية). 2) استخدم is_monotonic_increasing كحاجز تحقق في الدوال التي تقطع الفهرس. 3) رتّب البيانات عند تحميلها واحتفظ بـ DataFrame المرتبة طوال مسار المعالجة لتجنب الترتيب المتكرر. 4) في DataFrame التي تستخدم MultiIndex، تأكد من ترتيب جميع المستويات، وليس المستوى الخارجي فقط. 5) استخدم timeit للتحقق من أن الترتيب يحقق تسارعًا متوقعًا فعلًا في مسار المعالجة المحدد لديك.
تحقق سريع
اختبر مدى فهمك لأداء الفهارس المرتبة مما تعلمته في هذا الدرس.
مراجعة الدرس
تعلمت في هذا الدرس أن is_monotonic_increasing تتحقق مما إذا كان الفهرس مرتبًا وما إذا كان البحث الثنائي متاحًا، وأن sort_index() ترتب البيانات في مكانها أو تعيد نسخة مرتبة، وأن timeit تقيس التسارع الفعلي للتأكد من الفائدة. بعد ذلك سنستكشف دوال النوافذ — إحصاءات متحركة ومتوسعة للسلاسل الزمنية والبيانات المالية.
تعلم Python مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 30
- الدروس
- 120
الأسئلة الشائعة
هل درس «فوائد الأداء للفهرس المرتب» مجاني؟
نعم — نص درس «فوائد الأداء للفهرس المرتب» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Pandas & NumPy Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.
ماذا ستتعلم في «فوائد الأداء للفهرس المرتب»؟
افرز MultiIndex باستخدام sort_index()، وقِس أداء الشرائح باستخدام timeit، واستخدم is_monotonic_increasing كحارس. تتمرن على Pandas & NumPy Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Pandas & NumPy Academy؟
لا تُشترط خبرة سابقة. Pandas & NumPy Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «فوائد الأداء للفهرس المرتب»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Pandas & NumPy Academy هذا؟
نعم. كل درس في Pandas & NumPy Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- إنشاء MultiIndex
- تحديد البيانات من MultiIndex
- محاذاة الفهارس وإعادة فهرستها
- فوائد الأداء للفهرس المرتب