6.7. תצוגות והעתקים#

תצוגה היא חלון שני אל אותו בלוק נתונים כמו המקור. שום נתון אינו מועתק; התצוגה מחזיקה מתאר חדש (צורה, צעדים ו-dtype משלה) אך חולקת את החוצץ. תצוגות הן בעצם חינמיות.

העתק מבקש מהמצלמה חוצץ חדש ועובר על המקור כשהוא ממלא אותו. העתקים עולים גם זמן וגם RAM.

רוב המתודות משנות-הצורה מייצרות תצוגות. רוב אלה משנות-הנתונים מייצרות העתקים. ידיעה איזו היא איזו מכריעה האם לולאה חמה תרוקן את ה-RAM של המצלמה.

6.7.1. Reshape#

reshape() מחזיר מערך בצורה המבוקשת. המספר הכולל של האיברים חייב להישאר ללא שינוי אחרת מעורר ValueError

a = np.arange(12, dtype=np.uint8)
m = a.reshape((3, 4))

התוצאה היא תצוגה – m ו-a חולקים נתונים. כתיבה דרך m[0, 0] = 99 משנה גם את a[0].

השמת tuple חדש ל-shape היא קיצור לאותה פעולה:

a = np.arange(9)
a.shape = (3, 3)

6.7.2. Transpose#

transpose() (או הקיצור .T) הופך את הצירים. ממומש על ידי היפוך הצעדים – שום נתון אינו מוזז:

m = np.arange(6, dtype=np.uint8).reshape((2, 3))
t = m.T                  # shape (3, 2), shares m's buffer

תצוגה משוחלפת אינה עוברת על בלוק הנתונים ברציפות. קריאת t לעיל שורה אחר שורה מבקרת במיקומי זיכרון 0, 3, 1, 4, 2, 5, ולא בסדר 0, 1, 2, 3, 4, 5 הבסיסי שבו הבתים פרוסים. חשבון רגיל וצמצומים מטפלים בזה בסדר – הם פוסעים דרך הצעדים – אך tobytes() אינו יכול, משום שהוא מחזיר את החוצץ הבסיסי ישירות מבלי להעתיק. הבתים שהחוצץ מחזיק אינם תואמים את הסדר שצורת התצוגה מרמזת עליו, ולכן המתודה מעוררת ValueError על כל תצוגה לא-רציפה. כאשר הבתים נדרשים בסדר המשוחלף, אלצו תחילה העתק רציף חדש:

bytes_out = t.copy().tobytes()

6.7.3. Flatten ו-flat#

flatten() מחזיר העתק חד-ממדי של המערך:

f = m.flatten()          # new dense 1-D ndarray

העבירו order='C' (ברירת מחדל) כדי לעבור על הציר האחרון תחילה או order='F' עבור הציר הראשון תחילה:

m = np.arange(6, dtype=np.uint8).reshape((2, 3))
# m = [[0, 1, 2],
#      [3, 4, 5]]
m.flatten()              # array([0, 1, 2, 3, 4, 5], dtype=uint8)
m.flatten(order='F')     # array([0, 3, 1, 4, 2, 5], dtype=uint8)

flat היא הצורה האיטרטיבית. היא מניבה כל איבר של ndarray מכל דרגה כסקלרים, מבלי להקצות העתק שטוח:

for x in m.flat:
    print(x)

כאשר היישום צריך לעבור על כל איבר, העדיפו את flat; כאשר הוא צריך חוצץ חד-ממדי צפוף להעביר לפונקציה אחרת, השתמשו ב-flatten().

6.7.4. איטרציה#

איטרציה על מערך חד-ממדי מניבה סקלרים; איטרציה על מערך מדרגה גבוהה יותר מניבה תצוגות מדרגה (n-1)

m = np.array([[0, 1, 2], [3, 4, 5]], dtype=np.uint8)
for row in m:
    print(row)               # array([0, 1, 2]), array([3, 4, 5])

השורות שמניבה איטרציה על מטריצה הן תצוגות, כך ששינוין משנה את המקור.

6.7.5. העתקים#

copy() היא הדרך המפורשת לקבל ndarray עצמאי ששינוייו אינם משפיעים על המקור. חוצץ חדש מוקצה והמקור נסרק לתוכו:

c = a.copy()

tobytes() מחזיר bytearray החולק זיכרון עם בלוק הנתונים של המערך. כתיבות דרך ה-bytearray משנות את המערך במקום. מעורר ValueError אם המערך אינו צפוף (תצוגה חתוכה, transpose, …).

tolist() מחזיר את התוכן כ-list של פייתון שעשוי להיות מקונן. שימושי לסריאליזציה של תוצאות קטנות; יקר עבור גדולות, משום שכל איבר הופך לאובייקט פייתון נפרד.

6.7.6. אילו פעולות מחזירות מה#

הכלל המלא:

הפעולות הבאות מחזירות תצוגות:

  • חיתוך – a[1:5], a[::2], m[:, 0];

  • אינדוקס לפי ציר יחיד של מערך מדרגה גבוהה יותר – m[0];

  • איטרציה על מערך n-ממדי;

  • reshape(), כאשר הפריסה המבוקשת תואמת;

  • transpose() / .T;

  • frombuffer();

  • asarray(), כאשר ה-dtype תואם.

הפעולות הבאות מחזירות העתקים:

השתמשו בהעתק מפורש רק כאשר באמת נדרש חוצץ עצמאי. על מצלמה עם RAM מוגבל, ההבדל בין תצוגה להעתק הוא לעיתים קרובות ההבדל בין קוד שנכנס לבין קוד שלא.