6.12. בחירה וסידור מחדש¶
צמצומים קיפלו מערך לכדי סקלר או תוצאה בעלת דרגה נמוכה יותר. בחירה מכסה את הפעולות שבוחרות אילו איברים שורדים והיכן הם מסתיימים: בחירה מותנית, גזירה (clipping), מיון, חיפוש אינדקסים וסידור מחדש לאורך ציר.
6.12.1. בחירה מותנית¶
where() מחזירה מערך הלוקח איברים מ-x היכן שהתנאי אמיתי ומ-y אחרת. שלושת האופרנדים מתפשטים יחד:
a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.where(a < 3, a, 0.0)
# array([1.0, 2.0, 0.0, 0.0, 0.0])
זהו הכלי הנכון עבור ”if/else לכל איבר“ מבלי לכתוב לולאת Python.
clip() היא קיצור עבור maximum(lo, minimum(a, hi)) – רוויית הערכים לטווח:
np.clip(a, 2.0, 4.0)
# array([2.0, 2.0, 3.0, 4.0, 4.0])
maximum() ו-minimum() לוקחות שני אופרנדים ומחזירות את הגדול / הקטן יותר לכל איבר:
np.maximum(a, 3.0)
np.minimum(a, np.array([5, 4, 3, 2, 1]))
6.12.2. מציאת אינדקסים¶
nonzero() מחזירה את הקואורדינטות של כל איבר שאינו אפס, מפוצלות למערך אינדקסים אחד לכל ממד. עבור קלט דו-ממדי התוצאה היא tuple של שני מערכים: הראשון מחזיק את אינדקסי השורות, השני מחזיק את אינדקסי העמודות. שילובם עמודה-מול-עמודה נותן את ה-(row, col) של כל מיקום שאינו אפס:
m = np.array([[0, 2, 0],
[3, 0, 0]], dtype=np.float)
np.nonzero(m)
# (array([0, 1], dtype=uint16), array([1, 0], dtype=uint16))
האיברים שאינם אפס ב-m הם m[0, 1] = 2 ו-m[1, 0] = 3. המערך הראשון המוחזר [0, 1] נותן את אינדקסי השורות שלהם; השני [1, 0] נותן את אינדקסי העמודות שלהם. קריאת שני המערכים זה לצד זה משחזרת את המיקומים (0, 1) ו-(1, 0).
שני צמצומים מייצרים גם הם אינדקסים:
argsort()– מערך מספרים שלמים שהיה ממיין את הקלט לאורך הציר הנתון (ברירת המחדל היא האחרון):a = np.array([40, 10, 30, 20], dtype=np.uint8) idx = np.argsort(a) # array([1, 3, 2, 0], dtype=uint16) a[idx] # array([10, 20, 30, 40])
argsortתמיד מחזירuint16; לכן המערך הממוין חייב להכיל לא יותר מ-65,535 איברים בציר הממוין.
bincount() סופר את מספר ההופעות של כל מספר שלם אי-שלילי בקלט חד-ממדי מסוג uint8 / uint16
histogram = np.bincount(np.array([0, 1, 1, 2, 2, 2], dtype=np.uint8))
# array([1, 2, 3], dtype=uint16)
שימושית לבניית היסטוגרמות של ערכי פיקסל קטנים-בערכם מבלי לכתוב לולאת Python.
6.12.3. מיון וסידור מחדש¶
sort() מחזירה עותק ממוין של המערך לאורך הציר הנתון (האחרון כברירת מחדל). השתמש ב-sort() על המערך עצמו עבור גרסה במקום (in-place):
np.sort(np.array([3, 1, 2], dtype=np.float))
# array([1.0, 2.0, 3.0])
flip() הופכת את הסדר לאורך הציר הנתון (כל ציר כאשר לא מועבר axis):
np.flip(np.array([1, 2, 3, 4]))
# array([4, 3, 2, 1])
roll() מסיטה איברים באופן מחזורי במספר הנתון. שימושית למימוש אוגר הזחה בסגנון חוצץ-טבעתי (ring-buffer):
np.roll(np.array([1, 2, 3, 4]), 1)
# array([4, 1, 2, 3])
take() היא הצורה המפורשת של אינדוקס מפואר (fancy indexing) – בחירת איברים באינדקסים שרירותיים:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.take(a, [0, 2, 4])
# array([10, 30, 50], dtype=uint8)
6.12.4. סינון ועריכות מבניות¶
compress() היא הצורה המפורשת של אינדוקס בוליאני – מחזירה את הפרוסות של a שנבחרו על ידי התנאי הבוליאני:
a = np.array([10, 20, 30, 40], dtype=np.uint8)
np.compress(a > 15, a)
# array([20, 30, 40], dtype=uint8)
delete() מחזירה עותק שבו האיברים באינדקסים הנתונים הוסרו:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.delete(a, [1, 3])
# array([10, 30, 50], dtype=uint8)
diff() מחזירה את ההפרש הקדמי הבדיד מסדר n של המערך לאורך ציר. משמשת לחישוב שינויים מסדר ראשון בין דגימות סמוכות:
samples = np.array([1, 3, 6, 10, 15], dtype=np.float)
np.diff(samples)
# array([2.0, 3.0, 4.0, 5.0])
6.12.5. מה עולה כל פעולה¶
כמעט כל פונקציה בעמוד זה מחזירה מערך שהוקצה מחדש. שני יוצאים מן הכלל:
sort()ממיינת במקום (in-place); הפונקציה החופשיתsort()מחזירה עותק ממוין.take()מקבלת מילת מפתחout=לכתיבה לתוך חוצץ (buffer) שכבר קיים.
בלולאה שרצה פעמים רבות בשנייה, העדף את sort() שפועלת במקום ועשה שימוש חוזר בחוצצים שהוקצו מראש בכל מקום אחר. מסכות בוליאניות עצמן מוקצות בכל פעם שההשוואה רצה – בנה מסכה פעם אחת ועשה בה שימוש חוזר על פני פעולות במקום לבנותה מחדש בתוך כל איטרציה.