apply() مع GroupBy
مرّر دالة متعددة الصفوف إلى groupby().apply() لحساب ملخصات معقدة على مستوى المجموعة لا يستطيع agg() التعبير عنها.
apply() مع GroupBy درس مجاني في Pandas & NumPy Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Pandas & NumPy Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.
لماذا تحتاج GroupBy إلى apply()
تتعامل الطريقة المضمّنة agg() مع عمليات التجميع البسيطة مثل الجمع والمتوسط والعدّ، بحيث تنتج قيمة مفردة واحدة لكل مجموعة. لكن بعض الحسابات على مستوى المجموعة تتطلب فحص DataFrame الفرعي الكامل للمجموعة، وليس عمودًا واحدًا فقط. تمرّر groupby().apply(func) إلى دالتكم DataFrame المجموعة بالكامل، ثم تجمع النتائج، مما يتيح إنشاء ملخّصات معقّدة لا يمكن التعبير عنها باستخدام agg().
import pandas as pd
import numpy as np
df = pd.DataFrame({
'region': ['North', 'North', 'South', 'South', 'North'],
'product': ['A', 'B', 'A', 'A', 'C'],
'revenue': [100, 250, 180, 90, 300]
})
print(df)إرجاع قيمة مفردة لكل مجموعة
عندما تُرجع الدالة المُمرَّرة إلى groupby().apply() قيمة مفردة، تكون النتيجة Series مفهرسة بمفاتيح المجموعات، تمامًا كما تنتج agg(). يكون هذا الأسلوب مفيدًا عندما تتطلب القيمة المفردة منطقًا يعتمد على عدة أعمدة، مثل حساب نسبة إيرادات المنتج الأعلى مبيعًا إلى إجمالي إيرادات المجموعة، وهو ما لا يمكن التعبير عنه في مواصفة عمود واحدة ضمن agg().
def top_product_share(group):
top = group['revenue'].max()
total = group['revenue'].sum()
return top / total
share = df.groupby('region').apply(top_product_share)
print(share)إرجاع Series لكل مجموعة
عندما تُرجع الدالة كائن pd.Series، تكون النتيجة ذات MultiIndex: المستوى الخارجي هو مفتاح المجموعة، والمستوى الداخلي هو فهرس Series. يفيد ذلك في حساب عدة إحصاءات لكل مجموعة ضمن استدعاء apply واحد، وإنشاء جدول ملخّص يحتوي على عدة صفوف للمقاييس الخاصة بكل مجموعة.
def group_stats(group):
return pd.Series({
'total': group['revenue'].sum(),
'top_product': group.loc[group['revenue'].idxmax(), 'product'],
'n_products': group['product'].nunique()
})
result = df.groupby('region').apply(group_stats)
print(result)إرجاع DataFrame لكل مجموعة
عندما تُرجع الدالة كائن pd.DataFrame، تقوم groupby().apply() بضم جميع DataFrames الفرعية عموديًا. وهذا هو الأسلوب الأكثر قوة؛ إذ يتيح لكم تصفية الصفوف أو تحويلها داخل كل مجموعة، ثم إرجاع مجموعة فرعية معدّلة. على سبيل المثال، يمكن الاحتفاظ بأفضل منتجين من حيث الإيرادات داخل كل منطقة.
def top2_per_region(group):
return group.nlargest(2, 'revenue')
top2 = df.groupby('region').apply(top2_per_region)
print(top2.reset_index(drop=True))حساب درجات Z داخل المجموعات
من الاستخدامات الشائعة لـ groupby().apply() حساب التوحيد القياسي داخل المجموعة. فبدلًا من تطبيع الإيرادات بالنسبة إلى جميع الطلبات، مما يخلط بين المناطق، احسبوا درجة Z للإيرادات داخل كل منطقة. تعني درجة Z تساوي 2 في منطقة الشمال «انحرافين معياريين فوق متوسط الشمال»، وهو معيار أكثر دلالة من كونها انحرافين معياريين فوق المتوسط العام.
def within_group_zscore(group):
mean = group['revenue'].mean()
std = group['revenue'].std()
group = group.copy()
group['revenue_z'] = (group['revenue'] - mean) / std
return group
df_z = df.groupby('region').apply(within_group_zscore).reset_index(drop=True)
print(df_z)تجميع مخصّص: المتوسط Winsorised
يقصّ المتوسط Winsorised القيم المتطرفة قبل حساب المتوسط، مما يجعله أكثر متانة من المتوسط البسيط في التوزيعات الملتوية. لا يمكن التعبير عن هذا التجميع المخصّص باستخدام دوال agg() القياسية، لكنه سهل التنفيذ باستخدام groupby().apply(): اقطع القيم عند المئين الخامس والمئين الخامس والتسعين داخل كل مجموعة، ثم احسب المتوسط للقيم المقطوعة.
def winsorised_mean(group, lower_pct=0.05, upper_pct=0.95):
col = group['revenue']
lo = col.quantile(lower_pct)
hi = col.quantile(upper_pct)
clipped = col.clip(lo, hi)
return clipped.mean()
wins_mean = df.groupby('region').apply(winsorised_mean)
print('Winsorised mean by region:')
print(wins_mean)نافذة متحركة مخصّصة لكل مجموعة
تتجاهل النوافذ المتحركة المطبَّقة على DataFrame بالكامل حدود المجموعات. فإذا حسبتم متوسطًا متحركًا من 3 صفوف على سلسلة زمنية تتناوب فيها بيانات منتجين، فستتجاوز النافذة حدود المنتجين بطريقة غير صحيحة. طبّقوا النافذة المتحركة داخل groupby().apply() لضمان بقاء كل عملية حساب داخل مجموعتها، مثل حساب متوسط متحرك للإيرادات على مدى 3 أشهر لكل منطقة.
def group_rolling_mean(group, window=3):
group = group.sort_values('order_date').copy()
group['rolling_revenue'] = group['revenue'].rolling(window, min_periods=1).mean()
return group
# df_ts has order_date column
# df_rolled = df_ts.groupby('region').apply(group_rolling_mean).reset_index(drop=True)
print('Rolling window per group applied inside apply()')المعامل include_groups (Pandas 2.2 فأحدث)
في Pandas 2.2 والإصدارات الأحدث، تُصدر groupby().apply() تحذيرًا من نوع FutureWarning إذا ظهرت مفاتيح التجميع في DataFrame الذي تستلمه الدالة. مرّروا include_groups=False لاستبعاد أعمدة التجميع من DataFrame الفرعي المُمرَّر إلى الدالة. ويؤدي ذلك إلى تجنّب التحذير والعمليات غير المقصودة على أعمدة المفاتيح داخل جسم الدالة.
def revenue_only(group):
# group does not include 'region' column when include_groups=False
return group['revenue'].sum()
# result = df.groupby('region').apply(revenue_only, include_groups=False)
print('include_groups=False avoids FutureWarning in Pandas 2.2+')دمج نتائج apply() مع reset_index
عندما تُرجع groupby().apply() DataFrame لكل مجموعة، تكون النتيجة ذات MultiIndex يتضمن مفتاح المجموعة في المستوى الخارجي. استخدموا reset_index(drop=True) لتحويل الفهرس إلى نطاق أعداد صحيح عادي. وبدلًا من ذلك، يمكنكم استخدام reset_index(level=0) لرفع مفتاح المجموعة إلى عمود، بحيث يظهر بوضوح في الناتج.
result = df.groupby('region').apply(top2_per_region)
print('With MultiIndex:')
print(result.head())
print('\nAfter reset_index:')
print(result.reset_index(drop=True))متى يُفضَّل استخدام transform() بدلًا من apply()
تُستخدم groupby().apply() للحسابات المعقّدة على مستوى المجموعة التي تُرجع شكلًا مختلفًا عن شكل الإدخال. أما groupby().transform() فتُستخدم للحسابات التي تضيف عمودًا جديدًا متوافقًا مع الفهرس الأصلي، مثل بثّ متوسط المجموعة مرة أخرى إلى كل صف بغرض التطبيع. استخدموا transform عندما تحتاجون إلى نتيجة بنفس شكل DataFrame الأصلي، واستخدموا apply عندما يختلف شكل النتيجة.
# transform: adds group mean back to each row
df['group_mean_revenue'] = df.groupby('region')['revenue'].transform('mean')
# apply: computes one scalar per group
group_totals = df.groupby('region')['revenue'].apply(sum)
print(df[['region', 'revenue', 'group_mean_revenue']])نصيحة لتحسين الأداء: تجنّبوا apply() في عمليات التجميع البسيطة
تكون groupby().apply() أبطأ بكثير من groupby().agg() في العمليات البسيطة، لأن apply() تنشئ كائن Python كاملًا لكل مجموعة. لذلك استخدموا agg() دائمًا للجمع والمتوسط والعدّ. واحتفظوا باستخدام apply() للحالات التي تتطلب فعلًا DataFrame المجموعة بالكامل، مثل المنطق الشرطي متعدد الأعمدة أو الإحصاءات المخصّصة أو التصفية داخل المجموعات.
# SLOW: apply for simple sum
slow = df.groupby('region').apply(lambda g: g['revenue'].sum())
# FAST: native agg
fast = df.groupby('region')['revenue'].sum()
print('Both produce the same result:')
print(fast.equals(slow))تحقّق سريع
اختبروا مدى فهمكم لمفاهيم تحليل البيانات التي تناولها هذا الدرس.
مراجعة الدرس
في هذا الدرس تعلّمتُم: إرجاع قيم مفردة وSeries وDataFrames من groupby().apply()، وحساب درجات Z داخل المجموعات وإحصاءات مخصّصة متينة، والاختيار بين apply() وagg() وtransform() لتنفيذ العمليات على مستوى المجموعات. بعد ذلك، سنستكشف map() وapplymap() لإجراء العمليات على مستوى العناصر في Series وDataFrames.
تعلم Python مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 30
- الدروس
- 120
الأسئلة الشائعة
هل درس «apply() مع GroupBy» مجاني؟
نعم — نص درس «apply() مع GroupBy» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Pandas & NumPy Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.
ماذا ستتعلم في «apply() مع GroupBy»؟
مرّر دالة متعددة الصفوف إلى groupby().apply() لحساب ملخصات معقدة على مستوى المجموعة لا يستطيع agg() التعبير عنها. تتمرن على Pandas & NumPy Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Pandas & NumPy Academy؟
لا تُشترط خبرة سابقة. Pandas & NumPy Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «apply() مع GroupBy»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Pandas & NumPy Academy هذا؟
نعم. كل درس في Pandas & NumPy Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- apply() على الأعمدة والصفوف
- apply() مع GroupBy
- map() وapplymap() للعمليات على مستوى العناصر
- تسلسل الأساليب باستخدام pipe()