برمجة بايثونتحليل البياناتعلم البيانات

بانداس: كيفية تجميع الصفوف إلى قائمة باستخدام GroupBy

دليل أكاديمي شامل يشرح كيفية تجميع صفوف البيانات في قوائم باستخدام دالة GroupBy في مكتبة Pandas مع أمثلة برمجية وتحليل للأداء والذاكرة.

تاريخ النشر

تُعد معالجة البيانات وإعادة تشكيلها حجر الزاوية في مشاريع علم البيانات وهندسة التحليلات المتقدمة. في بيئة بايثون البرمجية، تبرز مكتبة Pandas كأداة لا غنى عنها للتعامل مع البيانات المجدولة واستكشافها وتعديلها بكفاءة متناهية. ومن بين العمليات الجوهرية التي تواجه مهندسي البيانات وعلماء الذكاء الاصطناعي بشكل متكرر هي عملية تجميع السجلات المتعددة التي تشترك في خاصية فئوية معينة ضمن هيكل بياني مرن، وتحديداً تحويل الصفوف الموزعة إلى قوائم مترابطة داخل خلايا مجمعة باستخدام واجهة GroupBy البرمجية.

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

يقدم هذا المرجع الشامل دليلاً تقنياً وأكاديمياً معمقاً حول كيفية استغلال إمكانيات دالة GroupBy لتجميع الصفوف في قوائم وهياكل بيانية متقدمة. سنستعرض الأسس النظرية لنموذج التقسيم والتطبيق والدمج، والتحليل المقارن للأداء بين الدوال المختلفة، وطرق إدارة القيم المفقودة والتكرارات، بالإضافة إلى دراسة معمقة لكيفية تحسين استهلاك الذاكرة وتوسيع نطاق العمليات للبيانات الضخمة وربطها بنماذج التعلم العميق وهندسة الميزات المتقدمة.

1. مقدمة إلى دالة GroupBy ومفهوم تجميع الصفوف في قوائم ضمن مكتبة Pandas

1.1 المفهوم النظري لنموذج التقسيم والتطبيق والدمج (Split-Apply-Combine)

يرتكز محرك التحليل الجماعي في مكتبة Pandas على النموذج النظري الشهير المعروف باسم “التقسيم والتطبيق والدمج” (Split-Apply-Combine)، وهو مفهوم تمت صياغته وتأصيله أكاديمياً بواسطة هادلي ويكهام في سياق معالجة البيانات الإحصائية. في المرحلة الأولى، مرحلة التقسيم (Split)، يتم تفكيك إطار البيانات الأصلي إلى مجموعات فرعية منطقية استناداً إلى قيم فريدة لمتغير فئوي واحد أو أكثر، حيث يتم إنشاء فهارس داخلية تربط كل مفتاح تجميع بصفوف البيانات المقابلة له دون الحاجة بالضرورة إلى نسخ البيانات فعلياً في الذاكرة.

تأتي بعد ذلك مرحلة التطبيق (Apply)، وهي المرحلة المحورية التي تتمايز فيها العمليات التحليلية. في السيناريوهات التقليدية، تُطبق دوال الاختزال الإحصائي مثل حساب الانحراف المعياري أو الجمع، مما يقلص مجموعة الصفوف التابعة لكل فئة إلى قيمة رقمية مفردة. لكن عند استهداف تجميع الصفوف في قوائم، فإن مرحلة التطبيق لا تهدف إلى ضغط البيانات كمياً، بل تسعى إلى تجميع الكائنات في مصفوفات ديناميكية تحتفظ بكافة العناصر الفردية المكونة للمجموعة الأصلية، متجاوزة بذلك حدود الاختزال الرقمي التقليدي.

أخيراً، تتولى مرحلة الدمج (Combine) إعادة تجميع هذه القوائم الناتجة وتوحيدها ضمن هيكل بياني جديد، قد يكون سلسلة أحادية البعد (Series) أو إطار بيانات متكامل (DataFrame). يتم ربط المفاتيح الفئوية الفريدة بالقوائم المقابلة لها، مع تعيين الفهارس المناسبة لضمان اتساق البيانات. يتيح هذا النموذج للمطورين التعامل مع التجميعات الهيكلية المعقدة بأعلى درجات الكفاءة البرمجية مع الحفاظ على مرونة لغة بايثون في التعامل مع الكائنات متعددة الأبعاد.

1.2 دواعي تجميع الصفوف في قوائم بدلاً من دوال التجميع القياسية

تفرض العديد من المشكلات التحليلية المعاصرة متطلبات تتجاوز الإحصاءات التلخيصية البسيطة؛ ففي حين يوفر المتوسط أو الوسيط الحسابي لمحة عامة عن توزيع البيانات، فإنه يطمس التباين الداخلي والتسلسل التاريخي الدقيق للأحداث. يُعد الحفاظ على الترتيب الزمني والنسقي للأحداث الفردية لكل كيان فئوي الدافع الأساسي لتحويل الصفوف إلى قوائم، حيث تصبح القائمة وعاءً حافظاً للمسار التاريخي لحركات المستخدمين، مثل تتابع النقرات على المواقع الإلكترونية أو التسلسل الزمني لعمليات الشراء المالية.

إلى جانب ذلك، تعتمد نماذج التعلم الآلي المتقدمة، وخاصة الشبكات العصبية العميقة المتخصصة في معالجة المتتاليات مثل الشبكات العصبية المتكررة ونماذج المحولات (Transformers)، على مدخلات موحدة تمثل متتابعات زمنية محددة. من خلال تجميع السجلات في قوائم، يمكن لمهندس البيانات صياغة ميزات تسلسلية تمثل المدخل المباشر لخوارزميات التنبؤ بالسلوك المستقبلي، متفادياً تعقيدات استعلامات قواعد البيانات المتعددة التي تستهلك وقتاً طويلاً وتفتقر إلى المرونة الهيكلية المطلوبة.

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

1.3 البنية الأساسية لبيئة العمل ومجموعة البيانات النموذجية

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

تتضمن مجموعة البيانات النموذجية المعتمدة في شروحات هذا المرجع جدولاً تمثيلياً يضم متغيرات متنوعة تشمل معرفات الفئات (مثل أسماء الفرق الرياضية أو معرفات العملاء)، وسلاسل نصية تصف الأحداث أو المنتجات، ومتغيرات رقمية تمثل النقاط أو القيم المالية، فضلاً عن طوابع زمنية توثق لحظة وقوع الحدث. يتيح هذا التنوع البياني محاكاة السيناريوهات الحقيقية واختبار سلوك دوال التجميع عبر مختلف أنواع البيانات الأساسية.

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

2. تجميع صفوف عمود واحد في قائمة باستخدام GroupBy ودالة agg(list)

2.1 الصيغة البرمجية الأساسية لتجميع عمود فردي

تعتمد الصيغة القياسية والأكثر كفاءة لتجميع قيم عمود محدد في قوائم على دمج واجهة GroupBy مع ميثود التجميع التراكمي المتمثل في دالة agg مع تمرير كائن نوع القائمة list كمعامل إجرائي. تُصاغ هذه العملية برمجياً عبر استدعاء إطار البيانات وتحديد عمود التجميع الفئوي، ثم استخلاص السلسلة المستهدفة وتطبيق الميثود بالشكل التالي: df.groupby('group_var')['values_var'].agg(list). تتميز هذه الصياغة بالوضوح الدلالي والمواءمة التامة مع معايير مكتبة Pandas المتقدمة.

