6.10. البث (Broadcasting)¶
عندما يحصل عامل ثنائي على مصفوفتين لا يتطابق شكلاهما تمامًا، فإن numpy لا يطلق خطأ -- بل يبثّ. والبث مجموعة صغيرة من القواعد التي تقرر ما إذا كان شكلان متوافقين، وإن كانا كذلك، كيف يُمدَّد الأصغر افتراضيًا ليطابق الأكبر.
6.10.1. القواعد¶
عندما يكون للمعاملين شكلان A و B، تعالجهما numpy على خطوتين.
مطابقة الرتب. إذا كان لأحد المعاملين عدد محاور أقل من الآخر، فإن
numpyتحشو مقدمة شكله افتراضيًا بمحاور حجمها 1 حتى يصبح للشكلين العدد نفسه من المحاور. فمعامل أحادي البعد شكله(3,)مقترن بمعامل ثنائي البعد شكله(2, 3)يصبح(1, 3)مقابل(2, 3).فحص كل محور. بالسير عبر الشكلين المتساويين الطول الآن محورًا بمحور، يجب أن يحقق كل زوج من الأحجام أحد شرطين: إما أن يتساوى الحجمان، أو يكون أحدهما 1. والمحور ذو الحجم 1 يُمدَّد افتراضيًا إلى حجم الطرف الآخر لأجل العملية. فالزوج
(1, 3)مقابل(2, 3)متوافق لأن المحور الأول فيه 1 (يُمدَّد إلى 2) والمحور الثاني متطابق (3 == 3)؛ والنتيجة لها الشكل(2, 3).
إذا لم يحقق أي زوج من المحاور أيًّا من الشرطين، يكون الشكلان غير متوافقين ويطلق العامل ValueError.
6.10.2. أمثلة¶
عدد قياسي مقابل أي مصفوفة. يتصرف العدد القياسي كأنه شكل (1,) ويُمدَّد إلى أي شيء:
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10 # (2, 3) + scalar -> (2, 3)
متجه أحادي البعد عبر مصفوفة ثنائية البعد. القاعدة 1 تضيف محورًا بحجم 1 في المقدمة لتجعل (3,) تصبح (1, 3)؛ ثم تمدّد القاعدة 2 ذلك الصف نزولًا في كل عمود من a
row = np.array([100, 200, 300], dtype=np.float)
a + row # (2, 3) + (3,) -> (2, 3)
مصفوفتان أحاديتا البعد متساويتا الطول تُجمعان عنصرًا بعنصر -- دون حاجة إلى بث:
np.arange(4) + np.arange(4)
متجه عمودي مقابل متجه صفّي ينتج شكلًا "خارجيًا" ثنائي البعد: فالشكل (4, 1) المقترن بـ (3,) يصبح (4, 1) مقابل (1, 3) بعد إضافة محور الرتبة في المقدمة، وتمدّد القاعدة 2 كل معامل على امتداد محوره ذي الحجم 1:
x = np.array([1, 2, 3, 4]).reshape((4, 1)) # column
y = np.array([10, 20, 30]) # row
x + y # (4, 3) matrix
تنطبق قواعد الشكل نفسها على أي دالة عامة (ufunc) ثنائية الوسائط، بما في ذلك arctan2()
np.arctan2(y, 1.0)
np.arctan2(y, x)
6.10.3. ما الذي لا يخصصه البث¶
التمديد افتراضي. فـ numpy تسير عبر المعاملين معًا، معيدةً قراءة الأصغر على امتداد محور بثّه بدلًا من نسخه. ولا تُكرَّر بيانات المصفوفة الأقصر أبدًا في الذاكرة.
حجم مصفوفة المخرجات هو ما يهم بالنسبة للذاكرة. فـ a + row تخصص مخرجًا بشكل a، لا بشكل a مضافًا إليه شكل row. ومع ذلك، يمكن أن تنتج سلاسل البث الطويلة وسطيات كبيرة.
6.10.4. عندما يخطئ البث¶
الإخفاق الكلاسيكي هو شكلان ليس لأيٍّ منهما محور بحجم 1 ليُمدَّد والأحجام مختلفة -- (3, 4) مقابل (4, 3)، على سبيل المثال. فالقاعدة 2 لا تستطيع مطابقة 3 مع 4، لذا تطلق numpy الاستثناء ValueError.
وثمة مشكلة أدقّ هي بثّ ينجح، لكن ليس بالطريقة التي قصدها التطبيق. والمثال النموذجي هو (5,) مقابل (5, 1): فإضافة الرتبة في المقدمة تحوّل (5,) إلى (1, 5)، التي تُبثّ مقابل (5, 1) لتنتج مصفوفة (5, 5) -- وهي التركيب الخارجي للمتجهين، لا النتيجة العنصرية بطول 5 التي أرادها التطبيق على الأرجح. وعند الشك، اطبع shape على الجانبين وتدرّج عبر القواعد قبل اللجوء إلى reshape() أو transpose().