تُعد مكتبة NumPy الركيزة الأساسية والعمود الفقري لمنظومة الحوسبة العلمية وتحليل البيانات وتعلم الآلة في لغة بايثون الحديثة. لا يقتصر دور هذه المكتبة على توفير هياكل بيانات متقدمة وسريعة فحسب، بل يمتد لتقديم نموذج برمجي متكامل يتيح للمطورين والباحثين التفاعل مع كتل البيانات الضخمة بكفاءة تقترب من اللغات منخفضة المستوى مثل C وFortran، مع الاحتفاظ بمرونة وبساطة بناء الجمل البرمجية في بايثون. ومن بين العمليات الجوهرية الأكثر تكراراً في خطوط معالجة البيانات اليومية تبرز عملية استخراج الأعمدة وتجزئتها، وهي العملية التي تشكل المنطلق الأساسي لعزل المتغيرات، وهندسة الميزات (Feature Engineering)، وتغذية النماذج الإحصائية والرياضية بالبيانات المناسبة.
إن فهم الآليات الدقيقة التي تحكم كيفية استخلاص عمود محدد أو مجموعة أعمدة من مصفوفة متعددة الأبعاد ليس مجرد مسألة حفظ لصيغ التقطيع والفهرسة، بل هو استيعاب عميق لكيفية تمثيل البيانات داخل الذاكرة العشوائية (RAM)، وتتبع أثر الخطوات الرياضية (Strides)، وإدراك الفروق الهندسية الدقيقة بين “الرؤية” السطحية (View) و”النسخة” العميقة (Copy). يحدد هذا الفهم مدى كفاءة الخوارزميات وسرعتها في التنفيذ، ويمنع الوقوع في أخطاء تسريب الذاكرة أو التعديلات غير المقصودة على المصفوفات الأصلية، مما يضمن كتابة شيفرات برمجية متينة ومستقرة قابلة للتوسع والإنتاج الصناعي.
يقدم هذا الدليل المرجعي الشامل تفكيكاً شاملاً ومكثفاً لجميع جوانب استخراج الأعمدة من مصفوفات NumPy، متدرجاً من المبادئ البنيوية الأساسية في تخطيط الذاكرة ونظام المحاور الرياضي، مروراً بجميع تقنيات الفهرسة البسيطة والمتقدمة والمنطقية، ووصولاً إلى تحليل الأداء الحسابي والتعامل مع المصفوفات عالية الأبعاد والمصفوفات الهيكلية المنظمة، بهدف تزويد المطورين وعلماء البيانات بمرجع احترافي متكامل يغطي كافة السيناريوهات النظرية والعملية.
- 1. مقدمة تأسيسية حول مصفوفات NumPy وميكانيكا الفهرسة متعددة الأبعاد
- 2. البنية النحوية الأساسية لفهرسة واستخراج الأعمدة
- 3. استخراج عمود فردي كمتجه أحادي البعد (1D Array)
- 4. استخراج عمود فردي مع الحفاظ على البنية الثنائية كمتجه عمودي (2D Column Vector)
- 5. استخراج نطاقات من الأعمدة المتتالية باستخدام التقطيع (Slicing)
- 6. الفهرسة المتقدمة (Fancy Indexing) لاستخراج أعمدة غير متتالية
- 7. الفهرسة المنطقية (Boolean Indexing) واستخراج الأعمدة وفق شروط محددة
- 8. التمييز بين الرؤية (View) والنسخة (Copy) عند استخراج الأعمدة
- 9. استخراج الأعمدة من المصفوفات متعددة الأبعاد (3D وما فوق)
- 10. استخراج الأعمدة في المصفوفات المنظمة والمسماة (Structured Arrays)
- 11. تحليل الأداء وتحسين كفاءة الذاكرة عند عمليات استخراج الأعمدة
- 12. الأخطاء الشائعة واستكشاف المشكلات وحلها في استخراج الأعمدة
- خاتمة واستنتاجات هندسية شاملة
- References
1. مقدمة تأسيسية حول مصفوفات NumPy وميكانيكا الفهرسة متعددة الأبعاد
1.1 طبيعة مصفوفات ndarray وبنيتها الهيكلية في الذاكرة
يمثل كائن المصفوفة متعددة الأبعاد ndarray في توثيق NumPy الرسمي بنية بيانات متجانسة مبنية على حجز كتلة ذاكرة خطية متصلة ومستمرة (Contiguous Block of Memory). على عكس القوائم الافتراضية في بايثون التي تخزن مراجع أو مؤشرات (Pointers) مبعثرة لكائنات منفصلة في الذاكرة، تقوم مصفوفة NumPy بتخزين القيم العددية الفعلية في مساحة ذاكرة متتالية، مما يتيح للمعالج الاستفادة القصوى من الذاكرة المخبأة المؤقتة (CPU Cache) وتنفيذ التعليمات الحسابية المتزامنة للمتجهات (SIMD Instructions).
ترتكز كل مصفوفة على ثلاث سمات جوهرية تحدد هويتها الحسابية: أولها الشكل (Shape) وهو صف (Tuple) من الأعداد الصحيحة يحدد طول كل بعد من أبعاد المصفوفة؛ وثانيها عدد الأبعاد (ndim) الذي يعبر عن رتبة المصفوفة الفضائية؛ وثالثها نوع البيانات المتجانس (dtype) الذي يحدد الحجم البايتي الدقيق وطريقة تفسير البتات المخزنة لكل عنصر، سواء كان عدداً صحيحاً أو عشرياً أو مركباً.
تعتمد آلية الوصول إلى العناصر والأعمدة اعتماداً جذرياً على الترتيب التخطيطي للذاكرة؛ حيث تتبع المصفوفات نمطين رئيسيين: الترتيب المتسلسل القياسي للغة سي (C-contiguous / Row-Major)، وفيه تُخزن عناصر الصف الواحد بجوار بعضها البعض في الذاكرة الخطية، والترتيب المتعامد القياسي للغة فورتران (Fortran-contiguous / Column-Major)، وفيه تُخزن عناصر العمود الواحد متتالية في الذاكرة. يتم التنقل بين هذه العناصر عبر مفهوم الخطوات (Strides)، وهو صف يحدد عدد البايتات التي يجب أن يقفزها مؤشر الذاكرة للوصول إلى العنصر التالي على طول كل محور، مما يفسر التباين في سرعة استخراج الأعمدة مقارنة بالصفوف تبعاً لنمط التخزين المعتمد.
1.2 الفلسفة الرياضية للوصول إلى المحاور في بايثون
تتبنى مكتبة NumPy نظام إحداثيات ديكارتي متعدد الأبعاد يعتمد على مفهوم “المحاور” (Axes)، حيث يتم ترقيم المحاور تسلسلياً بدءاً من الصفر وفق نظام الفهرسة القائم على الصفر (Zero-based Indexing). في مصفوفة ثنائية الأبعاد، يمثل المحور الصامت (Axis 0) الاتجاه الرأسي الممتد عبر الصفوف، بينما يمثل المحور العمودي (Axis 1) الاتجاه الأفقي الممتد عبر الأعمدة. هذا التمييز يعني أن أي عملية تقطيع تتطلب تحديد الموقع الدقيق على طول المحور الرأسي أولاً ثم المحور الأفقي ثانياً.
يختلف هذا النموذج المفاهيمي اختلافاً جذرياً عن القوائم المتداخلة (Nested Lists) في بايثون القياسية؛ فعند الرغبة في استخراج عنصر أو شريحة من قائمة قوائم، يتعين الوصول إلى القائمة الداخلية كخطوة منفصلة مثل matrix[row][col]، وهو ما يتطلب خطوتي وصول منفصلتين للذاكرة ومؤشرات غير مباشرة. في المقابل، توفر NumPy واجهة وصول موحدة عبر الأقواس المعقوفة وتفصل بين أبعاد المحاور بفاصلة بسيطة داخل تعبير الفهرسة الفردي، مما يسمح لمحرك C الداخلي بحساب إزاحة الذاكرة المباشرة (Direct Byte Offset) بخطوة حسابية جبرية واحدة فائقة السرعة.
1.3 أهمية استخراج الأعمدة في نمذجة ومعالجة البيانات
يشكل استخراج الأعمدة الفردية أو المجموعات الفرعية من الأعمدة حجر الزاوية في خطوط أنابيب تعلم الآلة والنمذجة الإحصائية. ففي سياق النماذج التنبؤية، يتم تنظيم مجموعات البيانات عادة في جداول مصفوفية بحيث تمثل الصفوف العينات والمشاهدات (Observations)، بينما تمثل الأعمدة المتغيرات المستقلة أو السمات التفسيرية (Features)، بالإضافة إلى متغير الاستجابة أو الهدف (Target Variable). تبرز الحاجة هنا لعزل مصفوفة الميزات الرياضية X عن متجه الهدف y عبر تقطيع المحور الأفقي بدقة بالغة.
علاوة على ذلك، تتطلب الحسابات الإحصائية الوصفية—مثل حساب الانحراف المعياري، والمتوسط، والارتباط، واكتشاف القيم الشاذة—تطبيق العمليات الرياضية على أعمدة محددة تمثل قياسات فيزيائية أو مالية مستقلة. تتيح ميكانيكا الفهرسة في NumPy تنفيذ هذه العمليات عبر استخلاص الأعمدة بصفتها مناظر أو رؤى دون الحاجة إلى مضاعفة استهلاك الذاكرة، مما يوفر بيئة مثالية لمعالجة البيانات الضخمة في الذاكرة المحدودة بكفاءة استثنائية.
2. البنية النحوية الأساسية لفهرسة واستخراج الأعمدة
2.1 فك شفرة المعامل الأساسي data[:, col_index]
تعتمد الصيغة القياسية لاستخراج عمود كامل من مصفوفة ثنائية الأبعاد على التعبير النحوي data[:, col_index]. يتكون هذا التعبير من جزأين رئيسيين تفصل بينهما فاصلة داخل قوس الفهرسة: الجزء الأول يمثل المحور 0 (الصفوف)، واستخدام رمز النقطتين الرأسيتين : بدون تحديد قيم بداية أو نهاية يعني تضمين كافة الصفوف في المصفوفة دون أي استثناء من البداية إلى النهاية. أما الجزء الثاني فيمثل المحور 1 (الأعمدة)، ويحمل الفهرس العددي الصريح للعمود المطلوب استخلاصه.
داخلياً، يقوم مفسر بايثون بترجمة هذا التعبير إلى استدعاء خاص للتابع السحري __getitem__ ممرراً كائن الشريحة slice(None, None, None) للمحور الأول، والعدد الصحيح col_index للمحور الثاني. يتولى كود C المضمن في مكتبة NumPy معالجة هذا الطلب من خلال تثبيت مؤشر العمود عند الإزاحة المقابلة للفهرس المعطى، والتحرك عبر خطوة الصفوف (Row Stride) لتجميع العناصر، مما ينتج استخلاصاً فورياً دون أي حلقات تكرارية مكلفة على مستوى بايثون.
2.2 التعامل مع الفهارس الموجبة والسالبة للأعمدة
تدعم NumPy الفهرسة ثنائية الاتجاه للمحاور؛ حيث تبدأ الفهارس الموجبة من الطرف الأيسر للمصفوفة بالفهرس 0 للعمود الأول، وتتصاعد تدريجياً 1, 2, ... N-1 نحو اليمين وصولاً إلى العمود الأخير في مصفوفة تحوي N من الأعمدة. توفر هذه الفهرسة وسيلة طبيعية وبديهية لاستعراض الأعمدة بتسلسل إدخالها المرجعي.
في المقابل، توفر الفهرسة السالبة (Negative Indexing) آلية قوية للوصول العكسي إلى الأعمدة بدءاً من الطرف الأيمن، حيث يشير الفهرس -1 حتماً إلى العمود الأخير، ويشير -2 إلى العمود قبل الأخير، وهكذا دواليك. تكتسب هذه الميزة أهمية استثنائية في كتابة الشيفرات المعيارية والأنابيب الحسابية العامة؛ إذ يمكن استخدام data[:, -1] للوصول المباشر إلى عمود التصنيف أو التسمية (Label) في مصفوفات البيانات دون الحاجة المسبقة لمعرفة إجمالي عدد الأعمدة أو قراءة شكل المصفوفة data.shape[1].
2.3 الحدود الرياضية لمؤشرات الأعمدة واستثناء IndexError
تفرض NumPy رقابة صارمة على حدود الفهرسة عند تمرير أرقام صحيحة صريحة لضمان سلامة الوصول إلى عناوين الذاكرة المخصصة وتجنب القراءة من خارج النطاق المسموح (Memory Corruption). فإذا كانت المصفوفة تحتوي على M من الصفوف وN من الأعمدة، فإن النطاق القانوني لفهرس الأعمدة الموجب هو المجال المغلق [0, N-1]، والنطاق السالب هو [-N, -1].
عند محاولة تمرير فهرس يقع خارج هذا النطاق الرياضي، مثل طلب العمود ذي الفهرس 5 في مصفوفة تتألف من 4 أعمدة فقط، تقوم المكتبة فوراً بإيقاف التنفيذ وإطلاق الاستثناء الشهير IndexError: index 5 is out of bounds for axis 1 with size 4. لتفادي هذا الاستثناء في البرمجيات التفاعلية أو معالجة المدخلات الديناميكية، يُنصح بتطبيق دوال تحقق قبلية تعتمد على الخاصية data.shape[1] للتأكد من مشروعية الفهرس قبل إجراء الاستخلاص، كما هو موضح بالمنطق البرمجي التالي:
- التحقق من أن
-data.shape[1] <= col_index < data.shape[1]قبل تمرير الاستعلام. - استخدام هياكل التحوط البرمجي
try...except IndexErrorللتعامل مع الأخطاء غير المتوقعة في بيئات الإنتاج الحية.
3. استخراج عمود فردي كمتجه أحادي البعد (1D Array)
3.1 التطبيق العملي لاستخراج عمود واحد مسطح
عند استخدام الصيغة الكلاسيكية المعتمدة على تمرير عدد صحيح منفرد كفهرس للعمود مثل column_1d = data[:, 2]، تُرجع NumPy العمود الثالث من المصفوفة بصيغة متجه أحادي البعد (Flat 1D Array). على سبيل المثال، إذا كانت لدينا مصفوفة ذات أبعاد (4, 5) تحتوي على 4 صفوف و5 أعمدة، فإن ناتج العملية سيكون مصفوفة جديدة ذات شكل أحادي البعد يُرمز له رياضياً بالصف (4,) بدلاً من (4, 1).
تتم هذه العملية عبر إسقاط المحور المستهدف (Dimensionality Drop)، حيث يُزال البعد الثاني تماماً من التمثيل الرياضي للشكل، ويتم التعامل مع القيم الناتجة كسلسلة رقمية متصلة على محور أحادي (Axis 0). على الرغم من أن البيانات المعروضة تمثل عموداً رأسياً في المصفوفة الأصلية، إلا أن تمثيلها المستخرج يصبح متجهاً خطياً مسطحاً لا يمتلك أي اتجاه رأسي أو أفقي في المنطق الرياضي لـ NumPy.
3.2 الآثار الرياضية لتسطيح الأبعاد (Dimensionality Reduction)
يترتب على تسطيح أبعاد العمود المستخرج آثار رياضية وحسابية عميقة في سياق الجبر الخطي والعمليات المصفوفية. فالمتجه أحادي البعد ذو الشكل (N,) لا يخضع لنفس قواعد الجداء المصفوفي التقليدي (Matrix Multiplication) التي تحكم المصفوفات ثنائية الأبعاد. عند محاولة ضرب هذا المتجه في مصفوفة أخرى باستخدام المعامل @ أو الدالة np.matmul، تُجري NumPy تعديلاً ديناميكياً مؤقتاً لتطابق الأبعاد ثم تُعيد النتيجة إلى فضاء أحادي البعد مجدداً.
بالإضافة إلى ذلك، فإن المتجه أحادي البعد لا يمتلك مفهوم المنقول الجبري (Transpose) الفعلي؛ فتطبيق الخاصية column_1d.T لن يغير من شكله الرياضي شيئاً وسيظل (N,) دون تغيير. هذا السلوك يختلف جوهرياً عن المتجه العمودي ثنائي الأبعاد ذي الشكل (N, 1) الذي يتحول عند نقله جبرياً إلى متجه صفي ذي شكل (1, N)، وهو ما يتطلب من المطور الانتباه الدقيق لنوع المتجه الناتج وفق متطلبات المعادلات الرياضية المستخدمة.
3.3 حالات الاستخدام المثالية للمتجه أحادي البعد
يُعد المتجه أحادي البعد الخيار الأمثل والأنسب في مجموعة واسعة من التطبيقات التحليلية والعملية، ومن أبرزها:
- التوليد البصري والرسوم البيانية: تتطلب دوال مكتبة Matplotlib مثل
plt.plot(x, y)أوplt.scatter(x, y)مدخلات أحادية البعد للمحاور الإحداثية، ويسهل تمرير الأعمدة المسطحة مباشرة دون الحاجة إلى معالجة إضافية للأبعاد. - الحسابات الإحصائية الوصفية: تعمل دوال النزعة المركزية مثل
np.mean()، وnp.median()، وnp.std()، وnp.var()بكفاءة وسرعة فائقة على المتجهات أحادية البعد لحساب مؤشرات السمة المستخرجة. - العمليات الحسابية القياسية (Element-wise Operations): عند تطبيق تحويلات لوغاريتمية أو أُسية على سمة محددة بشكل مستقل ومعزول عن باقي المصفوفة.
4. استخراج عمود فردي مع الحفاظ على البنية الثنائية كمتجه عمودي (2D Column Vector)
4.1 استخدام الفهرسة بالقوائم data[:, [col_index]]
في كثير من الحالات الهندسية، يُشترط الحفاظ الصارم على الرتبة الثنائية للمصفوفة بحيث يظل العمود المستخرج محتفظاً بهيكله كمتجه عمودي (Column Vector) ذي شكل (N, 1) بدلاً من تسطيحه إلى (N,). يتم تحقيق ذلك بسهولة وأناقة نحوية عبر تغليف فهرس العمود داخل قائمة برمجية، كأن نكتب: column_2d = data[:, [2]].
يكمن السبب الرياضي في سلوك NumPy وراء ذلك في تفعيل قواعد الفهرسة المتقدمة (Advanced Indexing)؛ فعند تمرير قائمة تحتوي على رقم صحيح [2] بدلاً من الرقم القياسي المباشر 2، تحافظ المكتبة على المحور الثاني ببعد مقداره طول القائمة الممررة (وهو 1 هنا). ينتج عن ذلك مصفوفة ثنائية الأبعاد تتطابق في عدد صفوفها مع المصفوفة الأصلية مع بقاء بعد الأعمدة محجوزاً بحجم 1، وهو ما تؤكده الخاصية column_2d.shape التي تعيد (N, 1) بدقة متناهية.
4.2 استخدام np.newaxis و None لإعادة تشكيل العمود
توجد مقاربة احترافية أخرى لإنشاء المتجهات العمودية ثنائية الأبعاد تعتمد على إدخال بعد وهمي جديد عبر الثابت المخصص np.newaxis أو مرادفه اللغوي في بايثون None. يمكن للمبرمج استخراج العمود كشريحة بسيطة ثم زيادة رتبته في خطوة تعبيرية موحدة مثل: data[:, 2, np.newaxis] أو data[:, 2, None]، أو حتى بتطبيق الإضافة على المحور الثاني مباشرة: data[:, 2:3].
تتميز هذه الطريقة بوضوحها الدلالي الصريح (Semantic Clarity) داخل الشيفرة؛ فهي توضح للقارئ أن الغرض هو إضافة بعد إحداثي جديد للمصفوفة لتسهيل عمليات البث الرياضي (Broadcasting). من حيث الأداء والكفاءة، تتفوق طريقة np.newaxis والتقطيع 2:3 على طريقة القوائم [2] لأنها تنشئ “رؤية” (View) خفيفة وسريعة للبيانات في الذاكرة دون تخصيص مساحة جديدة أو استنساخ للقيم الأصلية.
4.3 استخدام دالة reshape لتعديل هيئة العمود المستخرج
توفر دالة إعادة التشكيل reshape مرونة هيكلية فائقة للتحكم في أبعاد المصفوفات المستخرجة. يمكن للمطور استخراج العمود بأسلوب التسطيح الأحادي أولاً ثم إعادة صياغة أبعاده ليصبح متجهاً عمودياً باستخدام التعبير: data[:, 2].reshape(-1, 1).
يعمل المعامل -1 في دالة reshape كأداة استنتاج تلقائي ذكية؛ حيث يوجه محرك NumPy إلى حساب طول البعد الأول تلقائياً بناءً على الحجم الإجمالي للبيانات وتقسيمه على الأبعاد الأخرى المحددة (وهي 1 في حالتنا). تكتسب هذه الصيغة أهمية جوهرية وقصوى عند العمل مع مكتبات تعلم الآلة المتقدمة مثل Scikit-Learn؛ حيث تفرض مصنفات ومقدرات التعلم مثل LinearRegression أو StandardScaler أن تكون مصفوفة الميزات المدخلة ثنائية الأبعاد حتماً بشكل (n_samples, n_features)، مما يجعل استدعاء reshape(-1, 1) شرطاً لازماً لتفادي أخطاء توافق الأبعاد (Dimensionality Mismatch Exceptions).
5. استخراج نطاقات من الأعمدة المتتالية باستخدام التقطيع (Slicing)
5.1 الصيغة العامة لتقطيع الأعمدة: [start:stop:step]
يوفر نظام التقطيع القياسي في بايثون وNumPy آلية قوية وسلسة لاستخلاص كتل متتابعة من الأعمدة بالاعتماد على الصيغة الثلاثية [start:stop:step]. يُحدد المعامل start فهرس العمود الأول المراد تضمينه، بينما يمثل المعامل stop الحد النهائي الحصري (Exclusive Upper Bound) الذي تتوقف عنده العملية دون تضمينه، في حين يحدد المعامل step مقدار الزيادة أو معدل القفز بين الأعمدة المتعاقبة.
إذا أردنا استخراج نطاق يضم الأعمدة الثاني والثالث والرابع من مصفوفة ما، فإننا نكتب data[:, 1:4]، حيث يبدأ التقطيع من الفهرس 1 ويقف عند الفهرس 4 ليشمل الأعمدة 1 و2 و3. وإذا رغبنا في البدء من العمود الأول وحتى العمود الثالث، يمكن إسقاط المعامل الأول وكتابة data[:, :3]. وبالمثل، لاستخراج كافة الأعمدة بدءاً من العمود الثالث وحتى نهاية المصفوفة، نستخدم التعبير data[:, 2:]، مما يمنح المطور اختصاراً برمجياً دقيقاً وعالي القراءة.
5.2 تحديد معدل الخطوة (Step) لاقتطاع الأعمدة المنتظمة
يتيح تفعيل معامل الخطوة step استخلاص أنماط دورية ومنتظمة من الأعمدة عبر المصفوفة الحسابية. على سبيل المثال، يمكن للمحلل استخراج جميع الأعمدة ذات الفهارس الزوجية (0، 2، 4، …) باستخدام التعبير data[:, 0::2] أو باختصار data[:, ::2]، وهو أسلوب شائع عند التعامل مع بيانات المسح المكاني أو الإشارات المجدولة التي تحتوي على قنوات زوجية وفردية منفصلة.
وعلى النقيض من ذلك، يمكن استخراج الأعمدة ذات الفهارس الفردية (1، 3، 5، …) عبر ضبط نقطة البداية لتصبح data[:, 1::2]. بالإضافة إلى ذلك، يوفر تمرير قيمة سالبة للخطوة مثل data[:, ::-1] وسيلة سريعة وفورية لعكس الترتيب المكاني للأعمدة بالكامل من اليمين إلى اليسار، دون إحداث أي حركة فيزيائية للبيانات في الذاكرة الحقيقية، بل عبر عكس خطوات المحور الثاني فقط في مصفوفة البيانات الواصفة (Metadata Header).
5.3 التقطيع متعدد الأبعاد المدمج بين الصفوف والأعمدة
تصل قوة التقطيع إلى ذروتها عند دمج تقطيع الصفوف مع تقطيع الأعمدة في أمر برمجي موحد، مما يسمح بعزل كتل مستطيلة أو مصفوفات فرعية (Sub-matrices) محددة جغرافياً أو زمنياً من المصفوفة الأم. على سبيل المثال، التعبير sub_matrix = data[0:10, 2:5] يقوم باستخراج الصفوف العشرة الأولى بالتوازي مع الأعمدة من الفهرس 2 إلى 4 فقط.
يوضح الجدول التوضيحي التالي مجموعة من التعبيرات الشائعة للتقطيع المدمج وتأثيرها الهيكلي على أبعاد البيانات الناتجة:
data[:, :]: استخراج كافة الصفوف وكافة الأعمدة (إنشاء رؤية كاملة للمصفوفة).data[:5, :2]: استخراج أول 5 صفوف مع أول عمودين فقط (شكل الناتج:(5, 2)).data[::2, 1::2]: اقتطاع الصفوف الزوجية متقاطعة مع الأعمدة الفردية.data[10:20, -3:]: استخراج الصفوف من 10 إلى 19 مع آخر 3 أعمدة في المصفوفة.
6. الفهرسة المتقدمة (Fancy Indexing) لاستخراج أعمدة غير متتالية
6.1 تمرير مصفوفات أو قوائم الفهارس الصحيحة
عندما تقتضي متطلبات العمل استخراج مجموعة من الأعمدة المتباعدة أو غير المتتالية التي لا يربطها نمط حسابي منتظم، يعجز التقطيع البسيط عن تلبية الغرض. هنا يأتي دور “الفهرسة المتقدمة” (Fancy Indexing)، وهي تقنية تتيح تمرير قائمة برمجية أو مصفوفة من الأعداد الصحيحة تمثل الفهارس الصريحة للأعمدة المرغوبة، كأن نكتب: selected_cols = data[:, [0, 2, 4, 7]].
تتميز هذه التقنية بمرونة فائقة تمكن المطور من إعادة ترتيب أعمدة المصفوفة كيفما يشاء؛ فتمرير الفهارس بترتيب عكسي أو مخصص مثل data[:, [3, 0, 1]] سيُنشئ مصفوفة جديدة تترتب أعمدتها وفق ترتيب الفهارس الممررة تماماً. ليس هذا فحسب، بل يمكن أيضاً تكرار عمود بعينه عدة مرات داخل المصفوفة المستخرجة عبر تكرار فهرسه مثل data[:, [1, 1, 2]]، وهو ما يُستخدم في خوارزميات مضاعفة العينات وهندسة السمات التكرارية.
6.2 توليد مصفوفات الأعمدة باستخدام np.arange و np.ix_
في بيئات الحوسبة المتقدمة، يتم توليد فهارس الأعمدة ديناميكياً باستخدام دوال التوليد مثل np.arange أو عبر توظيف الشروط الرياضية. للتوليد الديناميكي لنطاقات مخصصة، يمكن بناء متجهات الفهارس برمجياً ثم تمريرها إلى المصفوفة لاستخلاص الأعمدة المقابلة بكفاءة عالية.
علاوة على ذلك، توفر دالة الشباك الرياضية المساعدة np.ix_ أداة متخصصة لبناء شبكات فهرسة متقاطعة عبر المحاور المختلفة. عند الرغبة في استخراج صفوف غير متتالية وأعمدة غير متتالية في آن واحد، فإن كتابة data[[0, 2], [1, 3]] قد تفضي إلى استخراج أزواج النواحي الفردية (0,1) و(2,3) بدلاً من الشبكة الرباعية المتقاطعة؛ وهنا تُستخدم الدالة data[np.ix_([0, 2], [1, 3])] لبناء المولد الإحداثي الديكارتي وتوليد المصفوفة الفرعية المطلوبة بدقة هندسية مطلقة.
6.3 الآثار الهيكلية للفهرسة المتقدمة على نوع المخرجات
تحمل الفهرسة المتقدمة فارقاً جوهرياً وحرجاً يميزها عن التقطيع البسيط؛ حيث تفرض الفهرسة المتقدمة دوماً إنشاء مصفوفة جديدة مستقلة بالكامل في الذاكرة (Deep Copy) بدلاً من إنشاء رؤية مجردة (View). تتطلب هذه العملية تخصيص مساحة تخزينية جديدة في الذاكرة العشوائية ونقل البايتات الفعلية من المصفوفة الأم إلى المصفوفة الفرعية المستخرجة.
يترتب على هذا الفارق البنيوي ميزتان متناقضتان: من جهة، تمنح الفهرسة المتقدمة أماناً كاملاً ضد الآثار الجانبية، حيث يمكن تعديل قيم الأعمدة المستخرجة بحرية تامة دون القلق من تشويه أو تغيير بيانات المصفوفة الأصلية. ومن جهة أخرى، قد يؤدي استخدام الفهرسة المتقدمة لاستخراج أعمدة من مصفوفات عملاقة تحتوي على ملايين السجلات إلى استهلاك مضاعف ومفاجئ للذاكرة وانخفاض ملحوظ في الأداء الحسابي نتيجة تكاليف الحجز والنسخ، مما يستدعي توخي الحذر عند هندسة خطوط البيانات فائقة الضخامة.
7. الفهرسة المنطقية (Boolean Indexing) واستخراج الأعمدة وفق شروط محددة
7.1 إنشاء وتطبيق أقنعة الشروط المنطقية (Boolean Masks)
تُعد الفهرسة المنطقية (Boolean Indexing) واحدة من أقوى الميزات التحليلية في NumPy، حيث تسمح باستخراج الأعمدة بناءً على تلبية شروط منطقية محددة بدلاً من الاعتماد على الفهارس الرقمية المسبقة. تبدأ العملية بإنشاء مصفوفة أحادية البعد من القيم المنطقية (Boolean Mask) تحوي قيماً ثنائية True أو False، ويكون طولها مساوياً تماماً لعدد الأعمدة في المحور الأول للمصفوفة المستهدفة.
عند تمرير هذا القناع المنطقي إلى المصفوفة بالصيغة filtered_data = data[:, mask]، يقوم محرك المكتبة باستخراج كافة الأعمدة التي تقابل القيمة True فقط في القناع وإهمال الأعمدة المقابلة للقيمة False. يُشترط هنا بدقة رياضية أن يتطابق الحجم المنطقي للقناع mask.shape[0] مع طول محور الأعمدة في المصفوفة data.shape[1]، وإلا ستطلق NumPy خطأ عدم تطابق الأبعاد IndexError: boolean index did not match indexed array along dimension 1.
7.2 تصفية الأعمدة استناداً إلى خصائص قيمها الإحصائية
تتجلى التطبيقات العملية للفهرسة المنطقية في قدرتها على عزل وتصفية الأعمدة بناءً على الحسابات الإحصائية الإجمالية المطبقة على طول المحور الرأسي (Axis 0). ومن أبرز هذه التطبيقات العملية:
- عزل الأعمدة ذات المتوسط المرتفع: يمكن استخراج الأعمدة التي يتجاوز متوسطها الحسابي حداً معيناً عبر التعبير:
data[:, np.mean(data, axis=0) > 50.0]. - إزالة الميزات ذات التباين الصفري (Constant Features): في مرحلة تنظيف البيانات لتعلم الآلة، يمكن حذف الأعمدة الثابتة عديمة الفائدة عبر تصفية التباين:
data[:, np.var(data, axis=0) > 0]. - استبعاد الأعمدة الحاوية على قيم مفقودة (NaNs): لعزل الأعمدة النظيفة الخالية تماماً من البيانات المفقودة، يمكن دمج الدالة
np.isnanمع النفي المنطقي:data[:, ~np.isnan(data).any(axis=0)].
7.3 الدمج بين الشروط المنطقية المتعددة بواسطة المعاملات الثنائية
تتيح مكتبة NumPy دمج عدة شروط معقدة لتصفية الأعمدة عبر استخدام معاملات البت الثنائية (Bitwise Operators): المعامل & للربط المنطقي الشرطي (AND)، والمعامل | للجمع المنطقي الانفصالي (OR)، والمعامل ~ للنفي المنطقي (NOT). لا يجوز هنا استخدام الكلمات المفتاحية القياسية في بايثون مثل and أو or لأنها تقيم الحقيقة المنطقية للكائن ككل بدلاً من تقييم العناصر الفردية في المصفوفة.
تتطلب كتابة الشروط المنطقية المركبة في NumPy وضع كل شرط فرعي داخل أقواس دائرية منفصلة () بصورة إلزامية لضبط أسبقية العمليات البرمجية وتفادي أخطاء التقييم النحوي. على سبيل المثال، لاستخراج الأعمدة التي يقع متوسطها بين القيمة 10 والقيمة 50 مع اشتراط أن يكون تباينها أكبر من 1، نستخدم التعبير المركب:
mask = (np.mean(data, axis=0) > 10) & (np.mean(data, axis=0) < 50) & (np.var(data, axis=0) > 1) ثم نستخلص البيانات: data[:, mask].
8. التمييز بين الرؤية (View) والنسخة (Copy) عند استخراج الأعمدة
8.1 التقطيع البسيط ومفهوم الرؤية السطحية (View)
عند استخراج عمود أو شريحة من الأعمدة باستخدام التقطيع البسيط مثل col_view = data[:, 1] أو cols_view = data[:, 1:3]، لا تقوم NumPy بإنشاء كائن جديد يحتوي على بيانات جديدة، بل تُنشئ ما يُعرف بـ “الرؤية” (View). الرؤية هي مجرد ترويسة مصفوفية جديدة (New ndarray metadata wrapper) تشير مباشرة إلى نفس موقع الذاكرة الأصلي للمصفوفة الأم، مع تعديل معلمات الشكل (Shape) والخطوات (Strides) ونقطة البداية فقط.
يمكن التحقق برمجياً مما إذا كانت المصفوفة المستخرجة تمثل رؤية أم لا عبر فحص الخاصية col_view.base؛ فإذا كانت النتيجة تُرجع كائن المصفوفة الأصلية، فإن ذلك دليل قاطع على مشاركة الذاكرة. ينطوي هذا السلوك على أهمية بالغة من حيث الكفاءة والسرعة الصفرية في الحجز (Zero-copy Performance)، ولكنه يفرض مخاطر برمجية تسمى “الآثار الجانبية غير المقصودة” (Side Effects)؛ حيث إن أي تعديل على قيم col_view سيؤدي مباشرة وفورياً إلى تعديل وتغيير قيم المصفوفة الأصلية data في نفس الخلية التخزينية.
8.2 الفهرسة المتقدمة وإنشاء النسخ المستقلة (Deep Copy)
خلافاً للتقطيع البسيط، فإن استخدام الفهرسة المتقدمة بالقوائم مثل col_copy = data[:, [1]] أو الفهرسة المنطقية data[:, mask] يؤدي حتماً إلى إنشاء نسخة مستقلة جديدة كلياً (Deep Copy). عند فحص الخاصية col_copy.base، ستُرجع القيمة None، مما يؤكد أن البيانات المستخرجة تقع في عنوان ذاكرة جديد لا يرتبط بالكتلة الأصلية بأي شكل من الأشكال.
في هذا السيناريو، يتمتع المطور بحرية مطلقة في تعديل قيم col_copy، واستبدال محتوياتها، وتطبيق المعالجات التحويلية عليها دون أي خوف من التأثير على بيانات المصفوفة الأم. وإذا رغب المبرمج في تحقيق هذا العزل التام أثناء استخدام التقطيع البسيط، يتعين عليه استدعاء التابع الصريح copy() بالصيغة: safe_column = data[:, 1].copy()، وهو المعيار الذهبي المتبع لضمان استقلالية البيانات وسلامتها البرمجية.
8.3 المقارنة الاقتصادية في استهلاك الذاكرة وسرعة المعالجة
يوضح التحليل الحسابي المقارن بين الرؤية والنسخة تبايناً جذرياً في التعقيد الزمني والمكاني للعمليات البرمجية داخل بيئات المعالجة المكثفة:
- التعقيد الزمني (Time Complexity): تُنجز عمليات الرؤية (Views) بزمن ثابت مقداره
O(1)بغض النظر عن حجم البيانات لأنها تتطلب فقط تحديث الترويسة الوصفية، بينما تتطلب عمليات النسخ (Copies) زمناً خطياً مقدارهO(N)يتناسب طردياً مع عدد العناصر المنقولة عبر ممرات الذاكرة. - التعقيد المكاني (Space Complexity): تستهلك الرؤية قدراً ضئيلاً وثابتاً من الذاكرة لا يتجاوز بضع بايتات للترويسة
O(1)، في حين تضاعف عمليات النسخ استهلاك الذاكرة العشوائيةO(N). - استراتيجيات التحرير (Memory Management): في المصفوفات الضخمة التي تملأ سعة RAM، يؤدي الاعتماد الخاطئ على النسخ المتكرر إلى إطلاق أخطاء نفاد الذاكرة
MemoryError، مما يبرز تفوق الرؤى عند بناء تطبيقات البيانات الضخمة.
9. استخراج الأعمدة من المصفوفات متعددة الأبعاد (3D وما فوق)
9.1 فهرسة المصفوفات ثلاثية الأبعاد (3D Tensors)
يمتد مفهوم استخراج الأعمدة بسلاسة عند التعامل مع مصفوفات ذات رتب هندسية أعلى مثل التنسورات ثلاثية الأبعاد (3D Tensors) ذات الشكل (Depth/Batch, Rows, Columns)، والتي تُستخدم بكثافة في معالجة الصور الرقمية الملونة والمقاطع الزمنية متعددة القنوات. في هذه المصفوفات، يمثل المحور 0 مستوى الشرائح أو الدفعات (Batches)، والمحور 1 يمثل الصفوف الرأسية، والمحور 2 يمثل الأعمدة الأفقية.
لاستخراج عمود محدد عبر كافة الشرائح والصفوف في مصفوفة ثلاثية الأبعاد، يتم تمديد تعبير التقطيع ليشمل كافة المحاور السابقة وتثبيت محور العمود المستهدف بالصيغة: data[:, :, col_index]. على سبيل المثال، إذا كانت لدينا مصفوفة ذات شكل (10, 100, 30) تمثل 10 دفعات من البيانات كل دفعة تحوي 100 صف و30 عموداً، فإن تنفيذ data[:, :, 5] سيستخلص العمود السادس من كل شريحة ليُنتج مصفوفة ثنائية الأبعاد ذات شكل (10, 100) تمثل القناة المحددة عبر كافة الدفعات.
9.2 استخدام رمز الحذف (Ellipsis …) في الفهرسة متعددة الأبعاد
توفر بايثون ومكتبة NumPy كائناً خاصاً وأنيقاً يُعرف برمز الحذف (Ellipsis)، ويُكتب برمجياً على هيئة ثلاثة نقاط متتالية .... يعمل هذا الرمز كبديل ذكي يغني عن كتابة النقطتين الرأسيتين المتعددة :, :, ... للإشارة إلى شمول كافة المحاور المتبقية غير المحددة في التعبير الفهرسي.
عند الرغبة في استخراج العمود الأخير من مصفوفة معقدة دون الحاجة إلى القلق بشأن عدد أبعادها الكلية (سواء كانت ثلاثية، رباعية، أو خماسية الأبعاد)، يمكن كتابة التعبير الموحد: data[..., col_index]. تترجم NumPy رمز ... تلقائياً ليغطي جميع الأبعاد الأولى وصولاً إلى البعد الأخير، مما يرفع من جودة الشيفرة وقابليتها لإعادة الاستخدام عبر هياكل بيانات متباينة الأبعاد في شبكات التعلم العميق (Deep Neural Networks).
9.3 استخراج أعمدة معينة عبر أبعاد متباينة
تسمح مرونة الفهرسة المتقدمة بتثبيت أبعاد معينة واستخلاص أعمدة محددة من شرائح مختارة بدقة بالغة. على سبيل المثال، عند كتابة data[0, :, 2] في مصفوفة ثلاثية الأبعاد، نقوم بتثبيت الشريحة الأولى على المحور 0، واستخراج العمود الثالث عبر كافة صفوفها، ليكون الناتج متجهاً أحادي البعد يمثل عمود تلك الشريحة المحددة فقط.
كذلك، يمكن دمج التقطيع عبر المحاور المختلفة كأن نكتب data[2:5, 10:20, 1:4] لتفكيك التنسور الرياضي واستخراج كتلة حجمية ثلاثية الأبعاد محددة بدقة. تُعد هذه العمليات البنية التحتية لمعالجة مصفوفات السلاسل الزمنية المالية ثلاثية الأبعاد (الشركات، الأيام، المؤشرات الفنية)؛ حيث يتيح التقطيع استخراج مؤشر فني محدد (عمود) لشركات معينة عبر نطاق زمني محدد بخطوة حسابية مدمجة وسريعة.
10. استخراج الأعمدة في المصفوفات المنظمة والمسماة (Structured Arrays)
10.1 بنية المصفوفات المنظمة ومطابقتها لمفهوم الجداول
تمثل المصفوفات المنظمة (Structured Arrays) في NumPy المعادل الهندسي المنخفض المستوى للجداول وقواعد البيانات العلائقية؛ حيث تتيح للمطور تعريف أنواع بيانات مركبة ومخصصة (Custom Compound dtypes) تتألف من عدة حقول (Fields) يحمل كل حقل منها اسماً فريداً ونوع بيانات خاصاً به (مثل خلط الأعداد الصحيحة مع النصوص والأرقام العشرية داخل نفس السجل).
يختلف هذا الهيكل تماماً عن مصفوفات ndarray القياسية المتجانسة، ويقترب بنيوياً من كتل السجلات المترابطة في لغة C (C-structs) أو جداول مكتبة Pandas، ولكنه يتميز باحتفاظه بالأداء العالي للذاكرة المتصلة والسرعة الصفرية دون الأعباء البرمجية الثقيلة للأطر الجدولية، مما يجعله مثالياً لتخزين وقراءة الملفات الثنائية المعقدة (Binary Files) والبيانات الجيولوجية والفلكية.
10.2 استخراج الأعمدة بالاسم (Field-based Access)
في المصفوفات المنظمة، لا يتم الوصول إلى الأعمدة عبر الفهارس العددية للمحاور data[:, 0]، بل يتم استخراجها بالاسم المباشر للحقل عبر التمرير النصي للمفتاح داخل الأقواس المعقوفة، مثل: struct_data['age'] أو struct_data['salary']. يُرجع هذا الاستدعاء مصفوفة أحادية البعد تحتوي على كافة قيم ذلك العمود المحدد لنوع البيانات الأصلي للحقل.
كما تدعم المصفوفات المنظمة استخراج حقول وأعمدة متعددة في آن واحد عبر تمرير قائمة من الأسماء بالصيغة: struct_data[['name', 'salary']]. ينتج عن هذه العملية مصفوفة منظمة جديدة تحتوي فقط على الحقول المحددة، مع الحفاظ الكامل على الربط الهيكلي بين السجلات، مما يوفر وسيلة برمجية فائقة الوضوح تجمع بين سهولة التسمية النصية وسرعة معالجة لغة C المباشرة.
10.3 تطبيقات المصفوفات المنظمة في السجلات العلمية المعقدة
تبرز التطبيقات الحيوية للمصفوفات المنظمة في النظم المدمجة والأجهزة العلمية التي تولد تدفقات بيانات هجينة (Heterogeneous Data Streams). على سبيل المثال، قد يسجل جهاز رصد بيئي حزمة تتكون من (timestamp: int64, temperature: float32, status: bool) بتردد آلاف العينات في الثانية.
يوفر استخراج الأعمدة بالاسم في هذه المنظومات مرونة فائقة لمعالجة قنوات الاستشعار بشكل مستقل وفوري دون الحاجة لتحويل البيانات إلى أطر بيانات ثقيلة. علاوة على ذلك، يمكن إجراء تحويل بيني سلس بين المصفوفات المنظمة ومصفوفات ndarray التقليدية ثنائية الأبعاد باستخدام دوال التفكيك المساعد مثل numpy.lib.recfunctions.structured_to_unstructured عندما تقتضي الحاجة تطبيق خوارزميات الجبر الخطي القياسية.
11. تحليل الأداء وتحسين كفاءة الذاكرة عند عمليات استخراج الأعمدة
11.1 أثر تخطيط الذاكرة (Memory Layout) على سرعة قراءة الأعمدة
يرتبط الأداء الحسابي لعملية استخراج الأعمدة ارتباطاً وثيقاً بالبنية الفيزيائية لتخطيط الذاكرة. في الترتيب الافتراضي لمصفوفات NumPy وهو نمط C-contiguous، يتم تخزين البيانات صفاً تلو الآخر؛ وهذا يعني أن عناصر العمود الواحد تكون متباعدة في الذاكرة بمسافة بايتية تعادل طول الصف بالكامل مضروباً في حجم العنصر.
عند استخراج عمود من مصفوفة ضخمة بهذا الترتيب وتمريره في حلقة معالجة، يعاني المعالج من ظاهرة الإخفاق في الذاكرة المخبأة (Cache Misses)؛ لأن كل قراءة لعنصر في العمود تتطلب جلب خط ذاكرة جديد بالكامل من RAM. ولحل هذه المعضلة في التطبيقات التي تعتمد بشكل مكثف على معالجة الأعمدة، يُنصح بتحويل المصفوفة إلى ترتيب فورتران المتعامد باستخدام fortran_data = np.asfortranarray(data)؛ حيث تصبح عناصر كل عمود متجاورة تماماً في مسار الذاكرة الخطي، مما يؤدي إلى تسريع عمليات القراءة والحساب بمقدار أضعاف مضاعفة بفضل الاستفادة من التدفق التتابعي لذاكرة الكاش (Sequential Cache Prefetching).
11.2 استخدام أدوات التقييم الزمني (Benchmarking) عبر %timeit
يكشف التقييم الزمني الدقيق باستخدام أداة القياس المعيارية %timeit في بيئات بايثون وJupyter عن تباينات دراماتيكية في سرعة التنفيذ بين الطرق المختلفة لاستخراج الأعمدة. عند اختبار استخراج عمود من مصفوفة تحتوي على مليون صف وعشرة أعمدة، نصل إلى النتائج التحليلية الموضحة أدناه:
- التقطيع البسيط
data[:, 2](رؤية View): يستغرق زمناً يقارب 150 نانو ثانية فقط، وهو وقت مهمل يمثل مجرد إنشاء الترويسة الوصفية دون مساس بالبيانات. - الفهرسة المتقدمة
data[:, [2]](نسخة Copy): تستغرق ما يقارب 15 إلى 25 مللي ثانية، أي أبطأ بأكثر من 100,000 مرة من التقطيع البسيط؛ نظراً لضرورة حجز الذاكرة ونسخ مليون عنصر. - الدالة المخصصة
np.take(data, 2, axis=1): تقدم أداءً متوسطاً فائق السرعة للنسخ المباشر يتفوق على الفهرسة بالقوائم بفضل تجنب بعض أعباء التفسير في مفسر بايثون.
11.3 دوال الفهرسة المتخصصة: np.take و np.compress
توفر مكتبة NumPy دوال برمجية متخصصة ومبنية بلغة C تهدف إلى تسريع عمليات الاستخراج والفهرسة وتوفير بدائل وظيفية متقدمة للأقواس المعقوفة. ومن أبرز هذه الدوال:
- الدالة
np.take: تتيح استخراج الأعمدة عبر الفهارس الرقمية المباشرة بصيغة وظيفية واضحة:cols = np.take(data, indices=[0, 3], axis=1). تتميز هذه الدالة بسرعتها العالية وسهولة دمجها في خطوط المعالجة الوظيفية، كما توفر وضعيات مرنة للتعامل مع الفهارس الخارجة عن الحدود مثل خيار الالتفافmode='wrap'. - الدالة
np.compress: تمثل البديل الوظيفي الفائق السرعة للفهرسة المنطقية، حيث تقبل قناعاً منطقياً وتطبقه على المحور المحدد:filtered = np.compress(condition_mask, data, axis=1). تتفوقcompressفي تفادي بعض الحسابات الوسيطة في بايثون عند تصفية الأعمدة في المصفوفات الكبيرة.
12. الأخطاء الشائعة واستكشاف المشكلات وحلها في استخراج الأعمدة
12.1 الخلط الشائع بين فهرسة القوائم وفهرسة مصفوفات NumPy
يقع العديد من المبتدئين والمطورين المهاجرين من بايثون القياسية في فخ الخلط النحوي بين هياكل البيانات المختلفة. من أبرز هذه الأخطاء محاولة تطبيق صيغة تقطيع NumPy على القوائم المتداخلة مثل محاولة كتابة nested_list[:, 1]، وهو ما يقود حتماً إلى إطلاق الخطأ النحوي TypeError: list indices must be integers or slices, not tuple؛ لأن القوائم لا تدعم الفهرسة متعددة المحاور المفصولة بفواصل.
خطأ شائع آخر يتمثل في الخلط بين ترتيب المحاور وكتابة data[1, :] ظناً من المطور أنه يستخرج العمود الأول، في حين أن هذا التعبير يستخرج في الواقع الصف الثاني كاملاً. لتفادي هذه الارتباكات، يجب ترسيخ القاعدة الذهبية الدائمة: الموقع قبل الفاصلة للصفوف، والموقع بعد الفاصلة للأعمدة [Rows, Columns].
12.2 مشكلات تطابق الأشكال ومحاذاة الأبعاد في البث (Broadcasting Errors)
تنشأ الأخطاء الرياضية الأكثر تعقيداً عند محاولة إجراء عمليات حسابية ثنائية (مثل الطرح أو القسمة القياسية) بين مصفوفة أصلية وعمود مستخرج تم تسطيحه كمتجه أحادي البعد (N,) بدلاً من متجه عمودي (N, 1). عند محاولة تنفيذ عملية مثل data - data[:, 0] بهدف طرح العمود الأول من كافة أعمدة المصفوفة، تطلق NumPy استثناء البث الشهير:
ValueError: operands could not be broadcast together with shapes (M, N) (M,)
يحدث هذا الخطأ لأن قواعد البث (Broadcasting Rules) تبدأ بمحاذاة الأبعاد من اليمين إلى اليسار؛ حيث يتم محاذاة البعد M للمتجه مع البعد N للمصفوفة بدلاً من البعد M للصفوف. والحل الهندسي الصحيح هو الحفاظ الصريح على البعد العمودي عبر استخدام np.newaxis بالصيغة: data - data[:, 0, np.newaxis] أو data - data[:, [0]]، مما يسمح بمحاذاة الأبعاد (M, N) مع (M, 1) وتوسيع العمود أفقياً عبر كافة الأعمدة بنجاح تام.
12.3 أفضل الممارسات البرمجية لكتابة كود فهرسة متين وقابل للصيانة
لضمان استقرار المشاريع البرمجية وقابليتها للصيانة والتطوير، يُوصى باتباع مجموعة من الممارسات الهندسية الصارمة عند التعامل مع استخراج الأعمدة:
- تجنب الأرقام السحرية (Magic Numbers): لا تقم بتضمين فهارس الأعمدة الثابتة كأرقام صامتة داخل الشيفرة مثل
data[:, 4]، بل قم بتعريف متغيرات دلالية ثابتة مثلTARGET_COL_IDX = 4ثم استدعاءdata[:, TARGET_COL_IDX]لزيادة وضوح الكود وسهولة تعديله مستقبلاً. - التوكيدات البرمجية (Defensive Assertions): استخدم جمل التوكيد للتحقق من أبعاد وصحة البيانات قبل الاستخراج، مثل التأكد من أن
assert data.ndim == 2, "Array must be 2D"لضمان عدم تمرير مصفوفات مشوهة للمنظومة. - التوثيق النوعي (Type Hints): وظف مكتبة
numpy.typingلتوثيق الدوال التي تستقبل وتُرجع مصفوفات وأعمدة محددة، مثل تحديد الأنواع المتوقعةNDArray[np.float64]، مما يسهل عمليات الفحص الساكن للأخطاء عبر أدوات مثلmypy.
خاتمة واستنتاجات هندسية شاملة
يمثل استخراج الأعمدة في مكتبة NumPy حجر الزاوية الذي تنبني عليه كافة ممارسات معالجة وتحليل البيانات الحديثة في بايثون. ومن خلال ما تم استعراضه عبر هذا الدليل، يتضح أن المسألة تتجاوز مجرد استخدام الأقواس المعقوفة والنقاط الرأسية إلى ضرورة امتلاك رؤية شاملة لكيفية إدارة الذاكرة، والتفريق الواعي بين الرؤى والنسخ، واختيار الأبعاد المناسبة وفق المتطلبات الجبرية لخطوط الإنتاج والخوارزميات.
يتيح إتقان هذه التقنيات—بدءاً من التقطيع البسيط والفهرسة المنطقية إلى الفهرسة المتقدمة وتحسين ترتيب الذاكرة عبر Fortran-order—كتابة برمجيات فائقة الكفاءة قادرة على معالجة ملايين السجلات في أجزاء من الثانية دون استهلاك غير مبرر لموارد النظام. إن الاستثمار في فهم هذه الآليات الهندسية الدقيقة هو ما يصنع الفارق الحقيقي بين المطور التقليدي ومهندس البيانات المحترف القادر على تطويع خوارزميات الحوسبة العلمية بأقصى كفاءة ممكنة.
References
- Harris, C. R., Millman, K. J., van der Walt, S. J., Gommers, R., Virtanen, P., Cournapeau, D., … & Oliphant, T. E. (2020). Array programming with NumPy. Nature, 585(7825), 357-362. https://doi.org/10.1038/s41586-020-2649-2
- NumPy Developers. (2023). NumPy user guide and multidimensional array indexing reference. NumPy Documentation. https://numpy.org/doc/stable/user/basics.indexing.html
- van der Walt, S., Colbert, S. C., & Varoquaux, G. (2011). The NumPy array: a structure for efficient numerical computation. Computing in Science & Engineering, 13(2), 22-30. https://doi.org/10.1109/MCSE.2011.37
- McKinney, W. (2022). Python for data analysis: Data wrangling with pandas, NumPy, and Jupyter (3rd ed.). O’Reilly Media. https://wesmckinney.com/book/
- VanderPlas, J. (2016). Python data science handbook: Essential tools for working with data. O’Reilly Media. https://jakevdp.github.io/PythonDataScienceHandbook/
- Pedregosa, F., Varoquaux, G., Gramfort, A., Michel, V., Thirion, B., Grisel, O., … & Duchesnay, É. (2011). Scikit-learn: Machine learning in Python. Journal of Machine Learning Research, 12, 2825-2830. https://scikit-learn.org/