عند تمرير دالة list المدمجة إلى ميثود agg، يقوم المحرك الداخلي بتجاوز دوال الاختزال الرياضي القياسية المكتوبة بلغة C، مستخدماً بدلاً من ذلك حلقة تجميع داخلية تقوم بإنشاء كائن قائمة بايثون قياسي لكل مفتاح فريد، ثم إلحاق عناصر المتغير المستهدف تباعاً داخل هذه القائمة وفق ترتيب ظهورها في إطار البيانات الأصلي. ينتج عن هذه العملية كائن من نوع Series يحمل فهرساً يمثل المفاتيح الفئوية الفريدة، بينما تتكون قيمه من كائنات قوائم ديناميكية.

يتطلب هذا التحويل إدراكاً عميقاً لطبيعة نوع البيانات الناتج؛ فالعمود الذي كان يحمل نوع بيانات أولي مثل الأعداد الصحيحة (int64) أو النصوص (object) يتحول هيكلياً إلى عمود من نوع كائن عام (object dtype) يحتوي على مؤشرات تشير إلى كائنات القوائم في الذاكرة العشوائية. يتيح ذلك مرونة غير محدودة في التعامل مع عناصر القائمة، ولكنه يغير بدوره نمط المعالجة اللاحق لتلك السلسلة البيانية، مستبعداً إمكانية تطبيق العمليات المتجهة (Vectorized Operations) المباشرة عليها.

2.2 تطبيق عملي على متغيرات النقاط والفرق الرياضية

لتوضيح الآلية بصورة ملموسة، نفترض وجود إطار بيانات يوثق سجلات الأداء للاعبين ينتمون إلى فرق رياضية مختلفة، حيث يحتوي الجدول على عمود الفئات ‘Team’ وعمود النقاط المسجلة ‘Points’. بتطبيق تعليمة التجميع: df.groupby('Team')['Points'].agg(list)، يتم تقسيم الجدول بناءً على أسماء الفرق، ثم تجميع كافة النقاط المسجلة لكل فريق في قائمة مفردة تظهر بجانب اسم الفريق في المخرجات.

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

من الجوانب الجوهرية في هذا السياق كيفية تعامل دالة agg(list) مع التكرارات الرقمية؛ فإذا سجل لاعب معين 15 نقطة في مباراتين مختلفتين لنفس الفريق، فإن القائمة الناتجة ستحتوي على القيمة 15 مكررة مرتين بالترتيب الدقيق لتسجيلها. هذا السلوك يضمن الحفاظ على التمثيل الإحصائي الحقيقي للتوزيع التكراري، وهو ما يميز دالة القائمة عن الدوال التي تفرض التفرد مثل المجموعات الرياضية.

2.3 إعادة تعيين الفهرس وتنظيم الجدول النهائي باستخدام reset_index

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

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

يوضح التحليل المقارن بين مخرجات التجميع مع استدعاء reset_index وبدونه فروقاً هيكلية واضحة؛ فبدون هذا الاستدعاء، تظل البيانات محصورة في كائن Series أحادي البعد مفهرس، بينما يؤدي استدعاؤه إلى ترقية الهيكل البياني إلى إطار بيانات كامل (DataFrame)، مما يتيح إعادة تسمية الأعمدة وتطبيق ميثودات التخصيص وإعادة الترتيب بسلاسة تامة تخدم خطوط أنابيب معالجة البيانات المعيارية.

3. تجميع أعمدة متعددة إلى قوائم موازية ومتزامنة

3.1 تطبيق دالة التجميع على كامل إطار البيانات دفعة واحدة

في كثير من التطبيقات المتقدمة، لا يقتصر الهدف على تجميع عمود واحد فقط، بل يمتد ليشمل تحويل كافة الخصائص والمتغيرات المرتبطة بكل سجل فئوي إلى قوائم متزامنة. تتيح مكتبة Pandas تحقيق ذلك بمرونة فائقة عبر تطبيق دالة التجميع مباشرة على كائن GroupBy الشامل لإطار البيانات دون تحديد عمود فرعي، وذلك باستخدام الصياغة المباشرة: df.groupby('Team').agg(list).

عند تنفيذ هذا الأمر، تتولى الآلية الداخلية تطبيق دالة إنشاء القوائم على كل عمود متبقٍ في الجدول بشكل مستقل ومتزامن لكل مجموعة فئوية. ينتج عن ذلك إطار بيانات جديد تتماثل فيه أسماء الأعمدة مع الجدول الأصلي، ولكن بدلاً من القيم الفردية، تحتوي كل خلية على قائمة من القيم المقابلة لتلك الفئة في ذلك العمود بالتحديد، مما ينشئ مصفوفات متوازية متعددة الأبعاد داخل نفس الصف.

يتميز الجدول الناتج بالمحاذاة الدقيقة للعناصر عبر كافة الأعمدة المجمعة؛ حيث يتطابق طول القائمة في العمود الأول مع طول القائمة في العمود الثاني لكافة الفئات، ويشير العنصر ذو الترتيب n في أي قائمة إلى نفس السجل الأصلي الذي انحدرت منه كافة العناصر ذات الترتيب n في القوائم الأخرى. تشكل هذه الخاصية أساساً متيناً لإعادة تركيب السجلات أو إجراء المقارنات المتزامنة بين السمات المختلفة.

3.2 تحديد أعمدة معينة للتجميع باستخدام قواميس التجميع (Aggregation Dictionaries)

تتطلب البنى البيانية الواقعية في كثير من الأحيان تطبيق استراتيجيات تجميع متباينة على الأعمدة المختلفة؛ فقد يستدعي التحليل تجميع المتغيرات النصية في قوائم، بالتزامن مع حساب المتوسط الحسابي للمتغيرات الرقمية، واستخراج القيمة العظمى لمتغيرات أخرى. توفر مكتبة Pandas حلاً جذرياً لهذه المتطلبات من خلال قواميس التجميع (Aggregation Dictionaries) الممررة إلى دالة agg.

يتم بناء القاموس بحيث تمثل المفاتيح أسماء الأعمدة المستهدفة، بينما تمثل القيم الدوال البرمجية المراد تطبيقها على كل عمود. على سبيل المثال، يمكن صياغة القاموس بالشكل: {'Player': list, 'Points': 'sum', 'Assists': 'mean'}. عند تمرير هذا القاموس إلى df.groupby('Team').agg(...)، يقوم المحرك بتنفيذ التجميع القائم على القوائم لعمود اللاعبين، بينما يطبق خوارزميات الجمع الرياضي وحساب المتوسط الحسابي على عمودي النقاط والتمريرات الحاسمة على التوالي.

تسهم هذه المنهجية في تجنب استدعاءات GroupBy المتعددة ودمج النتائج يدوياً، مما يوفر قدراً هائلاً من الموارد الحاسوبية والوقت البرمجي. ومع ذلك، في حال تم تمرير قوائم من الدوال لنفس العمود الواحد داخل القاموس، سينتج عن ذلك جدول ذو فهارس متعددة للأعمدة (MultiIndex Columns)، مما يقتضي تسطيح أسماء الأعمدة لاحقاً للحفاظ على اتساق المعالجة الهندسية للبيانات.

3.3 دمج قيم أعمدة متعددة داخل قائمة واحدة من الأزواج أو الكائنات (Tuples/Lists)

في سيناريوهات متقدمة من معالجة البيانات، يُفضل دمج السمات المترابطة معاً في كائن هيكلي واحد بدلاً من الحفاظ على قوائم متوازية منفصلة عبر أعمدة متباعدة. على سبيل المثال، قد يكون من المرغوب فيه تجميع أسماء اللاعبين ونقاطهم المسجلة معاً كأزواج مرتبة (Tuples) تمثل كل منها سجلاً مصغراً داخل قائمة موحدة تابعة للفريق الرياضي.

