ما هي NumPy ولماذا هي مهمة؟

ما هي NumPy ولماذا هي مهمة؟

المقدمة

في عالم علوم البيانات والحوسبة العلمية، اكتسبت لغة Python سمعة كواحدة من أكثر لغات البرمجة تنوعًا وقوة. ويرجع ذلك بشكل أساسي إلى مكتبتها الواسعة، مع كون مكتبة NumPy واحدة من الأعمدة الأساسية. NumPy هي مكتبة مفتوحة المصدر مصممة للعمليات العددية، مما يتيح أداء عاليًا على المصفوفات الكبيرة متعددة الأبعاد، إلى جانب مجموعة من العمليات الرياضية للتعامل مع هذه المصفوفات.

تُعد معالجة NumPy الفعالة للبيانات ونطاقها الواسع من العمليات أمرًا لا غنى عنه لتحليل البيانات وتعلم الآلة وأي مشاريع تعتمد على الحسابات الثقيلة. سواء كنت تحلل مجموعات بيانات ضخمة أو تقوم بعمليات مصفوفات معقدة، فإن NumPy تقدم مجموعة أدوات قوية وفعالة.

لماذا يتم استخدام NumPy على نطاق واسع؟

  • الأداء: تم بناء NumPy باستخدام C وFortran، مما يجعل العمليات أسرع بكثير من استخدام قوائم Python الصافية.
  • سهولة الاستخدام: الصياغة بسيطة وتتوافق بسهولة مع الأدوات الأخرى للحوسبة العلمية.
  • المجتمع والنظام البيئي: تشكل NumPy الأساس للعديد من المكتبات الأخرى التي تركز على البيانات مثل pandas وSciPy وscikit-learn.

ما هي NumPy؟

NumPy هي مكتبة قوية للحوسبة العددية في Python توفر دعمًا للمصفوفات والمصفوفات الرياضية ومجموعة من العمليات الرياضية. تم إنشاؤها لمعالجة قيود بنية قائمة Python المدمجة عند تنفيذ العمليات العددية، وتقديم نوع مصفوفة محسّن للغاية يُسمى ndarray.

تاريخ موجز لـ NumPy

تعود جذور NumPy إلى عام 1995، حيث بدأت كمكتبة تُسمى Numeric. لاحقًا، تم تحسينها بمكتبة أخرى تُسمى Numarray، والتي كانت تهدف إلى التعامل بشكل أكثر كفاءة مع المصفوفات العددية الكبيرة. في عام 2005، قام Travis Oliphant بدمج المشروعين لتشكيل ما نعرفه الآن باسم NumPy، مع تعزيز وظائفها وتأسيسها كركيزة في الحوسبة العلمية.

لماذا نختار NumPy بدلاً من قوائم Python العادية؟

في حين أن قوائم Python متعددة الاستخدامات ومريحة للاستخدام العام، إلا أنها ليست مثالية للعمليات العددية. إليك السبب الذي يجعل NumPy مفضلة:

  • كفاءة الذاكرة: يتم تخزين مصفوفات NumPy بشكل أكثر ضغطًا من قوائم بايثون، مما يسمح باستخدام أقل للذاكرة.
  • السرعة: العمليات باستخدام مصفوفات NumPy أسرع بكثير من العمليات المكافئة باستخدام قوائم بايثون بفضل الكود الأمثل المكتوب بلغة C.
  • القدرات المتقدمة: تدعم NumPy المصفوفات متعددة الأبعاد، والعمليات العنصرية، والبث، وهي ضرورية للمهام الرياضية والعلمية المعقدة.

مثال مقارنة:

افترض أنك تريد جمع قائمتين من الأرقام:

باستخدام قوائم بايثون:


a = [1, 2, 3, 4]
b = [5, 6, 7, 8]
c = [a[i] + b[i] for i in range(len(a))]  # يتطلب حلقة
    

باستخدام NumPy:


import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
c = a + b  # عملية متجهية
    

مع NumPy، يكون نفس الجمع أكثر اختصارًا، وأكثر قابلية للقراءة، وأسرع بفضل العمليات المتجهية التي تتجنب الحلقات في بايثون وتستخدم كود C مترجمًا للحصول على أداء مثالي.

الميزات الرئيسية لـ NumPy

