الوحدة 7 — متغيّرات التجميع والنوافذ الزمنية
كانت الوحدات السابقة تحوّل صفًّا واحدًا في كلّ مرّة. أمّا هنا فتُبنى المعلومة بالنظر إلى صفوف عدّة معًا: تاريخ عميل، ومتوسّط فئة، واتّجاه الأيّام السبعة الأخيرة. وهذا هو الميدان الذي تعطي فيه هندسة المتغيّرات أكبر عوائدها — والذي يتربّص فيه تسرّب البيانات عند كلّ خطوة.
التجميع حسب المجموعة: تلخيص تاريخ
الحالة النمطية: جدول عمليات، وصفوف عدّة لكلّ عميل، ونموذج يتنبّأ على مستوى العميل. فلا بدّ من تلخيص التاريخ في متغيّرات.
agg = df.groupby("رقم_العميل").agg(
عدد_المشتريات = ("المبلغ", "count"),
متوسط_المبلغ = ("المبلغ", "mean"),
وسيط_المبلغ = ("المبلغ", "median"),
أقصى_مبلغ = ("المبلغ", "max"),
انحراف_المبلغ = ("المبلغ", "std"),
عدد_الفئات = ("الفئة", "nunique"),
)
وتلتقط كلّ دالّة تجميع وجهًا مختلفًا من السلوك. فـالعدّ يقيس كثافة العلاقة، والمتوسّط المستوى المعتاد، والوسيط المستوى نفسه مع صمود أمام المتطرّفات، والأقصى قدرة الذروة، والانحراف المعياري الانتظام — فالعميل ذو السلّة المستقرّة لا يشبه عميلًا متقلّبًا له المتوسّط نفسه. أمّا عدد القيم المتمايزة فيقيس التنوّع، وهو تنبّئيّ بالوفاء في كثير من الأحيان.
الفارق عن العادة: المتغيّر الذي يغيّر كلّ شيء
هذا هو نوع المتغيّر المذكور في الوحدة 1، وهو يستحقّ البناء المنهجيّ. فعملية بخمسمئة يورو لا معنى لها في المطلق: فهي عادية لعميل يصرف ستّمئة يورو في المتوسّط، ومريبة جدًّا لعميل معتاد على ثلاثين.
فالمعلومة ليست المبلغ إذًا بل الفارق بين المبلغ والعادة:
df["متوسط_العميل"] = df.groupby("رقم_العميل")["المبلغ"].transform("mean")
df["انحراف_العميل"] = df.groupby("رقم_العميل")["المبلغ"].transform("std")
df["الفارق_النسبي"] = (df["المبلغ"] - df["متوسط_العميل"]) / df["انحراف_العميل"]
وهذا المتغيّر الأخير درجة z محسوبة لكلّ عميل لا على السكّان. وهو نمط تفكير يُعاد استخدامه على نطاق واسع: نسبةُ القيمة إلى مرجعها الوجيه — تاريخ العميل، أو متوسّط فئة منتَجه، أو المعتاد في الموسم. ومن هنا تأتي كثير من عوائد الأداء، لا من خوارزمية أعقد.
ولاحِظ أنّ transform أفضل من agg هنا: فهو يُرجع قيمة لكلّ صفّ أصليّ فيندمج اندماجًا طبيعيًّا من دون دمج يدويّ.
النوافذ المتحرّكة والإزاحات: القاعدة الذهبية
على البيانات المرتّبة زمنيًّا، تريد تلخيص الماضي القريب: متوسّط الأيّام السبعة الأخيرة، ومجموع الشهر المنقضي، والاتّجاه. وهذه المتغيّرات قويّة، وهي المصدر الأوّل للتسرّب في هذه الدورة.
والقاعدة مطلقة: لا يجوز أن تحتوي النافذة إلّا على ماضٍ صريح. فالمتوسّط المتحرّك المركزيّ، أو نافذةٌ تضمّ الصفّ الجاري، يستخدم القيمة نفسها التي تحاول التنبّؤ بها.
df = df.sort_values(["رقم_العميل", "التاريخ"])
# القيمة السابقة: الماضي بلا لبس
df["المبلغ_السابق"] = df.groupby("رقم_العميل")["المبلغ"].shift(1)
# متوسط الصفوف السبعة السابقة: shift(1) قبل rolling
df["متوسط_7"] = (
df.groupby("رقم_العميل")["المبلغ"]
.transform(lambda s: s.shift(1).rolling(7, min_periods=1).mean())
)
ووضعُ shift(1) قبل rolling هو التفصيل الذي يحسم صحّة النموذج كلّه. فمن دونه تضمّ النافذة الصفّ الجاري: فيحتوي المتغيّر على جزء من الهدف، وتصير درجة التدريب مذهلة، ثمّ ينهار الإنتاج. وهذا أكثر الأخطاء شيوعًا على البيانات الزمنية.
ومتى أُرسيت هذه الأسس تتالت المتغيّرات المشتقّة: فنسبةُ متوسّط الأيّام السبعة الأخيرة إلى متوسّط الثلاثين تقيس تسارعًا؛ والفارق عن القيمة السابقة يقيس تغيّرًا؛ وعددُ الأيّام منذ آخر حدث يقيس حداثة على مستوى الصفّ.
التجميع حسب الفئة لا حسب الفرد
لا يقتصر التجميع على الأفراد. فالتلخيص حسب فئة المنتَج أو المنطقة أو يوم الأسبوع يوفّر مرجعًا تُنسَب إليه كلّ رصدة: هل هذا المنتَج غالٍ بالنسبة إلى فئته؟ وهل يتفوّق هذا المتجر على منطقته؟
لكن احترِس: فتجميع الهدف حسب الفئة هو الترميز بالهدف من الوحدة 4، بالشرط نفسه من تنعيم وحساب خارج العيّنة. أمّا تجميع متغيّرات غير الهدف فلا يطرح هذه المشكلة.
أوّلًا ترتيب الفرز: فمن دون sort_values على التاريخ يُنتج shift وrolling عبثًا في صمت. وثانيًا المجموعة: فنسيان groupby يخلط تواريخ عملاء عدّة. وثالثًا الإتاحة: هل سيكون هذا التجميع قابلًا للحساب في الإنتاج، لحظة التنبّؤ، بالبيانات المعلومة سلفًا وحدها؟ فمتغيّرٌ يقتضي جدولًا كاملًا يُعاد حسابه كلّ ليلة لا يحمل كلفة shift بسيط.
الخلاصة
- التجميع حسب المجموعة يلخّص تاريخًا؛ والعدّ والمتوسّط والوسيط والأقصى والانحراف المعياري وعدد القيم المتمايزة يلتقط كلٌّ منها وجهًا من السلوك.
- الفارق عن العادة (درجة z لكلّ فرد) أكثر تنبّؤًا من القيمة الخامّ غالبًا: ونسبةُ القيمة إلى مرجعها الوجيه انعكاسٌ يُعمَّم.
- لا يجوز أن تحتوي النافذة المتحرّكة إلّا على ماضٍ صريح:
shift(1)قبلrolling، وإلّا وقع تسرّب هائل. - التجميع حسب الفئة يوفّر مراجع نافعة، أمّا تجميع الهدف فهو الترميز بالهدف واحتراساته.
الوحدة التالية: تسرّب البيانات، وأشكاله الأكثر شيوعًا، والإشارات التي يجب أن تنبّه.