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 תואם.

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

  • copy();

  • flatten();

  • אינדוקס בוליאני – a[mask];

  • חשבון – a + b, a * 2, np.sin(a);

  • array() – תמיד מעתיק, גם ממערך אחר;

  • concatenate().

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