تستمد شعبية NumPy من قدراتها القوية في التعامل مع المصفوفات ونطاقها الواسع من العمليات. فيما يلي بعض الميزات الرئيسية لها:

  • Ndarray (المصفوفة متعددة الأبعاد): في قلب NumPy يوجد ndarray، وهي حاوية سريعة ومرنة لمجموعات البيانات الكبيرة في بايثون. على عكس قوائم بايثون، يمكن أن تكون ndarrays متعددة الأبعاد، مما يمكّن المستخدمين من التعامل مع المصفوفات والأوتار بكفاءة.
  • البث: تتيح هذه الميزة إجراء العمليات الحسابية على المصفوفات ذات الأشكال والأحجام المختلفة دون الحاجة إلى إعادة تشكيل أو تكرار البيانات. على سبيل المثال، يعد إضافة مصفوفة أحادية البعد إلى كل صف من مصفوفة ثنائية الأبعاد أمرًا سلسًا باستخدام البث.
  • التسريع: تم تصميم NumPy للأداء العالي. حيث تُطبق العمليات على المصفوفات بالكامل دون استخدام حلقات بايثون، مما يؤدي إلى حسابات أسرع.
  • مجموعة شاملة من العمليات الرياضية: تتضمن NumPy مجموعة ضخمة من العمليات الرياضية مثل np.sum() وnp.mean() وnp.std() ودوال مثلثية، وغيرها.
  • الت indexing والتقطيع المتقدمة: يمكن فهرسة المصفوفات وتقطيعها باستخدام صياغة بديهية، مما يجعل التعامل مع البيانات أسهل وأكثر وضوحًا.
  • التكامل: يتكامل NumPy بشكل جيد مع مكتبات بايثون الأخرى مثل pandas وmatplotlib وscikit-learn، مما يجعله جزءًا رئيسيًا من نظام بيانات بايثون.

نظرة سريعة على ndarray

كائن ndarray هي مصفوفة ذات حجم ثابت في الذاكرة يمكن أن تخزن بيانات من نفس نوع البيانات. إليك مثال بسيط على إنشاء ndarray:


import numpy as np

# إنشاء مصفوفة أحادية البعد
array_1d = np.array([1, 2, 3, 4, 5])

# إنشاء مصفوفة ثنائية الأبعاد
array_2d = np.array([[1, 2, 3], [4, 5, 6]])

print(array_1d)
print(array_2d)
    

كفاءة الذاكرة ومقارنة السرعة

تعد مصفوفات NumPy أكثر كفاءة من حيث الذاكرة لأنها تخزن البيانات في كتل متجاورة من الذاكرة. مما يسمح بأداء أفضل لذاكرة التخزين المؤقت وتقليل الفائض مقارنةً بقوائم بايثون.

مثال الأداء:


import numpy as np
import time

size = 1000000

# استخدام قوائم Python
list1 = range(size)
list2 = range(size)
start_time = time.time()
result_list = [x + y for x, y in zip(list1, list2)]
print("الوقت المستغرق باستخدام القوائم:", time.time() - start_time)

# استخدام مصفوفات NumPy
array1 = np.arange(size)
array2 = np.arange(size)
start_time = time.time()
result_array = array1 + array2
print("الوقت المستغرق باستخدام مصفوفات NumPy:", time.time() - start_time)
    

في الممارسة العملية، تجعل كفاءة استخدام NumPy للذاكرة وتحسينات الحسابات العملية أسرع حتى 50 مرة للعمليات واسعة النطاق مقارنة بقوائم Python.

البدء مع NumPy

التثبيت

لبدء استخدام NumPy، تحتاج إلى تثبيته في بيئة Python الخاصة بك. إذا لم تكن قد قمت بتثبيته بالفعل، استخدم الأمر التالي:


pip install numpy
    

سيقوم هذا الأمر بتثبيت أحدث إصدار من NumPy من PyPI.

استيراد NumPy

بمجرد التثبيت، فإن الصياغة القياسية لاستيراد NumPy هي:


import numpy as np
    

تعتبر هذه الصياغة شائعة في مجتمع Python للحفاظ على شفرة البرمجة مختصرة وسهلة القراءة.

إنشاء المصفوفات

