6.3. יצירת מערכים

כל דוגמה בשאר העמודים האלה מתחילה עם ndarray שכבר נמצא בידינו. עמוד זה הוא הקטלוג של איך מערך זה נוצר. יש ארבע משפחות של בנאים:

  • מתוך איטרבל של Python – הצורה הרגילה של ליטרל / רשימה / tuple.

  • מלא מראש בצורה נתונה – אפסים, אחדים, ערך קבוע, מטריצת יחידה.

  • מיוצר כסדרה – ערכים בטווח או במרווח אחיד.

  • עטיפת חוצץ שכבר נמצא ב-RAM – מקרה ההתקן ההיקפי.

כל בנאי מקבל מילת מפתח dtype= ובברירת מחדל הוא float. נתוני חיישן כמעט תמיד רוצים dtype קטן יותר מברירת המחדל.

כל דוגמה למטה מתחילה ב:

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 הוא מספר שלם בודד (עבור מערך חד-ממדי) או tuple.

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() מחבר tuple של מערכים לאורך ציר קיים:

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)

כל הקלטים חייבים לחלוק את אותו dtype ו-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. הבתים שההתקן ההיקפי כתב הם המערך.

כאשר התקן היקפי כותב ערכים רבי-בתים בסדר בתים שה-CPU של המצלמה אינו קורא באופן טבעי, byteswap() הופך את סדר הבתים של כל איבר כך שהערכים נקראים נכון. הוא מחזיר מערך חדש כברירת מחדל; העברת inplace=True משנה את המקור במקום.

frombuffer() מטפל רק ב-dtypes ש-numpy עצמו מגדיר. עבור התקנים היקפיים המפיקים דגימות שלמים בנות 32 סיביות, from_int32_buffer() וחבריו ממירים ל-float במעבר אחד.