انتقل إلى المحتوى الرئيسي

الوحدة 4 — NumPy: المصفوفات والبثّ والحساب المتّجه

NumPy أساس المنظومة كلّها: pandas وscikit-learn وحتى موتّرات PyTorch تستعير مفاهيمه. كائنه الوحيد — المصفوفة المتجانسة ndarray — ومبدؤه الوحيد — وجِّه بدل أن تكرّر — يكفيان لتسريع الحسابات من 10 إلى 100 مرّة.

المصفوفة ndarray: كتلة متجانسة منمّطة

بخلاف قائمة بايثون (مراجع إلى كائنات مبعثرة)، مصفوفة NumPy كتلة ذاكرة متّصلة من عناصر من النوع نفسه. هذا التجانس هو ما يشتري السرعة: تنزل العمليات إلى C المُجمَّعة بدل تفسير كلّ عنصر.

import numpy as np

a = np.array([1.5, 2.0, 3.5])
m = np.zeros((3, 4)) # مصفوفة 3×4 من الأصفار
x = np.arange(0, 10, 0.5) # 0, 0.5, 1.0, …, 9.5
g = np.random.default_rng(42).normal(size=(1000, 3)) # عشوائية قابلة للتكرار

a.shape # (3,) — الأبعاد
a.dtype # float64 — نوع العناصر الموحّد
m.ndim # 2 — عدد المحاور

shape وdtype هما أول ما يُفحَص أمام أيّ سلوك غريب: نصف أخطاء NumPy أشكالٌ غير متوقّعة.

التوجيه: العملية تنطبق على المصفوفة كلّها

# أسلوب بايثون الخالص — بطيء
results = []
for price in price_list:
results.append(price * 1.15)

# أسلوب NumPy — أسرع 10 إلى 100 مرّة، وأوضح
results = prices * 1.15

كلّ العمليات الحسابية والدوالّ الشاملة (np.log وnp.exp وnp.sqrt…) تنطبق عنصراً عنصراً على المصفوفة كاملة. اشتمال القائمة من الوحدة 2 يصبح تعبيراً بلا حلقة:

standardized = (x - x.mean()) / x.std()      # التقييس في سطر واحد

القاعدة المهنية: إن وجدت نفسك تكتب حلقة for على مصفوفة NumPy فتوقّف — توجد على الأرجح صياغة موجَّهة، أسرع وأقرب إلى الرياضيات.

البثّ (broadcasting): العمل بين شكلين مختلفين

يمدّد NumPy تلقائياً المصفوفات ذات الأشكال المتوافقة:

m = np.array([[1., 2., 3.],
[4., 5., 6.]]) # شكل (2, 3)

m * 10 # عدد مفرد يُبثّ في كلّ مكان
m - m.mean(axis=0) # يطرح متوسّط كلّ عمود — شكل (3,) يُبثّ على (2, 3)

القاعدة الرسمية: بُعدان متوافقان إذا كانا متساويين أو أحدهما يساوي 1، بمحاذاة الأشكال من اليمين. عملياً، الحالة المهمّة هي أعلاه: تمركز الأعمدة أو تحجيمها بلا حلقة. وهذه بالضبط خطوة التقييس التي تسبق معظم النماذج.

حين يخطئ البثّ في صمت

طرح مصفوفة شكلها (n,) من مصفوفة شكلها (n, 1) ينتج مصفوفة (n, n) — بلا أيّ خطأ. إن أعاد حسابٌ شكلاً عبثياً فابحث عن بثّ غير مقصود؛ x.reshape(-1, 1) وx.ravel() يصحّحان الأشكال.

الأقنعة المنطقية: الترشيح بشرط

أكثر تراكيب NumPy استخداماً — والآلية الحرفية وراء مرشّحات pandas في الوحدة التالية:

ages = np.array([22, 35, 58, 41, 17, 63])

mask = ages >= 40 # array([False, False, True, True, False, True])
ages[mask] # array([58, 41, 63]) — انتقاء
(ages >= 40).mean() # 0.5 — النسبة (True تُحسَب 1)

# شروط مركّبة: & (و)، | (أو)، والأقواس إلزامية
active_seniors = ages[(ages >= 40) & (ages < 65)]

# استبدال شرطي
capped = np.where(ages > 60, 60, ages)

الأقواس حول كلّ شرط ليست اختيارية: أولوية & و| أعلى من المقارنات، ونسيانها ينتج خطأً غامضاً.

التجميعات والمحاور: التلخيص في الاتجاه الصحيح

grades = np.array([[12, 15, 9],
[14, 11, 16]]) # طالبان × 3 موادّ

grades.mean() # 12.83 — المتوسّط العامّ
grades.mean(axis=0) # [13. 13. 12.5] — لكلّ مادّة (تنهار الصفوف)
grades.mean(axis=1) # [12. 13.67] — لكلّ طالب (تنهار الأعمدة)

القاعدة الذهنية الراسخة: axis يسمّي المحور الذي يختفي. axis=0 يطوي الصفوف فتبقى قيمة لكلّ عمود. التجميعات نفسها في كلّ مكان: sum وmin وmax وstd وargmax (مؤشّر الأقصى — الذي يعطي الفئة المتنبَّأ بها في شبكة عصبية).

دقيقتان من العالم الحقيقي: القيم الناقصة تنتشر (np.nan يلوّث أيّ مجموع — استخدم np.nanmean وعائلتها)، وتقطيعات NumPy عروض (views) على الذاكرة نفسها لا نسخ (.copy() للفصل).

الخلاصة

  • المصفوفة ndarray متجانسة منمّطة؛ shape وdtype أول عادات التشخيص.
  • التوجيه يستبدل الحلقات: عمليات عنصراً بعنصر، أسرع 10 إلى 100 مرّة، وأوضح.
  • البثّ يتيح العمل بين أشكال متوافقة — تمركز الأعمدة في سطر — لكنه قد يصنع أشكالاً عبثية في صمت.
  • الأقنعة المنطقية: ترشيح وعدّ واستبدال بشرط؛ الأقواس إلزامية مع & و|.
  • axis = المحور الذي يختفي؛ np.nanmean أمام النواقص؛ التقطيعات عروض.

الوحدة التالية: pandas، التي تضع تسميات — أسماء أعمدة وفهرساً — على هذه المصفوفات وتصبح أداة العمل اليومية على البيانات الجدولية.