وصلنا الآن إلى نهاية سلسلة مقالات NumPy، ونأمل أن تكون أدركت أهمية مكتبة NumPy وأنها مكتبة قوية ومتعددة الاستخدامات. وبنفس الوقت تذكر أن لغة بايثون هي لغة قوية جدًا، وقد تكون في بعض الحالات المحددة أقوى من NumPy.
لنفكر على سبيل المثال، في تمرين مثير للاهتمام على النحو التالي:
اقتباساكتب الشيفرة المختصرة لحساب جميع التخصيصات "القانونية" لأربعة أرصدة stocks بحيث تكون التخصيصات في كتلة واحدة 1.0، ومجموع التخصيصات هو 10.0.
جُمعت إجابات مختلفة من المجتمع، وأسفرت الحلول المقترحة عن نتائج مفاجئة. لكن لنبدأ بكتابة التعليمات البرمجية بلغة بايثون لحل هذا التمرين:
def solution_1(): # Brute force # 14641 (=11*11*11*11) iterations & tests Z = [] for i in range(11): for j in range(11): for k in range(11): for l in range(11): if i+j+k+l == 10: Z.append((i,j,k,l)) return Z
هذا الحل هو الأبطأ لأنه يتطلب 4 حلقات، كما أنه يختبر جميع المجموعات المختلفة والبالغ عددها 11641 والمكونة من 4 أعداد صحيحة بين 0 و 10 للاحتفاظ فقط بالمجموعات التي يكون مجموعها مساوٍ إلى 10. يمكننا طبعًا التخلص من الحلقات باستخدام أداة itertools، لكن التعليمات تظل بطيئة:
import itertools as it def solution_2(): # Itertools # 14641 (=11*11*11*11) iterations & tests return [(i,j,k,l) for i,j,k,l in it.product(range(11),repeat=4) if i+j+k+l == 10]
كان أحد أفضل الحلول المقترحة يستفيد من إمكانية الحصول على حلقات مخبأة ذكية تتيح لنا بناء كل صف tuple مباشرةً، دون أي اختبار كما هو موضح أدناه:
def solution_3(): return [(a, b, c, (10 - a - b - c)) for a in range(11) for b in range(11 - a) for c in range(11 - a - b)]
يستخدم كاتب هذا التمرين أفضل حل بالاعتماد على NumPy استراتيجية مختلفة مع مجموعة مقيدة من الاختبارات:
def solution_4(): X123 = np.indices((11,11,11)).reshape(3,11*11*11) X4 = 10 - X123.sum(axis=0) return np.vstack((X123, X4)).T[X4 > -1]
إذا قيّمنا مدة تنفيذ هذه التوابع نحصل على:
>>> timeit("solution_1()", globals()) 100 loops, best of 3: 1.9 msec per loop >>> timeit("solution_2()", globals()) 100 loops, best of 3: 1.67 msec per loop >>> timeit("solution_3()", globals()) 1000 loops, best of 3: 60.4 usec per loop >>> timeit("solution_4()", globals()) 1000 loops, best of 3: 54.4 usec per loop
كما تلاحظ حل Numpy هو الأسرع ولكن الحل باستخدام بايثون قابل للمقارنة. سنحاول الآن إضافة تعديل صغير على حل بايثون:
def solution_3_bis(): return ((a, b, c, (10 - a - b - c)) for a in range(11) for b in range(11 - a) for c in range(11 - a - b))
وسنحصل على النتيجة:
>>> timeit("solution_3_bis()", globals()) 10000 loops, best of 3: 0.643 usec per loop
اكتسبنا هنا عاملًا قدره 100 فقط عن طريق استبدال قوسين مربعين بأقواس عادية. كيف يعقل ذلك؟ يمكن العثور على التفسير من خلال النظر في نوع الكائن المُعاد:
>>> print(type(solution_3())) <class 'list'> >>> print(type(solution_3_bis())) <class 'generator'>
يعيد التابع ()solution_3_bis
مولدًا generator يمكن استخدامه لإنشاء القائمة الكاملة أو للتكرار على جميع العناصر المختلفة. على أي حال، تأتي عملية التسريع الضخمة من عدم الاستقرار في القائمة الكاملة، وبالتالي من المهم أن نتساءل عما إذا كنت بحاجة إلى مثيل فعلي لنتيجتك أو إذا كان المولد البسيط ينفذ المهمة.
مكتبة Numpy وأخواتها
هناك العديد من حزم بايثون الأخرى إلى جانب Numpy، والتي تستحق النظر لأنها تتناول أصناف مشابهة ولكن مختلفة من المشكلات باستخدام التقنيات المختلفة (التجميع، الجهاز الافتراضي، وحدة معالجة الرسوميات GPU، الضغط، إلخ). قد تكون حزمة واحدة أفضل من الأخرى اعتمادًا على مشكلتك المحددة وأجهزتك الخاصة. دعونا نوضح استخدامهم من خلال مثال بسيط جدًا، إذ نريد حساب تعبير بناءً على متجهين عددين float:
import numpy as np a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = 2*a + 3*b
حزمة NumExpr
توفر حزمة numexpr إجراءات للتقييم السريع لتعبيرات المصفوفة من ناحية العناصر باستخدام جهاز افتراضي يعتمد على المتجهات vector-based، وهي مشابهة لحزمة SciPy، لكنها لا تتطلب خطوة ترجمة منفصلة لشيفرات C أو ++C.
import numpy as np import numexpr as ne a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = ne.evaluate("2*a + 3*b")
مصرف Cython
Cython هو مصرّف ثابت محسن لكل من لغة برمجة بايثون ولغة كايثون Cython الموسعة (استنادًا إلى Pyrex). يجعل مصرّف كايثون كتابة امتدادات C لبايثون سهلة مثل بايثون نفسها.
import numpy as np def evaluate(np.ndarray a, np.ndarray b): cdef int i cdef np.ndarray c = np.zeros_like(a) for i in range(a.size): c[i] = 2*a[i] + 3*b[i] return c a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = evaluate(a, b)
مصرف Numba
يمنحك مصرّف Numba القدرة على تسريع تطبيقاتك من خلال وظائف عالية الأداء مكتوبة مباشرةً بلغة بايثون. مع بعض التعليقات التوضيحية يمكن تجميع تعليمات بايثون array-oriented و math-heavy في الوقت المناسب لتعليمات الآلة الأصلية، على غرار C و ++C وفورتران Fortran، دون الحاجة إلى تبديل اللغات أو مترجم بايثون.
from numba import jit import numpy as np @jit def evaluate(np.ndarray a, np.ndarray b): c = np.zeros_like(a) for i in range(a.size): c[i] = 2*a[i] + 3*b[i] return c a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = evaluate(a, b)
مكتبة Theano
مكتبة Theano هي مكتبة بايثون تتيح لك تحديد التعبيرات الرياضية التي تتضمن مصفوفات متعددة الأبعاد وتحسينها وتقييمها بكفاءة. تتميز Theano بالتكامل الوثيق مع الاستخدام غير المستقر والشفاف لوحدة معالجة الرسوميات GPU ، والتمايز الرمزي symbolic differentiation الفعال، وتحسينات السرعة والثبات، وإنشاء شيفرة C الديناميكية، واختبار الوحدة الشامل والتحقق الذاتي.
import numpy as np import theano.tensor as T x = T.fvector('x') y = T.fvector('y') z = 2*x + 3*y f = function([x, y], z) a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = f(a, b)
PyCUDA
يتيح لك PyCUDA الوصول إلى واجهة برمجة تطبيقات الحساب المتوازي CUDA الخاصة بشركة Nvidia من بايثون.
import numpy as np import pycuda.autoinit import pycuda.driver as drv from pycuda.compiler import SourceModule mod = SourceModule(""" __global__ void evaluate(float *c, float *a, float *b) { const int i = threadIdx.x; c[i] = 2*a[i] + 3*b[i]; } """) evaluate = mod.get_function("evaluate") a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = np.zeros_like(a) evaluate(drv.Out(c), drv.In(a), drv.In(b), block=(400,1,1), grid=(1,1))
مكتبة PyOpenCL
تتيح لك PyOpenCL الوصول إلى وحدات معالجة الرسومات وأجهزة الحوسبة المتوازية الأخرى من بايثون.
import numpy as np import pyopencl as cl a = np.random.uniform(0, 1, 1000).astype(np.float32) b = np.random.uniform(0, 1, 1000).astype(np.float32) c = np.empty_like(a) ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) mf = cl.mem_flags gpu_a = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=a) gpu_b = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=b) evaluate = cl.Program(ctx, """ __kernel void evaluate(__global const float *gpu_a; __global const float *gpu_b; __global float *gpu_c) { int gid = get_global_id(0); gpu_c[gid] = 2*gpu_a[gid] + 3*gpu_b[gid]; } """).build() gpu_c = cl.Buffer(ctx, mf.WRITE_ONLY, a.nbytes) evaluate.evaluate(queue, a.shape, None, gpu_a, gpu_b, gpu_c) cl.enqueue_copy(queue, c, gpu_c)
Scipy وأخواتها
إذا كان هناك عدة حزم إضافية لمكتبة Numpy، فهناك الملايين من الحزم الإضافية لـ scipy؛ إذ ربما يكون لكل مجال من مجالات العلوم مجموعته الخاصة، ومعظم الأمثلة التي كنا ندرسها حتى الآن كان من الممكن حلها في استدعائين أو ثلاثة استدعاءات باستخدام الحزمة ذات الصلة. لكن طبعًا لم يكن هذا هو الهدف. تعد برمجة الوظائف بنفسك عمومًا تمرينًا جيدًا إذا كان لديك بعض وقت الفراغ، وتتمثل أكبر صعوبة في هذه المرحلة في العثور على هذه الحزم ذات الصلة. فيما يلي قائمة قصيرة جدًا من الحزم التي جرى صيانتها واختبارها جيدًا والتي قد تبسط حياتك العلمية (اعتمادًا على مجالك). هناك طبعًا الكثير من الخيارات وذلك حسب احتياجاتك الخاصة، ومن المحتمل ألا تكون مضطرًا إلى برمجة كل شيء بنفسك.
مكتبة scikit-learn
scikit-learn هي مكتبة تعلم آلي مجانية للغة برمجة بايثون، وتتميز بخوارزميات تصنيف وانحدار وتجميع مختلفة بما في ذلك دعم خوارزمية أجهزة المتجهات vector machines، والغابات العشوائية random forests وتعزيز التدرج gradient boosting و k-means و DBSCAN، وهذه الحزمة مصممة للتفاعل مع مكتبات بايثون الرقمية والعلمية numpy و SciPy.
مكتبة scikit-image
scikit-image عبارة عن حزمة بايثون مخصصة لمعالجة الصور، واستخدام المصفوفات المتداخلة مثل كائنات صور. يصف هذا المقال كيفية استخدام scikit-image في مهام معالجة الصور المختلفة، ويركز على الارتباط بوحدات بايثون النمطية العلمية الأخرى مثل numpy و SciPy.
مكتبة SymPy
SymPy هي مكتبة بايثون للرياضيات الرمزية، وتهدف إلى أن تصبح كل ميزات النظام الحاسوبي جبرية -أو اختصارًا CAS- مع الحفاظ على الشيفرة بسيطة قدر الإمكان حتى تكون مفهومة وقابلةً للتوسّع بسهولة. كُتبت كامل مكتبة SymPy بلغة بايثون.
حزمة Astropy
مشروع Astropy هو جهد مجتمعي لتطوير حزمة أساسية واحدة لعلم الفلك astronomy في بايثون وتعزيز إمكانية التشغيل البيني بين حزم علم الفلك في بايثون.
حزمة Cartopy
Cartopy هي حزمة بايثون مصممة لتسهيل رسم الخرائط لتحليل البيانات والتصوير. تستفيد Cartopy من المكتبات القوية PROJ.4 والمكتوبة والمحددة الشكل ولها واجهة رسم بسيطة وبديهية لحزمة matplotlib لإنشاء خرائط جاهزة النشر.
محاكي Brian
Brian هو محاكي مجاني ومفتوح المصدر للشبكات العصبية، مكتوب بلغة البرمجة بايثون وهو متاح على جميع الأنظمة الأساسية تقريبًا. يساعد المحاكي في توفير وقت المعالجات إضافةً إلى توفير وقت الباحثين. صُمم محاكي Brian ليكون سهل التعلم والاستخدام ومرن جدًا وقابلًا للتوسيع بسهولة.
مكتبة Glumpy
Glumpy هي مكتبة تصور تفاعلي مبنية على OpenGL في بايثون، وهدفها هو تسهيل إنشاء تمثيلات مرئية سريعة وقابلة للتطوير وجميلة وتفاعلية وديناميكية.
الخلاصة
تُعد Numpy مكتبةً متعددة الاستخدامات، ولكن هذا لا يعني أنه ينبغي عليك استخدامها في كل الحالات. عرضنا في هذا الدليل بعض البدائل (بما في ذلك تعليمات بايثون العادية) وهي تستحق البحث والتعب، ولكن الاختيار عائدٌ لك، إذ عليك أن تفكر بالحل الأفضل بالنسبة لك من حيث وقت التطوير ووقت الحساب والجهد الذي ستحتاجه للتعديل. في حال قررت تصميم حلك الخاص فيجب عليك اختباره والمحافظة عليه، ولكن في المقابل ستكون حرًا في تصميمه بالطريقة التي تريدها. من ناحية أخرى: إذا قررت الاعتماد على حزمة طرف ثالث، فستوفر الكثير من وقت التطوير ولكن ستضطر إلى تكييف الحزمة مع احتياجاتك الخاصة.
ترجمة -وبتصرّف- للفصل Beyond NumPy من كتاب From Python to Numpy لصاحبه Nicolas P. Rougier.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.