إنشاء المصفوفات في NumPy أمر بسيط ويمكن القيام به بعدة طرق. فيما يلي بعض الطرق الشائعة:

  • من القوائم:
    
    array_from_list = np.array([1, 2, 3, 4, 5])
                
  • باستخدام الدوال المدمجة:
    • الأصفار: ينشئ مصفوفة مليئة بالأصفار.
      
      zeros_array = np.zeros((2, 3))  # مصفوفة 2x3 من الأصفار
                          
    • الأحاد: ينشئ مصفوفة مليئة بالأحاد.
      
      ones_array = np.ones((3, 3))  # مصفوفة 3x3 من الأحاد
                          
    • المتتالية: مشابه لـ range() في Python ولكنه يعيد مصفوفة.
      
      range_array = np.arange(0, 10, 2)  # مصفوفة: [0, 2, 4, 6, 8]
                          
    • linspace: يولد مصفوفة من القيم المتباعدة بالتساوي عبر نطاق محدد.
      
      linspace_array = np.linspace(0, 1, 5)  # مصفوفة: [0. , 0.25, 0.5 , 0.75, 1. ]
                          

خصائص المصفوفات

تأتي المصفوفات في NumPy مع خصائص متنوعة تقدم معلومات حول هيكلها:

  • الشكل:
    
    print(array_2d.shape)  # الناتج: (2, 3)
                
  • نوع البيانات:
    
    print(array_1d.dtype)  # الناتج: int32 أو int64
                
  • الحجم:
    
    print(array_2d.size)  # العدد الإجمالي للعناصر
                
  • عدد الأبعاد:
    
    print(array_2d.ndim)  # الناتج: 2 (للمصفوفة ثنائية الأبعاد)
                

تساعد هذه الخصائص في فحص المصفوفات والتلاعب بها، خاصة عند التعامل مع البيانات متعددة الأبعاد.

العمليات الأساسية

الآن بعد أن عرفنا ما هي NumPy وكيفية البدء، حان الوقت لنتناول بعض العمليات الأساسية. سواء كنت تقوم بتحليل البيانات أو تلعب بالمصفوفات، ستشكل هذه العمليات الأساسية أساس ما ستقوم به مع NumPy.

الرياضيات البسيطة مع المصفوفات

تجعل NumPy الرياضيات على المصفوفات سهلة للغاية. لا تحتاج إلى التكرار عبر العناصر واحدًا تلو الآخر كما تفعل مع قوائم Python العادية. إليك لمحة سريعة عن مدى سهولة ذلك:


import numpy as np

# إنشاء مصفوفتين
array1 = np.array([1, 2, 3, 4])
array2 = np.array([5, 6, 7, 8])

# إجراء عمليات الجمع والطرح والضرب والقسمة العنصرية
sum_array = array1 + array2    # [6, 8, 10, 12]
diff_array = array1 - array2   # [-4, -4, -4, -4]
prod_array = array1 * array2   # [5, 12, 21, 32]
div_array = array2 / array1    # [5.0, 3.0, 2.333, 2.0]

print("Sum:", sum_array)
print("Difference:", diff_array)
print("Product:", prod_array)
print("Division:", div_array)
    

لماذا هذا رائع؟ لأنه سريع وسهل! لا حاجة لكتابة تلك الحلقات المربكة. تستخدم NumPy شفرة C الفعالة في الخلفية، مما يعني أنه حتى مع ملايين العناصر، ستكون هذه العمليات سريعة.

تقطيع وفهرسة المصفوفات

إذا كنت قد قمت بتقطيع قائمة Python من قبل، فستكون في متعة مع NumPy. التقطيع مشابه ولكنه أقوى لأنه يمكنك القيام به على المصفوفات متعددة الأبعاد أيضًا:


# إنشاء مصفوفة ثنائية الأبعاد (مصفوفة)
matrix = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])

# الوصول إلى عناصر معينة
print(matrix[0, 1])  # الناتج: 20 (الصف 0، العمود 1)

# تقطيع الصفوف والأعمدة
print(matrix[1:, 1:])  # الناتج: [[50, 60], [80, 90]] (الصفوف 1 و2، الأعمدة 1 و2)

# الحصول على صف أو عمود كامل
print(matrix[:, 0])  # الناتج: [10, 40, 70] (جميع الصفوف، العمود 0)
    

