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

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

  • من كائن قابل للتكرار في Python -- الصيغة الحرفية / القائمة / الصف المعتادة.

  • مملوءة مسبقًا بشكل معطى -- أصفار، أو آحاد، أو قيمة ثابتة، أو مصفوفة وحدة.

  • مولّدة كمتتالية -- قيم ضمن نطاق أو متباعدة بالتساوي.

  • تغليف مخزن مؤقت موجود بالفعل في RAM -- حالة الطرفية.

كل مُنشئ يأخذ كلمة مفتاحية dtype= ويكون افتراضه float. وبيانات المستشعر تتطلب دائمًا تقريبًا نوع بيانات أصغر من الافتراضي.

كل مثال أدناه يبدأ بـ:

from ulab import numpy as np

6.3.1. من كائن قابل للتكرار في Python

يبني array() مصفوفة ndarray من أي كائن أرقام قابل للتكرار:

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

الخرج:

array([1.0, 2.0, 3.0, 4.0], dtype=float)

تنتج الكائنات القابلة للتكرار المتداخلة مصفوفات متعددة الأبعاد. ويجب أن يكون لجميع الكائنات الداخلية القابلة للتكرار نفس الطول، وإلا أُطلق ValueError

m = np.array([[1, 2, 3],
              [4, 5, 6]], dtype=np.uint8)

تُعد مصفوفة ndarray موجودة مسبقًا مدخلًا صالحًا أيضًا؛ وarray() تنسخ دائمًا. ولتجنّب النسخ عند عدم الحاجة إليه، استخدم asarray()

b = np.asarray(a, dtype=np.float)   # same dtype -> no copy

6.3.2. مملوءة مسبقًا بشكل معطى

عندما يكون الشكل الهدف معروفًا ولكن المحتويات لم تتحدد بعد، خصّص المخزن المؤقت مسبقًا واكتب فيه لاحقًا:

  • zeros() -- مملوءة بالأصفار.

  • ones() -- مملوءة بالآحاد.

  • full() -- مملوءة بقيمة معطاة.

  • empty() -- اسم مرادف لـ zeros() (لا يترك ulab المخزن المؤقت غير مُهيّأ).

  • eye() -- مصفوفة شبيهة بمصفوفة الوحدة بحجم N في M مع آحاد على القطر رقم k.

  • diag() -- مصفوفة قطرية من متجه، أو قطر مصفوفة.

np.zeros((3, 3))                   # 3x3 of zeros
np.ones(5, dtype=np.uint8)         # length-5 vector of ones
np.full((2, 3), 7, dtype=np.int8)  # 2x3, all 7
np.eye(4)                          # 4x4 identity
np.diag([1, 2, 3])                 # 3x3, [1, 2, 3] on the diagonal

وسيط shape هو إما عدد صحيح واحد (لمصفوفة أحادية البعد) أو صف.

6.3.3. مولّدة كمتتالية

  • arange() -- قيم متباعدة بالتساوي مثل range() المدمجة، لكنها تعيد دائمًا مصفوفة ndarray

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace() -- عدد num من النقاط المتباعدة بالتساوي بين حدّين، مع تضمين الحد الأعلى عندما يكون endpoint=True

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() -- نقاط متباعدة هندسيًا. القيمتان start وstop هما أُسّان، وليستا نقطتي نهاية؛ تمتد النتيجة من base ** start إلى base ** stop

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() -- يبني مصفوفتي إحداثيات من مصفوفتين أحاديتي البعد بحيث يمكن تقييم دالة لكل بكسل f(x, y) على شبكة كاملة في استدعاء واحد متجهي. وبالنظر إلى متجه x بطول W ومتجه y بطول H، يعيد meshgrid مصفوفتين بحجم H في W: X هي متجه x مكررًا أسفل كل صف، وY هي متجه y مكررًا عبر كل عمود، بحيث تكون X[i, j] هي إحداثي x وY[i, j] هي إحداثي y للخلية عند الصف i والعمود j

    x = np.arange(4)            # [0, 1, 2, 3]
    y = np.arange(3)            # [0, 1, 2]
    X, Y = np.meshgrid(x, y)
    # X = [[0, 1, 2, 3],
    #      [0, 1, 2, 3],
    #      [0, 1, 2, 3]]
    # Y = [[0, 0, 0, 0],
    #      [1, 1, 1, 1],
    #      [2, 2, 2, 2]]
    

    ثم تقيّم f(X, Y) الدالة عند كل خلية من الشبكة في تعبير واحد. فخريطة المسافة من المركز عبر إطار (H, W)، على سبيل المثال، هي np.sqrt((X - cx)**2 + (Y - cy)**2) مقابل المصفوفتين اللتين أعادتهما meshgrid().

6.3.4. الدمج

تدمج concatenate() صف مصفوفات على طول محور موجود:

a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
b = np.array([[5, 6]],         dtype=np.uint8)
np.concatenate((a, b), axis=0)
# array([[1, 2], [3, 4], [5, 6]], dtype=uint8)

يجب أن تشترك جميع المدخلات في نفس نوع البيانات ونفس ndim، وأن تتطابق على كل محور عدا محور الدمج. وتخصّص concatenate() مصفوفة جديدة كبيرة بما يكفي لاحتواء كل مدخل وتنسخ البيانات فيها، لذا فهي الأداة المناسبة للدمج لمرة واحدة لمصفوفات موجودة بالفعل؛ وهي الأداة الخاطئة داخل حلقة بثّ، حيث يكون النمط هو تخصيص الوجهة مسبقًا مرة واحدة والكتابة فيها عبر إسناد الشرائح.

6.3.5. تغليف مخزن مؤقت موجود

أكثر المُنشئات فائدة على الكاميرا هو frombuffer(). فهو يعيد تفسير مخزن مؤقت موجود شبيه بالبايتات على أنه ndarray أحادية البعد دون نسخ بايت واحد:

buf = bytearray(8)
audio = np.frombuffer(buf, dtype=np.int16)
# 4 int16 samples, sharing memory with buf

الكتابة عبر audio تكون مرئية في buf والعكس صحيح. ويجب أن يقسم نوع dtype المختار طول المخزن المؤقت بالتساوي.

يتخطى offset= ترويسة في بداية المخزن المؤقت؛ ويحدّ count= من عدد العناصر التي تُقرأ:

np.frombuffer(buf, dtype=np.uint8, offset=2, count=4)

هذا هو المُنشئ المناسب كلما سلّمت طرفية للتطبيق مخزنًا مؤقتًا خامًا -- عينات ADC في bytearray، أو حمولة مسحوبة من SPI. فالبايتات التي كتبتها الطرفية هي المصفوفة.

عندما تكتب طرفية قيمًا متعددة البايتات بترتيب بايتات لا تقرؤه وحدة المعالجة المركزية في الكاميرا بشكل أصلي، تعكس byteswap() ترتيب بايتات كل عنصر بحيث تُقرأ القيم بشكل صحيح. وتعيد مصفوفة جديدة افتراضيًا؛ ويؤدي تمرير inplace=True إلى تعديل المصدر في مكانه.

تتعامل frombuffer() فقط مع أنواع البيانات التي يعرّفها numpy نفسه. وبالنسبة للطرفيات التي تنتج عينات أعداد صحيحة بطول 32 بت، تحوّل from_int32_buffer() وما شابهها إلى float في تمريرة واحدة.