مرحباً بكم أيها المبرمجون! في هذا الدرس الاحترافي، سنتعمق في مكتبة NumPy، العصب الرئيسي للحوسبة العلمية في بايثون. سنتعلم كيفية استغلال قوتها لمعالجة المصفوفات بكفاءة وإجراء العمليات الرياضية المعقدة بسرعة فائقة.
الخطوة 1: إنشاء المصفوفات والخصائص الأساسية (Array Creation and Basic Attributes)
تعتبر كائنات ndarray (N-dimensional array) هي جوهر NumPy. لنبدأ بإنشاء بعض المصفوفات واستكشاف خصائصها.
import numpy as np # استيراد مكتبة NumPy بالاسم المستعار الشائع 'np'
# إنشاء مصفوفة أحادية البعد من قائمة بايثون
arr1 = np.array([1, 2, 3, 4, 5])
print("المصفوفة أحادية البعد:", arr1)
print("شكل المصفوفة (الأبعاد):", arr1.shape) # عدد العناصر في كل بعد
print("نوع بيانات العناصر:", arr1.dtype) # نوع البيانات المخزنة (مثال: int64)
# إنشاء مصفوفة ثنائية البعد (مصفوفة)
arr2 = np.array([[10, 20, 30], [40, 50, 60]])
print("\nالمصفوفة ثنائية البعد:\n", arr2)
print("شكل المصفوفة الثنائية:", arr2.shape)
print("عدد أبعاد المصفوفة:", arr2.ndim) # عدد الأبعاد (مثال: 2)
# إنشاء مصفوفات خاصة
zeros_arr = np.zeros((3, 4)) # مصفوفة 3x4 مليئة بالأصفار
ones_arr = np.ones((2, 2)) # مصفوفة 2x2 مليئة بالواحدات
range_arr = np.arange(0, 10, 2) # مصفوفة من 0 إلى 9 بخطوات 2 (0, 2, 4, 6, 8)
linspace_arr = np.linspace(0, 1, 5) # 5 أرقام موزعة بالتساوي بين 0 و 1
print("\nمصفوفة الأصفار:\n", zeros_arr)
print("مصفوفة الواحدات:\n", ones_arr)
print("مصفوفة المدى (arange):\n", range_arr)
print("مصفوفة المسافات المتساوية (linspace):\n", linspace_arr)
ملاحظة تقنية: استخدام
np.arrayهو الطريقة الأكثر شيوعًا لإنشاء المصفوفات من البيانات الموجودة. بينما توفر دوال مثلnp.zerosوnp.onesوnp.arangeأدوات قوية لإنشاء مصفوفات ذات أنماط محددة بسرعة.
الخطوة 2: العمليات الحسابية على المصفوفات (Array Arithmetic Operations)
تتيح NumPy إجراء عمليات حسابية على المصفوفات بكفاءة عالية، حيث تتم العمليات على مستوى العناصر (element-wise) افتراضياً.
import numpy as np
arr_a = np.array([1, 2, 3])
arr_b = np.array([4, 5, 6])
# الجمع والطرح والقسمة والضرب على مستوى العناصر
sum_arr = arr_a + arr_b
sub_arr = arr_b - arr_a
mul_arr = arr_a * arr_b
div_arr = arr_b / arr_a
print("المصفوفة A:", arr_a)
print("المصفوفة B:", arr_b)
print("الجمع (A + B):", sum_arr)
print("الطرح (B - A):", sub_arr)
print("الضرب (A * B):", mul_arr)
print("القسمة (B / A):", div_arr)
# ضرب المصفوفات (الضرب النقطي - Dot Product)
matrix_a = np.array([[1, 2], [3, 4]])
matrix_b = np.array([[5, 6], [7, 8]])
dot_product = np.dot(matrix_a, matrix_b) # أو matrix_a @ matrix_b في بايثون 3.5+
print("\nالمصفوفة A:\n", matrix_a)
print("المصفوفة B:\n", matrix_b)
print("الضرب النقطي (A . B):\n", dot_product)
# استخدام الدوال العالمية (Universal Functions - ufuncs)
x = np.array([0, np.pi/2, np.pi]) # قيم بالراديان
sin_x = np.sin(x) # حساب جيب الزاوية لكل عنصر
exp_x = np.exp(x) # حساب e^x لكل عنصر
sqrt_x = np.sqrt([4, 9, 16]) # حساب الجذر التربيعي لكل عنصر
print("\nقيم x:", x)
print("sin(x):", sin_x)
print("exp(x):", exp_x)
print("sqrt([4, 9, 16]):", sqrt_x)
ملاحظة تقنية: العمليات على مستوى العناصر في NumPy أسرع بكثير من الحلقات التكرارية في بايثون النقية، وذلك بفضل تنفيذها الأمثل بلغة C.
الخطوة 3: الفهرسة المتقدمة والتقطيع (Advanced Indexing and Slicing)
توفر NumPy طرقاً مرنة وقوية للوصول إلى عناصر المصفوفات وتعديلها.
import numpy as np
data = np.array([
[10, 20, 30, 40],
[50, 60, 70, 80],
[90, 100, 110, 120]
])
print("المصفوفة الأصلية:\n", data)
# التقطيع الأساسي (Basic Slicing)
# الصف الأول (كل الأعمدة)
row_1 = data[0, :]
print("\nالصف الأول:", row_1)
# العمود الثاني (كل الصفوف)
col_2 = data[:, 1]
print("العمود الثاني:", col_2)
# جزء من المصفوفة (من الصف 1 إلى 2، ومن العمود 0 إلى 2)
sub_matrix = data[1:3, 0:3]
print("مصفوفة فرعية (1:3, 0:3):\n", sub_matrix)
# الفهرسة بالمصفوفات الصحيحة (Integer Array Indexing)
# اختيار عناصر محددة: (0,0), (1,2), (2,1)
elements = data[[0, 1, 2], [0, 2, 1]]
print("\nعناصر مختارة (0,0), (1,2), (2,1):", elements)
# اختيار صفوف بترتيب معين
selected_rows = data[[2, 0]]
print("صفوف مختارة بترتيب (2, 0):\n", selected_rows)
# الفهرسة بالمصفوفات البوليانية (Boolean Array Indexing)
# اختيار العناصر الأكبر من 70
greater_than_70 = data[data > 70]
print("\nالعناصر الأكبر من 70:", greater_than_70)
# اختيار الصفوف التي يكون فيها العنصر الأول أكبر من 50
rows_condition = data[data[:, 0] > 50]
print("الصفوف حيث العنصر الأول > 50:\n", rows_condition)
# تغيير شكل المصفوفة (Reshaping)
reshaped_data = data.reshape(6, 2) # تحويل 3x4 إلى 6x2
print("\nالمصفوفة بعد تغيير الشكل (6x2):\n", reshaped_data)
ملاحظة تقنية: الفهرسة البوليانية قوية بشكل خاص لتصفية البيانات بناءً على شروط معينة، وهي تستخدم على نطاق واسع في تحليل البيانات.
الخطوة 4: البث (Broadcasting) والعمليات الإحصائية (Statistical Operations)
البث هو آلية قوية تسمح لـ NumPy بإجراء عمليات على مصفوفات ذات أشكال مختلفة. كما توفر دوالاً إحصائية مدمجة.
import numpy as np
# مثال على البث (Broadcasting)
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 10
vector_1d = np.array([100, 200, 300])
# إضافة قيمة عددية (scalar) إلى كل عنصر في المصفوفة
result_scalar_add = arr_2d + scalar
print("مصفوفة 2D:\n", arr_2d)
print("إضافة قيمة عددية (10):\n", result_scalar_add)
# إضافة مصفوفة أحادية البعد (vector) إلى كل صف في المصفوفة ثنائية البعد
# NumPy تقوم بتمديد (broadcasting) المتجه ليتناسب مع شكل الصفوف
result_vector_add = arr_2d + vector_1d
print("\nإضافة متجه 1D ([100, 200, 300]):\n", result_vector_add)
# العمليات الإحصائية
data_stats = np.array([
[10, 15, 20],
[25, 30, 35],
[40, 45, 50]
])
print("\nمصفوفة البيانات للعمليات الإحصائية:\n", data_stats)
print("المجموع الكلي:", np.sum(data_stats))
print("المتوسط الكلي:", np.mean(data_stats))
print("القيمة القصوى الكلية:", np.max(data_stats))
# العمليات الإحصائية على طول المحاور (Axes)
# axis=0 تعني على طول الأعمدة (كل صف يمثل نقطة بيانات)
# axis=1 تعني على طول الصفوف (كل عمود يمثل خاصية)
print("\nمجموع كل عمود (axis=0):", np.sum(data_stats, axis=0))
print("متوسط كل صف (axis=1):", np.mean(data_stats, axis=1))
print("الانحراف المعياري الكلي:", np.std(data_stats))
ملاحظة تقنية: فهم
broadcastingأمر حيوي لكتابة كود NumPy فعال وموجز، حيث يقلل من الحاجة إلى تكرار البيانات أو استخدام الحلقات الصريحة.
الكود النهائي الكامل
إليك السكربت كاملاً، يجمع كل المفاهيم التي تعلمناها في هذا الدرس.
import numpy as np
# الخطوة 1: إنشاء المصفوفات والخصائص الأساسية
print("--- الخطوة 1: إنشاء المصفوفات والخصائص الأساسية ---")
arr1 = np.array([1, 2, 3, 4, 5])
arr2 = np.array([[10, 20, 30], [40, 50, 60]])
print("المصفوفة أحادية البعد:", arr1)
print("شكل المصفوفة الثنائية:", arr2.shape)
print("مصفوفة الأصفار (2x3):\n", np.zeros((2, 3)))
print("-" * 50)
# الخطوة 2: العمليات الحسابية على المصفوفات
print("--- الخطوة 2: العمليات الحسابية على المصفوفات ---")
arr_a = np.array([1, 2, 3])
arr_b = np.array([4, 5, 6])
print("الجمع (A + B):", arr_a + arr_b)
matrix_a = np.array([[1, 2], [3, 4]])
matrix_b = np.array([[5, 6], [7, 8]])
print("الضرب النقطي (A . B):\n", np.dot(matrix_a, matrix_b))
print("sin([0, pi/2, pi]):", np.sin(np.array([0, np.pi/2, np.pi])))
print("-" * 50)
# الخطوة 3: الفهرسة المتقدمة والتقطيع
print("--- الخطوة 3: الفهرسة المتقدمة والتقطيع ---")
data = np.array([
[10, 20, 30, 40],
[50, 60, 70, 80],
[90, 100, 110, 120]
])
print("الصف الأول:", data[0, :])
print("العناصر الأكبر من 70:", data[data > 70])
print("مصفوفة بعد تغيير الشكل (2x6):\n", data.reshape(2, 6))
print("-" * 50)
# الخطوة 4: البث والعمليات الإحصائية
print("--- الخطوة 4: البث والعمليات الإحصائية ---")
arr_2d_broad = np.array([[1, 2, 3], [4, 5, 6]])
vector_1d_broad = np.array([100, 200, 300])
print("إضافة متجه 1D:\n", arr_2d_broad + vector_1d_broad)
data_stats = np.array([[10, 20], [30, 40]])
print("المجموع الكلي:", np.sum(data_stats))
print("متوسط كل عمود (axis=0):", np.mean(data_stats, axis=0))
print("-" * 50)
النتيجة المتوقعة
عند تشغيل السكربت أعلاه، ستحصل على مخرجات مطبوعة على الشاشة توضح كل خطوة من خطوات الدرس. ستشاهد أمثلة على إنشاء المصفوفات، ونتائج العمليات الحسابية (الجمع، الضرب النقطي، الدوال المثلثية)، وكيفية استخلاص البيانات باستخدام الفهرسة المتقدمة، بالإضافة إلى تأثير البث ونتائج العمليات الإحصائية على المصفوفات. ستكون المخرجات منظمة ومفصلة لتعكس كل جزء من الكود.
--- الخطوة 1: إنشاء المصفوفات والخصائص الأساسية --- المصفوفة أحادية البعد: [1 2 3 4 5] شكل المصفوفة الثنائية: (2, 3) مصفوفة الأصفار (2x3): [[0. 0. 0.] [0. 0. 0.]] -------------------------------------------------- --- الخطوة 2: العمليات الحسابية على المصفوفات --- الجمع (A + B): [5 7 9] الضرب النقطي (A . B): [[19 22] [43 50]] sin([0, pi/2, pi]): [0.00000000e+00 1.00000000e+00 1.22464680e-16] -------------------------------------------------- --- الخطوة 3: الفهرسة المتقدمة والتقطيع --- الصف الأول: [10 20 30 40] العناصر الأكبر من 70: [ 80 90 100 110 120] مصفوفة بعد تغيير الشكل (2x6): [[ 10 20 30 40 50 60] [ 70 80 90 100 110 120]] -------------------------------------------------- --- الخطوة 4: البث والعمليات الإحصائية --- إضافة متجه 1D: [[101 202 303] [104 205 306]] المجموع الكلي: 100 متوسط كل عمود (axis=0): [20. 30.] --------------------------------------------------