NumPy: أساسيات ndarray الأساسية
آخر تحديث: 2026-08-26
هيكل البيانات الأساسي في NumPy هو ndarray — المصفوفة متعددة الأبعاد. تبدو مثل قائمة Python، لكنها مختلفة تمامًا من الداخل: نوع ثابت، ذاكرة متجاورة، عمليات متجهة. فهم داخلية ndarray هو الخطوة الأولى لاستخدام NumPy بفعالية.
1. ما ستتعلمه
- ❶ جوهر ndarray: متجانس، نوع ثابت، ذاكرة متجاورة
- ❷ الأبعاد (ndim)، الشكل، والحجم
- ❸ الحدس وراء المحاور
- ❹ نظام dtype واستهلاك الذاكرة
- ❺ مقارنة نموذج الذاكرة بين ndarray والقائمة
2. قصة حقيقية لمحلل بيانات
(1) المشكلة: Excel في Python، ارتباك
بوب لديه جدول Excel بثلاثة صفوف وأربعة أعمدة. يخزنه كقائمة من القوائم في Python — 3 قوائم فرعية متداخلة داخل قائمة خارجية. يريد جمع العمود الثاني، لذا يكتب حلقة for للتكرار عبر كل صف. لمليون صف، يستغرق ذلك 5 ثوان. والأسوأ، عندما يتسلل نص إلى أحد الصفوف، لا تفشل الحلقة حتى تصل إلى تلك الخلية.
(2) الحل باستخدام ndarray
يقترح تشارلي على بوب تحويل الجدول إلى ndarray باستخدام np.array. NumPy يفلطح البيانات إلى كتلة ذاكرة متجاورة وينفذ مجموع العمود في عملية واحدة متجهة على مستوى لغة C — نفس المليون صف تستغرق 5 ملي ثانية فقط. يتم فرض الأنواع في وقت الإنشاء، لذا يتم اكتشاف أي نص غريب فورًا، وليس في منتصف العملية الحسابية.
(3) النتيجة
- أداء أسرع بـ 1000 مرة
- يتم اكتشاف عدم تطابق الأنواع في وقت الإنشاء
- البيانات الوصفية مثل الشكل والمحاور والخطوات تجعل العمليات متعددة الأبعاد بديهية
3. جوهر ndarray
(1) أنواع البيانات المتجانسة
يجب أن تكون جميع عناصر ndarray من نفس النوع. هذا مختلف جوهريًا عن قوائم Python — يمكن للقوائم خلط الأعداد الصحيحة والنصوص وحتى الكائنات، بينما يسمح ndarray بنوع واحد فقط.
import numpy as np
a = np.array([1, 2, 3])
print(a.dtype) # int64
b = np.array([1.0, 2.0, 3.0])
print(b.dtype) # float64
c = np.array([1, 2.0, 3])
print(c.dtype) # float64 (رفع تلقائي)
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
(2) تخزين الذاكرة المتجاورة
قوائم Python تخزن مصفوفة من المؤشرات — كل مؤشر يشير إلى كائن PyObject منفصل موزع عبر ذاكرة الكومة. ndarray، من ناحية أخرى، يرتب كل البيانات في كتلة واحدة متجاورة من الذاكرة، بدون حمل إضافي للمؤشرات وكفاءة ممتازة لذاكرة التخزين المؤقت لوحدة المعالجة المركزية.
| الميزة | قائمة Python | ndarray |
|---|---|---|
| نوع العنصر | أي خليط | نفس dtype |
| التخزين | مصفوفة مؤشرات ← كائنات PyObject مبعثرة | كتلة ذاكرة متجاورة |
| الحمل الإضافي لكل عنصر | 28+ بايت (رأس PyObject) | يعتمد على dtype (1/2/4/8 بايت) |
| ملائمة ذاكرة التخزين المؤقت | ضعيفة (قفز بين المؤشرات) | ممتازة (وصول تسلسلي) |
| أمان النوع | لا يوجد (يُكتشف في وقت التشغيل) | يُفرض في وقت الإنشاء |
▶ مثال
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
: خصائص المصفوفات 0D/1D/2D/3D (الصعوبة ⭐)
import numpy as np
a0 = np.array(42) # 0D عددي
a1 = np.array([1, 2, 3]) # 1D
a2 = np.array([[1, 2], [3, 4]]) # 2D
a3 = np.array([[[1, 2], [3, 4]],
[[5, 6], [7, 8]]]) # 3D
for name, arr in [("0D", a0), ("1D", a1), ("2D", a2), ("3D", a3)]:
print(f"{name}: ndim={arr.ndim}, shape={arr.shape}, size={arr.size}")
# 0D: ndim=0, shape=(), size=1
---
## 4. 1D: ndim=1, shape=(3,), size=3
---
## 5. 2D: ndim=2, shape=(2,2), size=4
---
## 6. 3D: ndim=3, shape=(2,2,2), size=8
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
7. الأبعاد، الشكل، والحجم
(1) ndim / shape / size
| الخاصية | المعنى | مثال (shape=(3,4)) |
|---|---|---|
ndim |
عدد الأبعاد (المحاور) | 2 |
shape |
طول كل محور، كمجموعة | (3, 4) |
size |
العدد الإجمالي للعناصر = حاصل ضرب أطوال المحاور | 12 |
import numpy as np
a = np.zeros((3, 4))
print(a.ndim) # 2
print(a.shape) # (3, 4)
print(a.size) # 12
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
(2) الخطوات (Strides)
strides تخبرك بعدد البايتات التي يجب تخطيها في الذاكرة للتحرك خطوة واحدة على طول محور معين.
| الخاصية | المعنى | shape=(3,4) dtype=int64 |
|---|---|---|
strides |
الخطوة لكل محور (بايت) | (32, 8) |
itemsize |
بايت لكل عنصر | 8 |
import numpy as np
a = np.zeros((3, 4), dtype=np.int64)
print(a.strides) # (32, 8)
---
## 8. المحور 0: تحرك صفًا واحدًا = 4 * 8 = 32 بايت
---
## 9. المحور 1: تحرك عمودًا واحدًا = 1 * 8 = 8 بايت
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
▶ مثال
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
: تصور الخطوات (الصعوبة ⭐⭐)
import numpy as np
a = np.arange(12, dtype=np.int32).reshape(3, 4)
print(a)
---
## 10. [[ 0 1 2 3]
---
## 11. [ 4 5 6 7]
---
## 12. [ 8 9 10 11]]
print(f"shape: {a.shape}") # (3, 4)
print(f"strides: {a.strides}") # (16, 4)
print(f"itemsize: {a.itemsize}") # 4
---
## 13. خطوة المحور 0: 4 عناصر * 4 بايت = 16
---
## 14. خطوة المحور 1: 1 عنصر * 4 بايت = 4
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
15. الحدس وراء المحاور
المحاور هي مفتاح فهم عمليات ndarray. axis=0 هو البعد الخارجي، axis=1 هو التالي، وهكذا.
| المحور | الموقع في shape | الحدس | 2D shape=(3,4) | 3D shape=(2,3,4) |
|---|---|---|---|---|
| axis=0 | 0 | الخارجي | اتجاه الصفوف (عبر الصفوف) | على طول محور "الطبقة" |
| axis=1 | 1 | الثاني | اتجاه الأعمدة (عبر الأعمدة) | على طول محور "الصف" |
| axis=2 | 2 | الأعمق | — | على طول محور "العمود" |
خدعة للذاكرة: axis=k يتوافق مع اتجاه shape[k]. القيام بـ sum/reduce على طول هذا المحور "يلغي" ذلك البعد.
import numpy as np
a = np.arange(24).reshape(2, 3, 4)
print(a.shape) # (2, 3, 4)
print(a.sum(axis=0).shape) # (3, 4) — المحور 0 أُلغي
print(a.sum(axis=1).shape) # (2, 4) — المحور 1 أُلغي
print(a.sum(axis=2).shape) # (2, 3) — المحور 2 أُلغي
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
16. dtype واستخدام الذاكرة
(1) نظام أنواع dtype
| dtype | الوصف | بايت | النطاق |
|---|---|---|---|
np.int8 |
عدد صحيح بإشارة 8 بت | 1 | -128 ~ 127 |
np.int32 |
عدد صحيح بإشارة 32 بت | 4 | -2^31 ~ 2^31-1 |
np.int64 |
عدد صحيح بإشارة 64 بت | 8 | -2^63 ~ 2^63-1 |
np.float32 |
عدد عائم 32 بت | 4 | ~±3.4e38، دقة 7 أرقام |
np.float64 |
عدد عائم 64 بت | 8 | ~±1.8e308، دقة 15 رقمًا |
np.bool_ |
منطقي | 1 | True/False |
np.complex128 |
عدد مركب 128 بت | 16 | float64 مزدوج |
(2) نموذج الذاكرة: ndarray ليس قائمة متداخلة
graph TB
A["كائن ndarray"] --> B["مؤشر البيانات<br/>كتلة ذاكرة خام"]
A --> C["dtype<br/>نوع العنصر"]
A --> D["shape<br/>(3, 4)"]
A --> E["strides<br/>(32, 8)"]
B --> F["0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | ... | 2,3"]
style A fill:#4CAF50,color:#fff
style B fill:#2196F3,color:#fff
style F fill:#FF9800,color:#fff
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
(3) جدول مرجعي للشكل والذاكرة
| shape | ndim | size | dtype=float64 nbytes | strides |
|---|---|---|---|---|
| (10,) | 1 | 10 | 80 | (8,) |
| (3, 4) | 2 | 12 | 96 | (32, 8) |
| (2, 3, 4) | 3 | 24 | 192 | (96, 32, 8) |
| (5, 2, 3, 4) | 4 | 120 | 960 | (192, 96, 32, 8) |
▶ مثال
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
: مقارنة ذاكرة أنواع dtype (الصعوبة ⭐)
import numpy as np
for dtype in [np.int8, np.int32, np.int64, np.float32, np.float64]:
a = np.zeros(1000, dtype=dtype)
print(f"{dtype.__name__:10s} itemsize={a.itemsize} nbytes={a.nbytes}")
---
## 17. int8 itemsize=1 nbytes=1000
---
## 18. int32 itemsize=4 nbytes=4000
---
## 19. int64 itemsize=8 nbytes=8000
---
## 20. float32 itemsize=4 nbytes=4000
---
## 21. float64 itemsize=8 nbytes=8000
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
▶ مثال
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
: التحقق من أن reshape لا ينسخ البيانات (الصعوبة ⭐⭐)
import numpy as np
a = np.arange(12, dtype=np.int64)
b = a.reshape(3, 4)
print(a.shape, b.shape) # (12,) (3, 4)
print(a.strides, b.strides) # (8,) (32, 8)
print(a.nbytes, b.nbytes) # 96 96
b[0, 0] = 999
print(a[0]) # 999 — نفس الذاكرة!
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
22. مثال شامل: تحليل متعمق لمصفوفة ثلاثية الأبعاد
▶ مثال
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
: تحليل كامل لخصائص مصفوفة 3×4×2 (الصعوبة ⭐⭐⭐)
import numpy as np
a = np.arange(24, dtype=np.int64).reshape(3, 4, 2)
print("=== المصفوفة الكاملة ===")
print(a)
---
## 23. [[[ 0 1]
---
## 24. [ 2 3]
---
## 25. [ 4 5]
---
## 26. [ 6 7]]
---
## 27. [[ 8 9]
---
## 28. [10 11]
---
## 29. [12 13]
---
## 30. [14 15]]
---
## 31. [[16 17]
---
## 32. [18 19]
---
## 33. [20 21]
---
## 34. [22 23]]]
print(f"ndim: {a.ndim}") # 3
print(f"shape: {a.shape}") # (3, 4, 2)
print(f"size: {a.size}") # 24
print(f"dtype: {a.dtype}") # int64
print(f"itemsize:{a.itemsize}") # 8
print(f"nbytes: {a.nbytes}") # 192 (24 * 8)
print(f"strides: {a.strides}") # (64, 16, 8)
---
## 35. المحور 0: 4*2*8 = 64 بايت لكل خطوة
---
## 36. المحور 1: 2*8 = 16 بايت لكل خطوة
---
## 37. المحور 2: 1*8 = 8 بايت لكل خطوة
---
## 38. تحقق يدوي من الخطوات
expected_strides = (
a.shape[1] * a.shape[2] * a.itemsize, # 4*2*8 = 64
a.shape[2] * a.itemsize, # 2*8 = 16
a.itemsize # 8
)
print(f"الخطوات اليدوية: {expected_strides}") # (64, 16, 8)
print(f"تطابق: {a.strides == expected_strides}") # True
---
## 39. تقليل المحاور
print(a.sum(axis=0).shape) # (4, 2)
print(a.sum(axis=1).shape) # (3, 2)
print(a.sum(axis=2).shape) # (3, 4)
> **المخرجات:** قم بتشغيل NumPy 2.x في بيئة Python المحلية لديك لرؤية مخرجات ndarray. خادم Piston لا يحتوي على NumPy مثبتًا مسبقًا — قم بتثبيته محليًا (`pip install numpy`) وقارن. قد تختلف القيم الفعلية حسب إصدار NumPy والبذرة العشوائية.
❓ أسئلة شائعة
📖 ملخص
- ndarray هو مصفوفة متعددة الأبعاد متجانسة، ثابتة النوع، ذات ذاكرة متجاورة
ndimهو عدد الأبعاد،shapeهو مجموعة أطوال المحاور،sizeهو العدد الإجمالي للعناصر- المحاور مرقمة من الخارج إلى الداخل: axis=0 هو الأبعد، axis=ndim-1 هو الأعمق
stridesتصف إزاحة الذاكرة للتحرك على طول كل محور؛ reshape لا يغير البيانات الأساسيةdtypeيحدد نوع العنصر وitemsize؛nbytes = size * itemsize- نموذج الذاكرة المتجاورة لـ ndarray مقابل نموذج القفز بين المؤشرات في القائمة هو أصل فجوة الأداء
📝 تمارين
-
مبتدئ (الصعوبة ⭐): أنشئ أربع مصفوفات بطول 5000 بأنواع int8 و int32 و float32 و float64. اطبع
nbytesلكل مصفوفة واشرح لماذا تستخدم الأنواع المختلفة كميات مختلفة من الذاكرة. -
متوسط (الصعوبة ⭐⭐): بمعلومية ndarray بـ shape=(5, 3, 2) و dtype=int16، احسب خطواتها و nbytes يدويًا، ثم تحقق من حسابك بالكود.
-
متقدم (الصعوبة ⭐⭐⭐): اشرح بكلماتك الخاصة كيف يتم ترتيب ndarray بـ shape=(2,3,4) في الذاكرة. ارسم مخططًا، وسم الخطوات، وأظهر إزاحة الذاكرة لكل عنصر.