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 במעבר אחד.