لتحقيق ذلك، يمكن دمج تقنيات بايثون القياسية مثل دالة zip مع تعبيرات GroupBy ودوال apply المخصصة. يتم تعريف دالة تدمج قيم العمودين في كائنات tuple ثم تحول الناتج إلى قائمة واحدة: df.groupby('Team').apply(lambda g: list(zip(g['Player'], g['Points']))). تتيح هذه الطريقة تشكيل بنى بيانية غنية تحتفظ بالترابط الوثيق بين السمات المتعددة ضمن حاوية موحدة داخل كل خلية.

من الناحية الهيكلية وإدارة الموارد، يُعد تخزين الأزواج المرتبة داخل قائمة واحدة أكثر كفاءة في الحفاظ على التكامل الدلالي للبيانات، حيث يمنع احتمالية عدم التزامن في الأطوال التي قد تطرأ عند معالجة القوائم المنفصلة لاحقاً. ومع ذلك، تتطلب هذه الطريقة استهلاكاً إضافياً للذاكرة لمعالجة كائنات بايثون الوسيطة، وهو ما ينبغي موازنته بعناية عند التعامل مع مجموعات بيانات ضخمة.

4. المقارنة المنهجية بين استخدام agg(list) و apply(list) و transform

4.1 الفروق الجوهرية بين ميثود agg وميثود apply عند التعامل مع القوائم

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

في المقابل، تُعد دالة apply واجهة عامة ومرنة للغاية تقوم بتمرير إطار بيانات فرعي كامل (أو سلسلة كاملة) لكل مجموعة إلى الدالة المستهدفة، مما يمنحها حرية التلاعب بالأبعاد والهياكل دون قيود مسبقة. لكن هذه المرونة تأتي على حساب الكفاءة الحوسبية؛ حيث تقوم apply بتنفيذ استدعاءات إضافية واستدلالات ضمنية لنوع البيانات وشكل المخرجات (Type Inference)، مما يتسبب في زيادة العبء الحسابي واستهلاك وقت معالجة أطول مقارنة بدالة agg.

تتجلى الفروق أيضاً في شكل الفهرس ونوع البيانات المرتجعة؛ فبينما تحافظ agg(list) على بنية واضحة ومحددة السلوك دائماً تعيد Series أو DataFrame وفقاً لنطاق الاستدعاء، قد تؤدي apply(list) في بعض السياقات إلى نتائج غير متوقعة إذا لم يتم تحديد العمود بدقة، حيث قد تعيد مصفوفة من أسماء الأعمدة أو تحاول دمج النتائج بشكل ثنائي الأبعاد، مما يستدعي تفضيل agg(list) كخيار قياسي مستقر.

4.2 استخدام دالة transform لتكرار القوائم المجمعة عبر الصفوف الأصلية

بينما تهدف دالتا agg و apply إلى تقليص عدد الصفوف ليتطابق مع عدد الفئات الفريدة، تؤدي دالة transform وظيفة مختلفة تماماً؛ إذ تقوم بتطبيق عملية التجميع ثم إعادة نشر وبث (Broadcasting) النتيجة المجمعة عبر كافة الصفوف الأصلية لإطار البيانات دون تغيير أبعاده أو عدد سجلاته، محتفظة بالفهرس الأصلي للجدول بالكامل.

لتكرار القائمة المجمعة عبر جميع صفوف الفئة الواحدة، يمكن استخدام تعبير برمجي يدمج تحويل المجموعة إلى قائمة وإعادتها بنفس طول السلسلة، كالتالي: df.groupby('Team')['Points'].transform(lambda x: [list(x)] * len(x)). ينتج عن ذلك ظهور قائمة النقاط الكاملة الخاصة بالفريق في كل صف ينتمي إلى ذلك الفريق بجانب البيانات الفردية لكل لاعب على حدة.

تكتسب هذه التقنية أهمية بالغة في هندسة الميزات (Feature Engineering) ونماذج المقارنات النسبية؛ حيث تسمح لعلماء البيانات بمقارنة أداء السجل الفردي بالقائمة الإجمالية لمجموعته دون الحاجة إلى إجراء عمليات دمج (Merge/Join) مكلفة حوسبياً، مما يسهل حساب رتبة السجل، أو انحرافه عن وسيط المجموعة، أو مسافة التشابه مع أقرانه ضمن نفس الصف البرمجي مباشرة.

4.3 اختبارات الأداء والمقارنة القياسية (Benchmarking)

لتقييم الفروق العملية بين الأساليب المختلفة لتجميع الصفوف في قوائم، تم إجراء اختبارات أداء قياسية باستخدام وحدة القياس الدقيق timeit في بيئة بايثون القياسية، عبر توليد مجموعات بيانات اختبارية بأحجام متفاوتة تتراوح من عشرة آلاف صف إلى مليون صف، مع توزيع الفئات بين مئات وآلاف المجموعات الفرعية المنفصلة.

أظهرت نتائج القياس تفوقاً كاسحاً لاستخدام agg(list) المباشر على عمود محدد، حيث حققت سرعة تنفيذ تفوق apply(list) بمعدل يتراوح بين مرتين إلى أربع مرات، وذلك بسبب تجنب الحلقات التكرارية المعقدة لطبقة بايثون الوسيطة التي تفرضها apply. من جهة أخرى، سجلت العمليات التي تعتمد على transform زمناً إضافياً ناتجاً عن مرحلة البث وتكرار تخصيص مؤشرات القوائم لكل صف في إطار البيانات الأصلي.

يوضح الجدول التحليلي التالي نتائج القياسات التقريبية لمتوسط زمن التنفيذ واستهلاك الذاكرة عبر عينات بيانات متزايدة الحجم:

حجم البيانات (عدد الصفوف) الطريقة المستخدمة متوسط زمن التنفيذ (مللي ثانية) استهلاك الذاكرة الإضافي
10,000 agg(list) 4.2 منخفض جداً
10,000 apply(list) 11.8 متوسط
10,000 transform(list_broadcast) 16.5 مرتفع
100,000 agg(list) 38.6 منخفض
100,000 apply(list) 124.3 متوسط إلى مرتفع
100,000 transform(list_broadcast) 178.1 مرتفع جداً
1,000,000 agg(list) 412.0 متوسط
1,000,000 apply(list) 1380.0 مرتفع
1,000,000 transform(list_broadcast) 2150.0 حرج

تؤكد هذه البيانات القياسية التوصية الهندسية الصارمة: يجب الاعتماد دائماً على agg(list) كخيار افتراضي عند تجميع الصفوف في قوائم لتقليل الضغط على وحدة المعالجة المركزية، وحصر استخدام apply أو transform في الحالات التحليلية الخاصة التي تعجز فيها دوال التجميع القياسية عن تلبية المتطلبات المنطقية للحسابات.

5. التعامل مع القيم الفريدة وإزالة التكرارات داخل القوائم المجمعة

5.1 تجميع القيم الفريدة باستخدام دالة set ومصفوفات NumPy

في العديد من المسائل التحليلية، لا تكون هناك حاجة للاحتفاظ بكافة السجلات المكررة داخل القوائم المجمعة، بل ينصب التركيز على استخراج الكيانات الفريدة المميزة لكل مجموعة. لتحقيق ذلك، يمكن استبدال دالة التجميع list ببناء يعتمد على المجموعات الرياضية، إما مباشرة عبر agg(set) أو من خلال تحويل النتيجة اللاحقة إلى قائمة عبر تعبير لامبدا: agg(lambda x: list(set(x))).

