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;asarray(), כאשר ה-dtype תואם.
הפעולות הבאות מחזירות העתקים:
אינדוקס בוליאני –
a[mask];חשבון –
a + b,a * 2,np.sin(a);array()– תמיד מעתיק, גם ממערך אחר;
השתמשו בהעתק מפורש רק כאשר באמת נדרש חוצץ עצמאי. על מצלמה עם RAM מוגבל, ההבדל בין תצוגה להעתק הוא לעיתים קרובות ההבדל בין קוד שנכנס לבין קוד שלא.