يمكنك دمج التقطيع والفهرسة المتقدمة لاستخراج أجزاء أو أنماط معينة من مصفوفاتك. هذه المرونة مفيدة للغاية في تحليل البيانات عندما تحتاج إلى التركيز على أجزاء من بياناتك.

إعادة تشكيل المصفوفات

لنقل أنك تمتلك مصفوفة أحادية البعد ولكنك تحتاج إلى إعادة تشكيلها إلى مصفوفة للقيام ببعض العمليات المصفوفية. لا تقلق—لدى NumPy طريقة reshape() لدعمك:


array = np.arange(1, 13)  # ينشئ [1, 2, 3, ..., 12]
reshaped_array = array.reshape(3, 4)  # إعادة تشكيل إلى مصفوفة 3x4
print(reshaped_array)
    

الناتج:


[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
    

هل تحتاج إلى تسطيحه مرة أخرى إلى أحادي البعد؟ فقط استدع flatten() أو استخدم ravel():


flattened_array = reshaped_array.ravel()
print(flattened_array)  # الناتج: [1, 2, 3, ..., 12]
    

العمليات والأساليب الشائعة

تحتوي NumPy على مجموعة من العمليات المدمجة التي تجعل معالجة البيانات سهلة للغاية. إليك بعض العمليات الشائعة التي ستجد نفسك تستخدمها كثيرًا:

العمليات الرياضية الأساسية

توفر NumPy وظائف للعمليات الإحصائية الأساسية مثل إيجاد المتوسط، الوسيط، والانحراف المعياري. إليك كيف يمكنك استخدامها:


data = np.array([15, 20, 25, 30, 35, 40])

# حساب المجموع، المتوسط، والانحراف المعياري
print("Sum:", np.sum(data))              # الناتج: 165
print("Mean:", np.mean(data))            # الناتج: 27.5
print("Standard Deviation:", np.std(data))  # الناتج: ~8.66

# القيم الدنيا والعليا
print("Min:", np.min(data))              # الناتج: 15
print("Max:", np.max(data))              # الناتج: 40
    

توليد الأرقام العشوائية

سواء كنت تحاكي البيانات أو تبدأ أوزان نموذج تعلم آلي، فإن np.random هي الوحدة المناسبة لك:


# توليد مصفوفة من الأرقام العشوائية بين 0 و 1
random_array = np.random.rand(3, 3)
print("Random Array:\n", random_array)

# توليد أعداد صحيحة عشوائية بين 1 و 10
random_integers = np.random.randint(1, 10, size=(2, 4))
print("Random Integers:\n", random_integers)
    

نصيحة احترافية: اضبط البذور باستخدام np.random.seed(42) قبل توليد الأرقام العشوائية للحصول على نتائج قابلة للتكرار. هذا مفيد بشكل خاص عند مشاركة الشفرة الخاصة بك أو إجراء تجارب.

وظائف التجميع

تريد إجراء عمليات على محور معين (مثل جمع كل صف أو عمود)؟ تقدم NumPy الدعم لك:


matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# جمع عبر الصفوف
print("Sum of each row:", np.sum(matrix, axis=1))  # الناتج: [6, 15, 24]

# جمع عبر الأعمدة
print("Sum of each column:", np.sum(matrix, axis=0))  # الناتج: [12, 15, 18]
    

تساعدك وظائف التجميع مثل np.sum() وnp.prod() وnp.cumsum() وnp.cumprod() على فهم البيانات والتعامل معها بسهولة.

نصائح وممارسات أفضل

إذا كنت تريد الحصول على أقصى استفادة من NumPy، فهناك بعض النصائح والممارسات الأفضل التي يمكن أن تساعدك في كتابة كود أنظف، أسرع، وأكثر كفاءة. دعنا نغوص في بعض النصائح العملية التي ستجعلك محترفًا في NumPy:

  • استخدم العمليات المتجهة

واحدة من أكبر الأخطاء التي يرتكبها الناس عند البدء في NumPy هي معاملتها مثل Python العادية. جمال NumPy هي أنها مصممة للعمل مع العمليات المتجهة. تجنب استخدام حلقات Python متى أمكن ذلك؛ استخدم العمليات المدمجة في NumPy بدلاً من ذلك.

ممارسة سيئة:


# إضافة مصفوفتين باستخدام حلقة
result = []
for i in range(len(array1)):
    result.append(array1[i] + array2[i])
    

ممارسة جيدة:


# إضافة مصفوفتين باستخدام عملية متجهة
result = array1 + array2  # أسرع بكثير وأسهل في القراءة
    
  • خصص المصفوفات مسبقًا

إذا كنت تبني مصفوفات ديناميكيًا، فخصص الذاكرة باستخدام np.zeros() أو np.empty() بدلاً من الإضافة إلى قائمة Python وتحويلها إلى مصفوفة لاحقًا. هذا يوفر الوقت والذاكرة.


# تخصيص الذاكرة لمصفوفة 100x100
matrix = np.zeros((100, 100))
    
  • احذر من النسخ مقابل العرض

فهم الفرق بين النسخة والعرض في NumPy أمر حيوي. تعديل عرض يؤثر على المصفوفة الأصلية، بينما تعديل نسخة لا يؤثر عليها.


array = np.array([1, 2, 3, 4])

# إنشاء عرض
view_array = array[1:3]
view_array[0] = 99
print(array)  # تم تعديل المصفوفة الأصلية: [1, 99, 3, 4]

# إنشاء نسخة
copy_array = array[1:3].copy()
copy_array[0] = 50
print(array)  # المصفوفة الأصلية لم تتعدل: [1, 99, 3, 4]
    
  • تعامل بحكمة مع أنواع البيانات

كن واعيًا لأنواع البيانات عند العمل مع المصفوفات لمنع السلوك غير المقصود أو الأخطاء، خاصة عند التعامل مع مجموعات بيانات كبيرة.


# تحديد نوع البيانات
array = np.array([1.5, 2.5, 3.5], dtype=np.float32)
print(array.dtype)  # الناتج: float32
    
  • استخدم np.save() وnp.load() للبيانات الكبيرة

عند التعامل مع مجموعات بيانات كبيرة، يمكن أن تكون استخدامات الإدخال والإخراج المدمجة في Python بطيئة وغير ملائمة. تمتلك NumPy دوال خاصة بها لحفظ وتحميل البيانات والتي تكون سريعة وفعالة.


# حفظ مصفوفة إلى ملف
np.save('my_array.npy', array)

# تحميل مصفوفة من ملف
loaded_array = np.load('my_array.npy')
    

الخاتمة

تهانينا! بحلول الآن، يجب أن يكون لديك فهم جيد لماهية NumPy وكيفية استغلاله للتعامل مع البيانات الرقمية بسهولة. لقد بدأنا بفهم أساسيات إنشاء المصفوفات وصولًا إلى مواضيع أكثر تقدمًا مثل العمليات الفعالة، والتقطيع، ونصائح الممارسات الأفضل. تعد NumPy مكتبة قوية تشكل أساس العديد من مكتبات علم البيانات وتعلم الآلة في Python. ستجعل إتقانها خطوة واحدة للأمام في رحلتك في البيانات.

NumPy ليست مجرد عمليات مصفوفة أسرع؛ إنها تتعلق بكتابة كود أوضح وأكثر كفاءة يتناسب مع احتياجاتك. سواء كنت تقوم بإجراء تحليل بيانات بسيط أو تبني نماذج تعلم آلي معقدة، فإن NumPy هي صديقك.

الموارد والمزيد من القراءة

إذا كنت متحمسًا للتعمق أكثر، إليك بعض الموارد لتوسيع معرفتك بـ NumPy:

استمر في الممارسة من خلال دمج NumPy في مشاريع صغيرة، وسرعان ما ستجد أنه أمر بديهي!

حول المحتوى:

في عالم علوم البيانات والحوسبة العلمية، اكتسبت لغة Python سمعة كواحدة من أكثر لغات البرمجة تنوعًا وقوة. ويرجع ذلك بشكل أساسي إلى مكتبتها الواسعة، مع كون مكتبة NumPy واحدة من الأعمدة الأساسية. NumPy هي مكتبة مفتوحة المصدر مصممة للعمليات العددية، مما يتيح أداء عاليًا على المصفوفات الكبيرة متعددة الأبعاد، إلى جانب مجموعة من العمليات الرياضية للتعامل مع هذه المصفوفات.