يعتمد استخدام كائن المجموعة set على جداول التجزئة الداخلية (Hash Tables) في بايثون، مما يتيح تصفية القيم المكررة بكفاءة زمنية تقارب O(1) لكل عملية إدراج. ومع ذلك، فإن العيب الجوهري لهذا الأسلوب يكمن في أنه يفقد البيانات ترتيبها الأصلي تماماً؛ فالمجموعات الرياضية بطبيعتها غير مرتبة، وبالتالي لن تعكس القائمة الناتجة التسلسل الزمني أو الترتيبي لظهور العناصر في البيانات الأصلية.

كبديل عالي الأداء، يمكن استدعاء دالة pd.unique أو دالة np.unique عبر التعبير: agg(lambda x: list(pd.unique(x))). تتميز ميثود pd.unique في مكتبة Pandas بأنها أسرع من دوال بايثون القياسية عند التعامل مع المصفوفات أحادية البعد، فضلاً عن أنها تحتفظ بترتيب الظهور الأول للعناصر، مما يجعلها خياراً متفوقاً يجمع بين كفاءة الأداء والحفاظ على الاتساق الترتيبي.

5.2 إزالة التكرار مع الحفاظ الصارم على الترتيب الأصلي للظهور

عندما تفرض متطلبات العمل ضرورة استبعاد القيم المكررة مع الحفاظ الدقيق والمطلق على ترتيب الظهور الأول لكل عنصر (Preserving Insertion Order)، تبرز تقنيات متخصصة تعتمد على خصائص هياكل البيانات الحديثة في بايثون. منذ إصدار بايثون 3.7، تضمن القواميس القياسية (Dictionaries) الحفاظ على ترتيب إدراج المفاتيح، مما يفتح الباب لاستخدامها كمرشحات تكرار فائقة الكفاءة.

تُطبق هذه الاستراتيجية عبر استدعاء التابع dict.fromkeys داخل دالة التجميع البرمجية بالشكل التالي: agg(lambda x: list(dict.fromkeys(x))). يقوم هذا الأمر بإنشاء قاموس مؤقت تكون مفاتيحه هي قيم السلسلة التجميعية، وبما أن مفاتيح القاموس فريدة حكماً، يتم إسقاط أي تكرار لاحق تلقائياً مع الاحتفاظ التام بترتيب الإدراج الأول، ثم يتم تحويل المفاتيح مباشرة إلى قائمة نهائية.

بالمقارنة مع تقنيات الفرز اليدوي أو استخدام الحلقات الشرطية التكرارية، توفر طريقة dict.fromkeys أداءً فائقاً يقترب من سرعة دوال لغة C المدمجة. يوضح التحليل البرمجي أن هذا النهج يقلل التعقيد الحسابي إلى O(N) مع الحفاظ على البصمة الترتيبية للأحداث، وهو أمر لا غنى عنه في تحليل المسارات المعقدة وسلاسل الانتقال الاحتمالية مثل سلاسل ماركوف.

5.3 تطبيقات عملية على بيانات التفاعلات وسجلات النشاط

تتجلى القوة العملية لتجميع القيم الفريدة في تحليل سجلات النشاط الرقمي وتفاعلات المستخدمين على منصات التجارة الإلكترونية وتطبيقات الويب. على سبيل المثال، في مجموعات بيانات النقر والولوج (Clickstream Logs)، يتم تسجيل معرف الجلسة (Session ID) مكرراً مع كل صفحة يزورها المستخدم. بتجميع المعرفات الفريدة للصفحات لكل جلسة، يمكن للمحلل استخلاص المسار الدقيق للرحلة الرقمية دون تشويش ناتج عن تكرار تحديث الصفحة ذاتها.

كذلك في قطاع التجزئة، يتيح تجميع المنتجات الفريدة المشتراة لكل عميل استناداً إلى معرفه (Customer ID) تكوين ملف تعريفي يمثل سلة المشتريات التراكمية. تُعد هذه القوائم المنقاة المدخل الأساسي لخوارزميات التوصية المعتمدة على المقارنة بين السلال (Basket Analysis) واستخراج قواعد الارتباط، حيث يؤدي وجود التكرارات غير المبررة إلى تضخيم غير دقيق لأوزان العلاقات بين المنتجات.

يوضح المثال التالي آلية استخراج المسار الفريد للمستخدمين مع الحفاظ على الترتيب:

بافتراض جدول يحتوي على ['User_ID', 'Page_Visited', 'Timestamp']، يتم ترتيب الجدول زمنياً أولاً، ثم تطبيق التجميع: df.groupby('User_ID')['Page_Visited'].agg(lambda x: list(dict.fromkeys(x))). تضمن هذه العملية الحصول على متسلسلة نقية تعبر عن الانتقال الحقيقي للمستخدم بين أقسام المنصة، مما يسهل اكتشاف نقاط الاختناق في مسارات الشراء وسلوك التصفح بدقة عالية.

6. إدارة القيم المفقودة (NaN / None) أثناء تجميع الصفوف

6.1 سلوك دالة list الافتراضي تجاه القيم المفقودة

تشكل القيم المفقودة والممثلة برمجياً بكائنات np.nan أو None تحدياً دقيقاً عند إجراء عمليات التجميع في مكتبة Pandas. في العمليات الإحصائية التقليدية مثل الجمع أو حساب المتوسط، يتجاهل محرك Pandas القيم المفقودة تلقائياً عبر المعامل الافتراضي skipna=True. ومع ذلك، يختلف هذا السلوك جذرياً عند استخدام دالة التجميع list.

عند تمرير agg(list)، تعامل لغة بايثون القيم المفقودة كعناصر صريحة ومستقلة، مما يؤدي إلى تضمين كائنات nan داخل القوائم المجمعة الناتجة تماماً كأي قيمة نصية أو رقمية صحيحة. يؤدي هذا السلوك إلى مشاكل جسيمة في خطوط الأنابيب اللاحقة؛ حيث تتسبب هذه القيم في إطلاق أخطاء برمجية عند محاولة معالجة عناصر القائمة لاحقاً باستخدام دوال النصوص أو العمليات الحسابية الصارمة التي تفترض وجود أنواع بيانات متجانسة.

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

6.2 استبعاد القيم المفقودة أثناء عملية التجميع البرمجي

لتفادي تلوث القوائم المجمعة بالقيم المفقودة، تتوفر استراتيجيتان برمجيتان رئيسيتان: المعالجة المسبقة للبيانات، أو التصفية الحركية أثناء مرحلة التجميع. تتمثل الاستراتيجية الأولى في تنقية السلسلة المستهدفة قبل التجميع باستخدام ميثود dropna()، كأن يتم تطبيق: df.dropna(subset=['Values']).groupby('Category')['Values'].agg(list). تتميز هذه الطريقة بالسرعة الفائقة واستغلال التحسينات الموجهة داخل محرك Pandas المكتوب بلغة C.

أما الاستراتيجية الثانية، فتعتمد على بناء تعبير لامبدا مخصص يقوم بفلترة العناصر أثناء تجميع القائمة، باستخدام تعبيرات الفهم المدمجة (List Comprehensions) وفحص التحقق من القيم غير المفقودة عبر pd.notna(): agg(lambda x: [i for i in x if pd.notna(i)]). تتيح هذه الطريقة مرونة استبعاد القيم المفقودة لعمود معين دون حذف الصف بأكمله من إطار البيانات الأصلي، مما يحافظ على وجود الصفوف للأعمدة الأخرى في حال تطبيق تجميع متعدد.

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

6.3 استبدال القيم المفقودة بقيم افتراضية داخل القوائم

في حالات نمذجة محددة، قد لا يكون استبعاد القيمة المفقودة حلاً مناسباً، خاصة إذا كان موقع القيمة داخل التسلسل يحمل دلالة زمنية أو نسقية لا يجب فقدها (مثل غياب تسجيل حضور في يوم معين ضمن أسبوع). في هذه الظروف، يصبح الخيار الأمثل هو تعويض القيم المفقودة بقيم بديلة قياسية (Imputation) تحافظ على طول القائمة واتساق نوع بياناتها.

