6.9. الدوال العامة

إن الدالة العامة (ufunc) هي دالة رياضية تُطبَّق على كل عنصر من عناصر مصفوفة في استدعاء واحد. والعوامل الحسابية في الصفحة السابقة ما هي إلا دوال عامة بصيغة عوامل؛ وهذه الصفحة هي فهرس الدوال المُسمّاة التي تغطي المثلثات والأسس / اللوغاريتمات والتقريب وبعض الدوال الأخرى.

تقبل كل ufunc قيمة عددية أو عنصرًا قابلًا للتكرار في Python أو ndarray، وتُرجع إما عددًا عشريًا مفردًا (عندما يكون المدخل عدديًا) أو ndarray عشرية:

from ulab import numpy as np

np.exp(2.0)                    # 7.389...
np.sin(range(4))               # 1-D float ndarray
np.sqrt([1, 4, 9, 16])         # array([1.0, 2.0, 3.0, 4.0])

a = np.arange(9).reshape((3, 3))
np.exp(a)                      # 3x3 float ndarray

6.9.1. الفهرس

تكشف numpy عن الدوال الرياضية التي يلجأ إليها التطبيق المضمَّن غالبًا:

تعالج كل دالة المصفوفة بأكملها في استدعاء واحد للمكتبة. والتسريع مقارنةً باستيعاب قائمة Python يستدعي math.sin() عنصرًا بعنصر يبلغ 10-30 ضعفًا على مخزن مؤقت نموذجي.

6.9.2. الكلمة المفتاحية out=

يخصص كل استدعاء ufunc عادةً مصفوفة نتائج جديدة لاحتواء مخرجاته. وفي حلقة تعمل عدة مرات في الثانية، تتراكم تلك التخصيصات وتهدر RAM. أما تمرير out= -- وهي مصفوفة عشرية موجودة مسبقًا بالشكل ذاته للمدخلات -- فيكتب النتيجة في تلك المصفوفة بدلًا من تخصيص واحدة جديدة:

x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)

while True:
    np.sin(x, out=y)
    # use y ...

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

6.9.3. الدوال العامة ذات المعاملَين

إن arctan2() هي الدالة العامة الوحيدة الحقيقية ذات المعاملَين في القائمة أعلاه -- إذ تُرجع ظل التمام العكسي المدرك للربع لـ y / x وتبثّ المعاملَين:

y = np.array([1, 2.2, 33.33, 444.444])
np.arctan2(y, 1.0)             # against a scalar
np.arctan2(1.0, y)             # the other way
np.arctan2(y, y)               # against another array

6.9.4. تأليف الدوال العامة

تتألف الدوال العامة مثل أي تعبير مصفوفة آخر. وفيما يلي بعض الأنماط التي تظهر على الكاميرا:

تصحيح غاما (في فضاء الأعداد العشرية)

gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma

مُنعِّم بسيط لتمرير الترددات المنخفضة (اقتراب alpha من 1.0 يعني تحديثًا بطيئًا):

alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample

السيجمويد

sigmoid = 1.0 / (1.0 + np.exp(-x))

طيف القدرة بوحدة dB

spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)

6.9.5. np.vectorize

يمكن ترقية دالة Python عادية إلى دالة على هيئة ufunc بواسطة vectorize(). والقابل للاستدعاء الناتج يقبل القيم العددية أو العناصر القابلة للتكرار أو قيم ndarray

def f(x):
    return x * x

vf = np.vectorize(f)

vf(44.0)                          # array([1936.0])
vf(np.array([1, 2, 3, 4]))        # array([1.0, 4.0, 9.0, 16.0])
vf([2, 3, 4])                     # array([4.0, 9.0, 16.0])

افتراضيًا يكون نوع بيانات النتيجة float. وتتجاوز otypes= ذلك:

vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)

يجب أن تأخذ دالة Python معاملًا واحدًا وأن تُرجع عددًا واحدًا.

إن vectorize() نحوية في معظمها -- إذ لا يزال على دالة Python المغلَّفة أن تعمل مرة واحدة لكل عنصر، لذا تعود معظم تكلفة المفسّر لكل عنصر التي تتجنبها ufunc الحقيقية. توقّع تسريعًا متواضعًا بنسبة 30%-50% مقارنةً باستيعاب القائمة، وليس الـ 30 ضعفًا لدالة عامة حقيقية. وهي الأداة المناسبة عندما يتعين على دالة واحدة أن تعمل على القيم العددية والقوائم و المصفوفات تحت الاسم نفسه -- وليست كذلك عندما تكون السرعة الخام هي الهدف.

للاطلاع على التوقيعات الكاملة لكل دالة عامة مدرجة أعلاه، راجع numpy --- عمليات المصفوفات المتوافقة مع numpy.