يتم تنفيذ الاستبدال مسبقاً باستخدام ميثود fillna() المخصص للسلاسل البيانية قبل الشروع في التجميع. على سبيل المثال، يمكن تعويض المتغيرات الرقمية بالصفر عبر: df['Points'].fillna(0)، أو تعويض السلاسل النصية بقيمة دلالية صريحة مثل df['Event'].fillna('Missing')، قبل تطبيق groupby().agg(list). يضمن هذا النهج أن تصبح القوائم الناتجة متجانسة كلياً وخالية من الكائنات غير المعرفة التي قد تعطل النماذج اللاحقة.

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

7. التجميع الشرطي وتصفية العناصر داخل القوائم الناتجة

7.1 تصفية الصفوف قبل التجميع مقارنة بالتصفية بعد التجميع

تتطلب سيناريوهات التحليل المتقدم في كثير من الأحيان تجميع العناصر التي تستوفي شروطاً منطقية معينة فقط. يبرز هنا تساؤل معماري محوري في هندسة البيانات: هل من الأفضل تطبيق أقنعة التصفية المنطقية (Boolean Masking) على إطار البيانات قبل تنفيذ GroupBy، أم إجراء التجميع أولاً ثم تصفية القوائم الناتجة لاحقاً؟

تثبت القواعد الهندسية الراسخة أن التصفية المسبقة لصفوف الجدول عبر التعبير: df[df['Score'] > 50].groupby('Category')['Score'].agg(list) تتفوق جذرياً على التصفية اللاحقة. يرجع ذلك إلى أن التصفية المسبقة تقلص حجم البيانات الممررة إلى محرك GroupBy، مما يقلل من عدد المفاتيح الفرعية والعمليات الحسابية المطلوبة لإنشاء القوائم، ويوفر مساحة واسعة من الذاكرة العشوائية.

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

7.2 تطبيق شروط معقدة على عناصر القوائم باستخدام List Comprehensions

عند الحاجة إلى تطبيق قواعد تصفية شرطية معقدة ومتعددة المستويات تعتمد على تحويلات ديناميكية للعناصر الفردية، تبرز تعبيرات الفهم للقوائم (List Comprehensions) المدمجة داخل دوال التجميع المخصصة كأداة برمجية فائقة المرونة والقوة التعبيرية.

يمكن تمرير دالة مخصصة تفحص كل عنصر وتطبق عليه شروطاً رياضية أو نصية قبل إدراجه في القائمة النهائية. على سبيل المثال، لتجميع مضاعفات الأرقام التي تتجاوز حداً معيناً مع استبعاد القيم السالبة، يُصاغ الكود بالشكل: agg(lambda x: [val * 2 for val in x if val > 0 and val % 2 == 0]). تتيح هذه الصياغة دمج عمليات التحويل الحسابي والتصفية المنطقية في خطوة تنفيذية واحدة لكل مجموعة.

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

7.3 ترتيب العناصر داخل القوائم المجمعة استناداً إلى عمود فرز ثانوي

في كافة التطبيقات المعتمدة على التسلسل الزمني والمنطقي، يكتسب ترتيب العناصر داخل القائمة المجمعة أهمية حاسمة تماثل أهمية القيم ذاتها. لا تضمن دالة groupby().agg(list) ترتيباً معيناً للقوائم إلا الترتيب الطبيعي الذي تظهر به الصفوف في إطار البيانات المدخل لحظة تنفيذ العملية.

لضمان ترتيب العناصر داخل القوائم الناتجة بدقة متناهية بناءً على عمود محدد (مثل عمود التاريخ أو الترتيب الرقمي)، يجب تنفيذ ميثود sort_values() على الجدول بأكمله قبل الشروع في استدعاء GroupBy. على سبيل المثال: df.sort_values(by=['Team', 'Date']).groupby('Team')['Points'].agg(list). تضمن هذه الخطوة المسبقة إدراج القيم داخل كل قائمة بالتسلسل الزمني التصاعدي الدقيق للتواريخ المقابلة.

كبديل آخر، يمكن تنفيذ الفرز الداخلي للقوائم أثناء التجميع عبر تمرير دالة الفرز المدمجة: agg(lambda x: sorted(list(x)))، أو تطبيق دوال فرز مخصصة تعتمد على مفاتيح ترتيب معقدة (Custom Sort Keys). ومع ذلك، يبقى الفرز المسبق لإطار البيانات هو الأسلوب الأكثر كفاءة وسرعة من الناحية الحوسبية بفضل خوارزميات الفرز المحسنة المنفذة في طبقات لغة C التحتية لمكتبة Pandas.

8. التحويل إلى هياكل بيانات أخرى: مصفوفات NumPy وقواميس ومجموعات

8.1 تجميع الصفوف في مصفوفات NumPy أحادية وثنائية الأبعاد

على الرغم من المرونة العالية لقوائم بايثون القياسية، إلا أنها تفتقر إلى الكفاءة الرياضية الصارمة المطلوبة في الحسابات المتجهة المتقدمة. في السياقات العلمية والهندسية، يبرز تجميع الصفوف مباشرة في مصفوفات NumPy أحادية الأبعاد (NumPy Arrays) كخيار بديل فائق الأهمية، ويتم ذلك ببساطة عبر استدعاء: df.groupby('Category')['Values'].agg(np.array) أو agg(list_to_array).

تتميز مصفوفات NumPy بتخزين البيانات في كتل متجاورة ومتجانسة في الذاكرة (Contiguous Memory Blocks)، مما يتيح استدعاء العمليات الرياضية المسرعة عبر مكتبات الجبر الخطي مثل BLAS و LAPACK مباشرة على محتوى الخلايا. يتيح ذلك تطبيق عمليات الضرب المصفوفي، وتحويلات فورييه، وحساب المسافات الإقليدية على التجميعات الفرعية بسرعات تفوق القوائم التقليدية بأضعاف مضاعفة.

ومع ذلك، يجب الانتباه إلى معضلة المصفوفات غير متساوية الأطوال (Ragged Arrays)؛ فإذا كانت الفئات المختلفة تحتوي على أعداد متفاوتة من الصفوف، فإن دمج هذه المصفوفات في مصفوفة ثنائية الأبعاد شاملة يتطلب تطبيق تقنيات الحشو (Padding) لتوحيد الأبعاد، وهو متطلب تقني شائع عند تجهيز دفعات البيانات (Batches) لشبكات التعلم العميق.

8.2 إنشاء قواميس (Dictionaries) مجمعة من صفين مرتبطين

تمثل العلاقات البيانية من نوع “واحد إلى متعدد” (One-to-Many Mappings) أحد الأنماط الأكثر شيوعاً في قواعد البيانات، حيث يرتبط المفتاح الفئوي بمجموعة من الأزواج المكونة من خاصية وقيمتها. بدلاً من تجميع عمودين في قائمتين منفصلتين، توفر مكتبة Pandas إمكانية دمج هذين العمودين في كائن قاموس بايثون (Dictionary) متكامل لكل مجموعة فئوية.

يتحقق ذلك عبر دمج دالة apply مع تعبير تحويل الأزواج إلى قاموس: df.groupby('Category').apply(lambda g: dict(zip(g['Key_Column'], g['Value_Column']))). ينتج عن هذا التحويل هيكل بياني يحتوي على قاموس داخل كل خلية، مما يمكن المحلل من البحث السريع بالاعتماد على المفاتيح الفرعية لكل فئة دون الحاجة إلى تفكيك الجدول أو إعادة مسحه.

تكتسب هذه البنية الهيكلية قيمة استثنائية عند تجهيز البيانات للتصدير بصيغة JSON الموجهة نحو واجهات برمجة التطبيقات (REST APIs) وقواعد بيانات المستندات مثل MongoDB؛ حيث يتطابق الهيكل الناتج المكون من قواميس مجمعة تماماً مع المخططات الهيكلية (Schemas) لملفات JSON المتداخلة، مما يلغي الحاجة إلى كتابة خوارزميات تحويل معقدة على خوادم التطبيقات.

8.3 توليد كائنات المجموعات غير القابلة للتعديل (Frozensets) لتطبيقات التنقيب

في مجالات التنقيب عن البيانات واستخراج قواعد الارتباط (Association Rule Mining) باستخدام خوارزميات مثل Apriori و FP-Growth، يتطلب تمثيل سلال المشتريات استخدام كائنات قابلة للتجزئة (Hashable) وغير قابلة للتعديل (Immutable). هنا يبرز كائن المجموعة المجمدة frozenset كبديل مثالي للقوائم والمجموعات العادية.

يتم تجميع العناصر في كائنات مجمدة عبر تمرير النوع مباشرة إلى دالة التجميع: df.groupby('Transaction_ID')['Item'].agg(frozenset). يضمن استخدام frozenset حذف التكرارات مع إغلاق الهيكل البياني ضد أي تعديل لاحق، والأهم من ذلك، إمكانية استخدام هذه الخلايا الناتجة كمفاتيح في قواميس أخرى أو كفهارس في جداول تكرارية متقدمة، وهو ما يستحيل تنفيذه باستخدام القوائم العادية نظراً لكونها كائنات قابلة للتغيير (Mutable).

يوفر هذا النهج كفاءة حسابية ملحوظة عند مقارنة تشابه السلال وحساب مقاييس الدعم (Support) والثقة (Confidence) في خوارزميات التوصية، مما يجعله مكوناً أساسياً في حزمة أدوات مهندسي البيانات المتخصصين في أنظمة التجزئة والتسويق الذكي.

9. تحسين الأداء وإدارة الذاكرة عند تجميع البيانات الضخمة

9.1 تأثير تخزين القوائم في خلايا Pandas على استهلاك الذاكرة

على الرغم من المزايا التحليلية الواسعة لتخزين القوائم داخل أطر بيانات Pandas، إلا أن هذه الممارسة تحمل تكلفة خفية باهظة على صعيد استهلاك الذاكرة العشوائية (RAM). يعود السبب الجوهري في ذلك إلى أن محرك Pandas مصمم أساساً للتعامل مع البيانات المتجهة المتجانسة ذات الأحجام الثابتة، بينما يتطلب تخزين القوائم تحويل العمود إلى نوع كائن عام object.

في نمط تخزين الكائنات، لا يحتوي عمود Pandas على البيانات الفعلية، بل يحتوي على مصفوفة من المؤشرات (Pointers) التي تشير إلى كائنات بايثون المستقلة الموزعة عشوائياً في الذاكرة. يترتب على ذلك فقدان كامل لمزايا التخزين المتجاور والضغط الديناميكي، فضلاً عن العبء الإضافي لكل كائن قائمة في بايثون (List Overhead)، مما يؤدي إلى تضخم استهلاك الذاكرة بأضعاف مقارنة بالأعمدة الرقمية القياسية.

لقياس الحجم الحقيقي لاستهلاك الذاكرة بدقة وتفادي الحسابات السطحية للمؤشرات، يجب استدعاء ميثود df.memory_usage(deep=True). يكشف هذا الاستدعاء العميق الحجم الفعلي للبيانات المخزنة داخل كائنات القوائم في الذاكرة، وهو مؤشر حاسم يجب مراقبته باستمرار لتجنب استنزاف موارد النظام أثناء معالجة مجموعات البيانات الكبيرة.

9.2 تسريع التجميع باستخدام مكتبة Numba ومحركات الحوسبة البديلة

عند التعامل مع مصفوفات رقمية ضخمة تتطلب تجميعاً فائق السرعة، تصبح دوال بايثون القياسية عنق زجاجة حاسوبي واضح. للتغلب على هذا القيد، يمكن الاستعانة بمكتبة Numba، وهي محرك ترجمة فورية (JIT Compiler) يقوم بتحويل كود بايثون وNumPy إلى تعليمات لغة آلة فائقة السرعة تتطابق في أدائها مع لغتي C و Fortran.

يتيح محرك Numba تسريع عمليات GroupBy المخصصة عبر تجميع البيانات الرقمية مباشرة في مصفوفات داخلية دون المرور بطبقة كائنات بايثون البطيئة. من خلال استخدام خيار engine='numba' المتاح في إصدارات Pandas الحديثة لبعض دوال التجميع، يمكن تحقيق قفزات أدائية هائلة تقلص زمن المعالجة بنسب تتجاوز 80% في التجميعات الحسابية المعقدة.

ومع ذلك، ينبغي مراعاة القيود التقنية لمحرك Numba؛ فهو مصمم لمعالجة الأنواع الرقمية والمصفوفات المتجانسة حصراً، ويعجز عن التعامل المباشر مع السلاسل النصية المعقدة أو كائنات بايثون الديناميكية المخصصة داخل القوائم، مما يجعله حلاً مخصصاً للمعالجات الرياضية والهندسية الدقيقة للبيانات الرقمية.

9.3 المعالجة المتوازية والتوزيع باستخدام Dask و Polars

عندما يتجاوز حجم البيانات سعة الذاكرة العشوائية للجهاز الواحد، تصبح مكتبة Pandas التقليدية عاجزة عن إتمام العمليات. في هذه البيئات الضخمة، يبرز استخدام محركات الحوسبة المتوازية والموزعة، وعلى رأسها مكتبة Dask ومحرك Polars فائق التطور المكتوب بلغة Rust.

تتيح مكتبة Dask تقسيم إطار البيانات الضخم إلى كتل متعددة (Partitions) وتوزيع تنفيذ عمليات GroupBy التجميعية عبر أنوية المعالج المتعددة أو عبر عنقود حوسبي كامل (Cluster). يتم استدعاء دالة التجميع بصيغة مطابقة لـ Pandas، مع إدارة ذكية لحركة البيانات وتجنب اختناقات الذاكرة عبر تقنيات التنفيذ الكسول (Lazy Evaluation).

من جانب آخر، يقدم محرك Polars ثورة في معالجة القوائم؛ حيث يدعم أصلاً نوع البيانات القائم على القوائم (List Data Type) دون اللجوء إلى كائنات كائنية غير متجانسة، مما يتيح له تنفيذ عمليات تجميع الصفوف في قوائم بسرعات تفوق Pandas بعشرات المرات، مع استهلاك جزء ضئيل من الذاكرة بفضل نموذج الذاكرة المشترك المعتمد على مقاييس Apache Arrow.

10. العمليات العكسية: تفكيك القوائم المجمعة إلى صفوف فردية (Explode)

10.1 استخدام دالة df.explode() لاستعادة الهيكل الجدولي الأصلي

تمثل المعالجة الهيكلية للبيانات عملية تفاعلية ثنائية الاتجاه؛ فبقدر ما تبرز الحاجة إلى تجميع الصفوف في قوائم لتنفيذ تحليلات متقدمة، تبرز حاجة مقابلة لإعادة تفكيك هذه القوائم واستعادة الهيكل الجدولي المسطح الأصلي لمواءمة متطلبات التصدير أو النمذجة التقليدية. توفر مكتبة Pandas لهذه الغاية ميثوداً محورياً عالي الكفاءة يُعرف باسم df.explode().

تعمل دالة explode على أخذ العمود الذي يحتوي على كائنات قابلة للتكرار (مثل القوائم أو المصفوفات)، وتوليد صف مستقل لكل عنصر من عناصر تلك القائمة، مع تكرار كافة قيم الأعمدة الأخرى المرتبطة بذلك الصف بشكل متطابق. على سبيل المثال، إذا كان الصف يحتوي على الفئة ‘Team A’ وقائمة من ثلاثة أرقام، سينتج عن استدعاء df.explode('Points') ثلاثة صفوف منفصلة تحمل جميعها الفئة ‘Team A’ مع قيمة رقمية مفردة في كل صف.

يحافظ ميثود explode بدقة على الفهارس الأصلية للجدول ما لم يتم تعيين خيار ignore_index=True، مما يتيح تتبع السجل الأصلي الذي انحدر منه كل صف مفكك. تُعد هذه الدالة المعكوس الرياضي الدقيق لعملية groupby().agg(list)، وتشكل الركيزة الأساسية لتدفقات التحويل الدائري للبيانات.

10.2 تفكيك أعمدة متعددة تحتوي على قوائم متزامنة

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

منذ الإصدار 1.3.0 لمكتبة Pandas، أصبحت دالة explode تدعم تمرير قائمة من أسماء الأعمدة بالشكل: df.explode(['Players', 'Points', 'Assists']). يشترط هذا الاستدعاء المتعدد أن تكون كافة القوائم في الأعمدة المحددة داخل الصف الواحد متطابقة تماماً في الطول؛ حيث يقوم المحرك بتفكيك العناصر ذات الترتيب المتماثل في كل قائمة إلى صف واحد مشترك، محافظاً على المحاذاة الأصلية بدقة متناهية.

في حال وجود عدم تطابق في أطوال القوائم بين الأعمدة لنفس الصف، ستطلق الدالة خطأ برمجياً صريحاً (ValueError). لمعالجة هذه الحالات الشاذة، يتعين أولاً تسوية أطوال القوائم باستخدام دوال حشو مخصصة أو استبدال القيم الناقصة بكائنات np.nan لضمان استيفاء شروط الاتساق الهيكلي للتفكيك المتزامن.

10.3 تطبيقات دائرية: التحويل من جداول إلى قوائم ثم استرجاع الجداول

يُعد نمط المعالجة الدائري (Round-trip Processing) أحد الأنماط المعمارية المتقدمة في هندسة البيانات؛ حيث يتم تجميع البيانات المسطحة في قوائم مؤقتاً لتطبيق خوارزميات معينة تتطلب رؤية المجموعة بالكامل، ثم يُعاد تفكيك النتائج لاستئناف التدفق الجدولي القياسي داخل خط أنابيب المعالجة.

من الأمثلة البارزة على هذا النمط تطبيق خوارزميات التنعيم الإحصائي (Smoothing Filters) أو إزالة القيم الشاذة النسبية لكل فئة على حدة؛ حيث يتم تجميع قياسات الحساسات لكل جهاز في قائمة مرتبة، وتمرير القائمة إلى دالة معالجة إشارات تزيل التشويش وتعيد قائمة بنفس الطول، ثم يتم استدعاء explode() مباشرة لإعادة نشر السجلات المنقاة في الهيكل المسطح الأصلي.

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

11. الأخطاء البرمجية الشائعة وطرق تصحيحها واستكشاف الأخطاء

11.1 الخلط بين تجميع السلاسل وتجميع القوائم وتوليد كائنات غير متوقعة

من الأخطاء الشائعة والمدمرة التي يقع فيها المبرمجون استدعاء دالة apply(list) على كائن GroupBy لإطار بيانات كامل دون تحديد عمود مستهدف، متوقعين الحصول على قوائم لقيم البيانات. في هذا السيناريو، يقوم محرك Pandas بتمرير إطار البيانات الفرعي لكل مجموعة، وتحويله عبر دالة list القياسية، مما يؤدي إلى توليد قائمة تحتوي على أسماء الأعمدة النصية بدلاً من قيم السجلات الفعلية.

لتصحيح هذا الخطأ، يجب التحقق دائماً من تحديد السلسلة المستهدفة بدقة عبر الأقواس المربعة قبل التجميع: df.groupby('Cat')['Target'].agg(list)، أو تحديد قاموس تجميعي صريح. كما يجب التأكد من عدم الخلط بين تجميع القوائم وتجميع السلاسل النصية؛ فاستخدام دالة ','.join(x) يتطلب أن تكون كافة المدخلات سلاسل نصية أصلاً، وإلا سينتج خطأ في الأنواع (TypeError) إذا احتوى العمود على أرقام ما لم يتم تحويلها مسبقاً عبر astype(str).

يوضح التحليل التشخيصي لأخطاء الأنواع ضرورة فحص نوع المخرجات باستخدام type() و df.dtypes فور إتمام عملية التجميع؛ للتأكد من أن الكائن الناتج يطابق التوقعات الهندسية ولا يحتوي على مصفوفات وصفية غير مقصودة نشأت عن تمرير غير دقيق للدوال التجميعية.

11.2 مشاكل الأداء الناتجة عن الحلقات التكرارية اليدوية بدلاً من GroupBy

يلجأ بعض المطورين، نتيجة نقص المعرفة بالإمكانيات المتقدمة لمكتبة Pandas، إلى كتابة حلقات تكرارية يدوية باستخدام for وميثود iterrows() أو itertuples() لبناء القوائم المجمعة يدوياً عبر قواميس خارجية. يُعد هذا النهج من أسوأ الممارسات الهندسية على الإطلاق في بيئة بايثون لتحليل البيانات.

تتسبب الحلقات التكرارية اليدوية في إبطاء سرعة التنفيذ بمعدلات تتراوح بين 50 إلى 100 ضعف مقارنة بالاستدعاء المباشر لـ groupby().agg(list)؛ حيث تؤدي حلقات بايثون الصريحة إلى تكرار إنشاء كائنات Series وسيطة لكل صف، وتكسير التحسينات المتجهة المنفذة على مستوى لغة C، فضلاً عن مضاعفة استهلاك الذاكرة وتكلفة إدارة دورة حياة الكائنات (Garbage Collection Overhead).

تقتضي المعايير الاحترافية إعادة صياغة كافة الأكواد القديمة القائمة على الحلقات التكرارية اليدوية واستبدالها بالصيغ المدمجة التجميعية، مما يضمن أقصى استغلال لقدرات المعالجة المتوازية الداخلية لمكتبة Pandas وتحقيق زمن استجابة شبه لحظي حتى مع ملايين السجلات.

11.3 التعامل مع أنواع البيانات المعقدة والكائنات المخصصة داخل القوائم

عند احتواء إطار البيانات على أنواع بيانات متقدمة مثل التواريخ والأوقات (Datetimes ذات المناطق الزمنية)، أو الفترات الزمنية (Timedeltas)، أو كائنات برمجية مخصصة، قد تظهر مشكلات غير متوقعة أثناء تجميعها في قوائم، وتحديداً عند محاولة تصدير هذه البيانات وتسلسلها (Serialization) إلى ملفات JSON أو Parquet.

تكمن المشكلة في أن وحدات التسلسل القياسية مثل json.dumps() تعجز عن تسلسل كائنات Timestamp الخاصة بـ Pandas الموجودة داخل القوائم المجمعة، مما يطلق أخطاء من نوع TypeError: Object of type Timestamp is not JSON serializable. لحل هذه المشكلة، يجب تحويل كائنات التواريخ إلى سلاسل نصية قياسية بنسق ISO-8601 باستخدام dt.strftime() قبل الشروع في خطوة تجميع القوائم.

كذلك عند التعامل مع مجموعات بيانات بالغة الضخامة، قد يؤدي تجميع القوائم إلى أخطاء نفاد الذاكرة (Out-of-Memory / OOM) المفاجئة. يتطلب استكشاف هذه الأخطاء ومعالجتها مراقبة مستمرة لحجم الذاكرة، وتقسيم المعالجة إلى دفعات (Batches)، وتفريغ المتغيرات الوسيطة غير المستخدمة دورياً باستخدام وحدة gc.collect() لضمان استقرار بيئة التنفيذ.

12. حالات دراسية وتطبيقات متقدمة في هندسة البيانات والتعلم الآلي

12.1 بناء متسلسلات الإدخال للشبكات العصبية المتكررة (RNNs / LSTMs)

تتطلب الشبكات العصبية المتكررة (Recurrent Neural Networks) ونماذج الذاكرة طويلة المدى قصيرة المدى (LSTM) مدخلات ثلاثية الأبعاد تأخذ الشكل القياسي (Samples, Timesteps, Features). يُعد تجميع الصفوف في قوائم الخطوة التحضيرية الحاسمة لبناء هذه المتسلسلات الزمنية انطلاقاً من البيانات المجدولة التاريخية للمستخدمين أو الأجهزة.

تبدأ العملية بفرز سجلات النشاط لكل مستخدم ترتيباً زمنياً صارماً، ثم تجميع المتغيرات الرقمية (مثل قيم العمليات المالية أو مؤشرات الأداء الحيوية) في قوائم متسلسلة باستخدام groupby('User_ID')['Metric'].agg(list). بعد الحصول على القوائم، يتم استدعاء دوال الحشو المسبق مثل tf.keras.preprocessing.sequence.pad_sequences في TensorFlow لتوحيد أطوال القوائم عبر كافة المستخدمين وإضافة أصفار حشو للمتسلسلات القصيرة.

تُحول هذه المصفوفات الموحدة مباشرة إلى كائنات Tensors متوافقة مع مكتبات التعلم العميق مثل PyTorch، مما يتيح تدريب النماذج على التنبؤ بالاحتيال المالي، أو توقع انقطاع العملاء (Churn Prediction) استناداً إلى البنية الديناميكية للمتسلسلات الزمنية دون فقدان أدق التفاصيل التتابعية لسلوك كل كيان.

12.2 معالجة اللغات الطبيعية (NLP) وتجميع النصوص والرموز (Tokens)

في خطوط أنابيب معالجة اللغات الطبيعية (Natural Language Processing)، يتم تمثيل المستندات الكبيرة في كثير من الأحيان كجداول مسطحة تحتوي على الكلمات الفردية أو الجمل المفككة مع معرفاتها الوصفية والتصنيفية. يلعب تجميع القوائم دوراً جوهرياً في إعادة تجميع هذه الرموز (Tokens) في وحدات لغوية متكاملة جاهزة للتحليل الدلالي المتقدم.

باستخدام الصيغة df.groupby('Document_ID')['Token'].agg(list)، يتم دمج مصفوفات الكلمات المنقاة لكل مستند في قائمة واحدة تمثل النص المفكك. تتيح هذه القوائم المجمعة حساب مصفوفات التواتر، وبناء قواميس المفردات المشتركة، واستخراج متتاليات الكلمات (N-grams) بكفاءة عالية لكل تصنيف موضوعي على حدة.

علاوة على ذلك، تشكل هذه القوائم النصية المدخل المباشر لتدريب نماذج التضمين اللغوي مثل Word2Vec ونماذج FastText؛ حيث تتطلب خوارزميات التدريب تمرير قوائم من الرموز تمثل الجمل الحقيقية لتعلم العلاقات السياقية بين الكلمات، مما يجعل عملية GroupBy التجميعية خطوة لا غنى عنها في المعالجة القبلية للنصوص الضخمة.

12.3 تحليل مسارات سلة المشتريات وأنظمة التوصية (Recommender Systems)

تعتمد أنظمة التوصية المعاصرة وخوارزميات الترشيح التشاركي (Collaborative Filtering) اعتماداً مطلقاً على السجلات التاريخية لتفاعلات المستخدمين. يتطلب بناء هذه الأنظمة تحويل جداول المبيعات المسطحة إلى ملفات تعريفية تحتوي على التاريخ الكامل لمشتريات كل مستخدم وقوائم التقييمات المرتبطة بها.

من خلال تجميع معرفات المنتجات في قوائم لكل مستخدم عبر: df.groupby('Customer_ID')['Product_ID'].agg(list)، يتشكل تمثيل متكامل لسلة المشتريات التراكمية. تُمرر هذه القوائم إلى خوارزميات حساب التشابه الجيبي (Cosine Similarity) أو نماذج التوصية القائمة على الرسوم البيانية (Graph Neural Networks) لاكتشاف الأنماط المشتركة والتوصية بالمنتجات المكملة بدقة متناهية.

كما يتيح تجميع متتاليات النقر (Clickstream Sequences) تحليل مسارات الانتقال والتنبؤ بالوجهة التالية للزائر في الوقت الفعلي؛ حيث تُخزن هذه القوائم في قواعد بيانات سريعة الذاكرة مثل Redis لتوفير استرجاع شبه لحظي لسياق المستخدم، مما يرفع من دقة الاستهداف التجاري ومعدلات التحويل في المنصات الرقمية الكبرى.

خاتمة

استعرض هذا المرجع الأكاديمي والتقني الشامل الأبعاد المتكاملة لعملية تجميع الصفوف في قوائم باستخدام واجهة GroupBy ضمن مكتبة Pandas. لقد رأينا كيف يتجاوز هذا النمط الهيكلي حدود التلخيص الإحصائي التقليدي ليصبح جسراً أساسياً يربط بين جداول البيانات المسطحة والبنى المتسلسلة المعقدة التي تتطلبها نماذج التعلم الآلي الحديثة والتحليلات الاستكشافية المتقدمة.

أظهرت التحليلات المعمارية والمقارنات القياسية أن استخدام دالة agg(list) يمثل الخيار الهندسي الأكثر كفاءة واستقراراً لمعظم التطبيقات، مع ضرورة الحذر من التكاليف المرتفعة للذاكرة الناتجة عن تخزين كائنات القوائم داخل أعمدة Pandas، واتباع أفضل الممارسات في إدارة القيم المفقودة والتكرارات والفرز المسبق للبيانات.

إن إتقان هذه التقنيات والتحويلات العكسية المرتبطة بها مثل explode، والقدرة على التوسع نحو مكتبات متقدمة مثل Dask و Polars و Numba، يزود مهندسي وعلماء البيانات بالأدوات الضرورية لبناء خطوط أنابيب بيانات قوية، قابلة للتوسع، وعالية الأداء، قادرة على تلبية متطلبات أحدث التطبيقات الذكية في العصر الرقمي.

References

اقتباس هذا المقال

looti, M. (2026, أغسطس 31). بانداس: كيفية تجميع الصفوف إلى قائمة باستخدام GroupBy. عرب سايكلوجي. https://arabpsychology.com/statistics/pandas-how-to-group-rows-into-list-using-groupby/
looti, Mohammed. “بانداس: كيفية تجميع الصفوف إلى قائمة باستخدام GroupBy.” عرب سايكلوجي, 31 أغسطس 2026, https://arabpsychology.com/statistics/pandas-how-to-group-rows-into-list-using-groupby/.
looti, Mohammed. “بانداس: كيفية تجميع الصفوف إلى قائمة باستخدام GroupBy.” عرب سايكلوجي. أغسطس 31, 2026. https://arabpsychology.com/statistics/pandas-how-to-group-rows-into-list-using-groupby/.