12 דברים שאתה חייב לדעת כשאתה עובד עם ספריית Numpy של Python
ספריית Numpy של Python מאפשרת לעבוד עם מערכים רב-מימדיים, ומספקת פונקציות מתמטיות לעבודה עם המערכים.
מערכים של Python דומים לרשימות בזה שהם מורכבים מרשימה של פריטים. ההבדל העיקרי הוא שכל פריטי המערך חייבים להיות מאותו סוג נתונים (int, float, string).
Numpy מבצעת חישובים מהר יותר מ-Python, ומציעה שפע של פונקציות שחוסכות לנו את הצורך לעבוד בתוך לולאות.
0. כיצד לייבא את numpy?
import numpy as np
1. כיצד יוצרים מערך של numpy
הפונקציה np.zeros() יוצרת מערך הכולל אפסים בלבד. לדוגמה, ניצור מערך של 3 אפסים:
a = np.zeros(3)
print(a)
והתוצאה:
array([0.,0.,0.])
לאיזה סוג שייך המערך?
type(a)
numpy.ndarray
הסוג הוא מערך של numpy.
מהו הסוג אליו שייכים פרטי המערך?
type(a[0])
numpy.float64
כל הפריטים של מערך numpy חייבים להיות שייכים לאותו סוג. במקרה זה, הסוג הוא float.
דרך חלופית ליצור מערך ולאכלס אותו באפסים היא באמצעות הפונקציה np.empty().
e = np.empty(3)
ניתן לייצר מערך של אחדים באמצעות הפונקציה np.ones(). ניצור מערך המונה 5 פריטים:
o = np.ones(5)
print(o)
array([1., 1., 1., 1., 1.])
ניתן לאכלס מערך בתוך טווח באמצעות np.linspace(start,stop,num_items). לדוגמה, מערך בטווח 0 עד 10 המכיל חמישה פריטים עם רווחים שווים ביניהם:
l = np.linspace(0,10,5)
print(l)
array([ 0. , 2.5, 5. , 7.5, 10. ])
וכמובן, ניתן לייצר מערך numpy ממערך רגיל:
n = np.array([10,20,30])
type(n)
numpy.ndarray
למה להפוך מערך רגיל של פייתון למערך של Numpy? בגלל שמהירות החישוב של מערכי Numpy גבוהה יותר והעבודה עם המערכים היא הרבה יותר נוחה הודות לפונקציות שחוסכות מאיתנו את הצורך לעבוד בתוך לולאות (עוד בנושא זה בהמשך המדריך).
2. כיצד כותבים מערך רב-מימדי?
a = np.array([[1, 2, 3] , [4, 5, 6]])
-
מערך זה מכיל 2 שורות ו- 3 עמודות, ואם נדפיס אותו נקבל ב-Jupyter notebook:
array([[1, 2, 3], [4, 5, 6]])
- ממש טבלה עם שורות ועמודות, ומכאן החובה להקפיד על מספר פריטים זהה בכל השורות.
- חובה להקפיד על סוג נתונים אחיד בכל הפריטים (כולם int בדוגמה שלנו).
- זה יעבוד גם עם float
ניתן לכתוב מערכים רב-מימדיים בדרגות מורכבות שונות, לדוגמה, מערך המכיל שורת נתונים אחת:
b = np.array([[1, 2, 3]])
שתי שורות:
c = np.array([[0, 1, 2], [3, 4, 5]])
שלוש שורות:
d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
וכיו"ב, כמה שורות שצריך.
3. כיצד לסקור את המערכים?
a.shape
התוצאה:
(2, 3)
מציג tuple עם מספר השורות והעמודות. קודם מספר השורות ורק אח"כ מספר העמודות.
(Nrows, Ncols)
a.size
התוצאה:
6
- 6 כמספר פריטי המערך.
בדיקה של סוג הנתונים של המערך:
type(a)
תיתן את התוצאה:
numpy.ndarray
- מערך רב-מימדי של numpy.
4. כיצד לגשת לפריטים בתוך המערך?
ניקח את המערך הבא בתור דוגמה:
v = np.array([1, 2, 3, 4, 5])
אנו יכולים לשלוף פריט מתוך המערך ע"פ האינדקס שלו. לדוגמה, ניתן לשלוף את הפריט הראשון:
print(v[0])
1
זוכרים? מחשבים מתחילים לספור מאפס.
כדי לשלוף את הפריט האחרון:
print(v[-1])
5
ניתן לשלוף טווח של פריטים:
print(v[1:3])
array([2, 3])
- שימו לב! השליפה כוללת את הפריט הראשון אבל לא את האחרון.
וניתן לשלוף את כל הטווח:
print(v[:])
array([1, 2, 3, 4, 5])
5. מהם סקלרים, וקטורים, מטריקסים וטנסורים?
סקלר הוא ערך מספרי בודד:
s = 11
וקטור הוא אובייקט שניתן לכתוב באמצעות מערך חד-מימדי בשורה אחת:
v = np.array([1, 2, 3])
מטריקס ניתן לתאר באמצעות מערך דו-מימדי.
מטריקס המורכב משני וקטורים:
m = np.array([[2, 3, 4], [5, 6, 7]])
אפשר לראות שהמטריקס מורכב משני וקטורים. כשנדפיס אותו נקבל את התוצאה הבאה:
array([[2, 3, 4], [5, 6, 7]])
המטריקס יכול להכיל יותר משני וקטורים, לדוגמה שלושה וקטורים:
m = np.array([[2, 3, 4], [5, 6, 7],[-1, 4, 6]])
array([[ 2, 3, 4], [ 5, 6, 7], [-1, 4, 6]])
טנסורים הם מערכים רב-מימדיים מסדר גבוה.
נדגים את זה:
נוסיף מטריקס m1 שיהיה באותה הצורה של המטריקס m:
m1 = np.array([[4, -2, 5], [2, 3, -1]])
נייצר משני המטריקסים טנסור באופן הבא:
t = np.array([m, m1])
נדפיס את הטנסור, ונקבל:
array([[[ 2, 3, 4], [ 5, 6, 7]], [[ 4, -2, 5], [ 2, 3, -1]]])
ברמה הטכנית, טנסור הוא אוסף של מטריקסים.
בלמידת מכונה טנסור הוא מערך רב מימדי.
נסכם:
מימדים | שם | תיאור |
---|---|---|
0 | סקלר | ערך בודד |
1 | וקטור | מערך |
2 | מטריקס | טבלה שטוחה |
n | טנסור | מערך בעל n מימדים |
בלמידת מכונה טנסור הוא מערך רב-מימדי.
6. כיצד לגשת לפריטים בתוך המערך הרב-ממדי?
לדוגמה, רוצים לגשת לפריט השני בשורה הראשונה:
a[0, 1] // 2
כדי לשנות את ערכו של פריט ניתן להציב לתוכו את הערך החדש:
a[0, 1] = 7
נדפיס את המערך לאחר השינוי:
array([[1, 7, 3], [4, 5, 6]])
כיצד לגשת לכל הפריטים של שורה מסויימת. לדוגמה, כל הפריטים בשורה הראשונה?
a[0, :]
array([1, 7, 3])
וניתן לגשת לכל הפריטים של עמודה מסוימת. לדוגמה, כיצד לגשת לכל הפריטים בעמודה השנייה בכל אחת מהשורות של המערך?
a[: , 1]
array([7, 5])
כדי לגשת לכל הפריטים בעמודה האחרונה:
a[: , -1]
array([3, 6])
אפשר להיות יותר ספציפי. לדוגמה, מערך המורכב משתי שורות ראשונות ושתי עמודות ראשונות:
a[0:2 , 0:2]
array[[1 2] [4 5]]
7. מערכים וסוגי נתונים
סוג הנתונים השימושי ביותר הוא float עשרוני. ניצור מערך של מספרים עשרוניים רציפים מ0 עד 4 :
x = np.array([0, 1, 2, 3], dtype=np.float16)
print(x)
מה סוג הנתונים?
print(x.dtype) # float16
- 16 - מציין באמצעות כמה ביטים בזיכרון מייצגים את המספר.
- אפשר גם 16, 32, 64, 128. ככל שהערך גבוה יותר מידת הדיוק עולה.
כדי להמיר. לדוגמה, לסוג מספרים שלמים int:
y = x.astype(int)
print(y) # [0 1 2 3]
print(y.dtype) # int64
8. מערכים ותנאים
כשעובדים עם מערכים של Numpy מאוד קל לעשות לוגיקה. לדוגמה, אילו פריטים במערך הבא גדולים מ-3.
z = np.array([8, -2, 13, 4, 1.2])
z > 3
התוצאה היא מערך של בוליאנים:
array([ True, False, True, True, False])
קל מאוד לשלוף את הפריטים שעומדים בתנאי:
z[z > 3]
הטכניקה נקראת mask, וזו התוצאה:
array([ 8., 13., 4.])
אפשר לשכלל את התנאי ע"י שימוש באופרטורים:
- | - אופרטור או.
- & - אופרטור גם.
נדגים את זה:
a = z[z>3] # [ 8. 13. 4.]
b = z[(z>3)&(z<9)] # [8. 4.]
c = z[(z<3)|(z>9)] # [-2. 13. 1.2]
9. כיצד לבצע פעולות חשבוניות על מערכים?
ניתן לבצע פעולות חשבוניות על מערכים.
הכפלה של מערך:
2 * a
array([[ 2, 14, 6], [ 8, 10, 12]])
יכפיל את הערך של כל אחד מהפריטים במערך פי - 2.
חיבור של מערכים:
a + b
array([[2, 9, 6], [5, 7, 9]])
יחבר כל אחד מהפריטים במערך a עם כל אחד מהפריטים המקבילים במערך השני (לדוגמה, הפריט הראשון בשורה הראשונה של a יחובר עם הפריט הראשון בשורה הראשונה של b, כנ"ל הפריט השני בשורה הראשונה, וכיו"ב כמספר הפריטים).
באותו אופן, ניתן לכפול בין מערכים:
a * b
array([[ 1, 14, 9], [ 4, 10, 18]])
וגם לחלק:
a / b
array([[ 1. , 3.5, 1. ], [ 4. , 2.5, 2. ]])
מכיוון שפעולות החשבון מתבצעות על פריטים מקבילים המבנה של המערכים חייב להיות זהה (מספר זהה של שורות ושל עמודות) .
10. שינוי צורה - reshape
אנחנו יכולים לשנות את הצורה של מערך באמצעות הפקודה reshape.
הצורה של מערך shape היא מספר הפרטים בכל אחד מהממדים שלו, וכשאנו משנים את הצורה של המערך אנחנו מוסיפים או מפחיתים ממדים, ומשנים את מספר הפריטים בממדים.
בדוגמה הבאה נמיר מערך של ממד אחד ובו 6 פריטים ל-2 ממדים ובכל אחד מהם 3 פריטים:
a = np.array([1, 2, 3, 4, 5, 6])
r = a.reshape(2, 3)
print(r)
[[1 2 3] [4 5 6]]
בדומה, אנחנו יכולים להפוך מערך חד-ממדי למערך בן שלושה ממדים:
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
r = a.reshape(2, 2, 3)
print(r)
[[[ 1 2 3] [ 4 5 6]] [[ 7 8 9] [10 11 12]]]
אנו יכולים לשנות את הצורה של המערך כמה שצריך כל עוד נקפיד שמספר הפריטים במערך המקורי והחדש יהיו זהים. בהתאם, יכולנו להפוך מערך חד ממדי שבו 6 פריטים למערך דו-ממדי המורכב משני מערכים שבכל אחד מהם 3 פריטים כי במערך החדש ישנם 2X3 פריטים. אותם שישה פריטים כמו במערך המקורי.
אם ננסה לשנות את המערך למספר לא תואם של פריטים נקבל שגיאה:
a = np.array([1, 2, 3, 4, 5, 6])
r = a.reshape(2, 4)
print(r)
ValueError: cannot reshape array of size 6 into shape (2,4)
יכול להיות מצב שבו נרצה שאת אחד הממדים תחשב הפונקציה reshape. את מקומו של הממד נסמן באמצעות -1.
a = np.array([1, 2, 3, 4, 5, 6])
r = a.reshape(2, 3, -1)
print(r)
[[[1] [2] [3]] [[4] [5] [6]]]
כדי לשטח מערך ולהפוך אותוו למערך בממד אחד נעביר לפונקציה reshape את הערך -1
a = np.array([[1,2,3],[4,5,6]])
r = a.reshape(-1)
print(r)
[1 2 3 4 5 6]
כדי להוסיף מימד למערך רב-מימדי אפשר להשתמש במתודה reshape():
x = np.arange(1,7)
print("original: ", x)
print("original shape: ", x.shape)
print("row vector: ", x.reshape((1, -1)))
print("row vector shape: ", x.reshape((1, -1)).shape)
original: [1 2 3 4 5 6] original shape: (6,) row vector: [[1 2 3 4 5 6]] row vector shape: (1, 6)
דרך אחרת מקובלת היא שימוש ב-np.newaxis. לדוגמה, כדי ליצור וקטור שורה row vector דו-ממדי:
x[np.newaxis, :]
[[1 2 3 4 5 6]]
- np.newaxis במקום בו צריך להתווסף המימד. במקרה זה המימד החדש הוא שורה.
- שתי נקודות : בשביל לציין את כל טווח העמודות.
או שניתן להוסיף מימד נוסף לעמודות וכך ליצור וקטור עמודה column vector דו-ממדי:
x[:, np.newaxis]
[[1] [2] [3] [4] [5] [6]]
11. טרנספוזיציה
וקטורים מתחלקים לשני סוגים: וקטורי שורה לעומת וקטורי עמודה. אפשר להשתמש בטרנספוזיציה כדי להפוך וקטור שורה לוקטור עמודה, והפוך.
כדי שפייתון יבצע טרנספוזיציה צריך להשתמש בתחביר הבא:
vector.T
אבל אם ננסה לעשות טרנספוזיציה סתם ככה על וקטור נגלה שזה לא עובד:
v = np.array([1, 2, 3])
v.T
array([1, 2, 3])
הסיבה היא שפייתון דורש שקודם נהפוך את הוקטור למערך רב-מימדי:
v_reshaped = v.reshape(1,3)
v_reshaped
ורק על המערך הרב מימדי נוכל לעשות את הטרנספוזיציה:
v_reshaped_transposed = v_reshaped.T
v_reshaped_transposed
array([[1], [2], [3]])
כפי שאפשר לראות, הערכים נשארים אבל המיקום שלהם משתנה.
באותה מידה שאפשר להפוך וקטור של שורה לוקטור של עמודה, אפשר גם הפוך:
v_reshaped_transposed_twice = v_reshaped_transposed.T
v_reshaped_transposed_twice
array([1, 2, 3])
טרנספוזיציה אפשר לעשות גם על מטריקסים:
n = np.array([[1, 2, 3], [4, 5, 6]])
print(n)
כך נראה המקור:
[[1 2 3] [4 5 6]]
נעשה טרנספוזיציה למטריקס:
print(n.T)
התוצאה:
[[1 4] [2 5] [3 6]]
- הטרנספוזיציה הופכת את מיקום הפריטים לאורך האלכסון הראשי. התוצאה היא שבכל פריט מתהפכים השורה והעמודה.
12. כיצד לעשות כפל מטריצות?
כפל מטריצות היא הפעולה הנפוצה ביותר בלמידת מכונה.
בתנאים מסוימים ניתן לכפול בין מערכים בעלי ממדים שונים באמצעות הפונקציה dot של numpy.
לדוגמה, נכפיל את הוקטור v במטריקס m:
v = np.array([1, 2, 3])
m = np.array([[2, 3, 4], [5, 6, 7], [1, 5, 8]])
np.dot(v, m)
array([20, 38, 35])
התנאי לכפל מטריצות היא שמספר העמודות במערך הימני יהיה שווה למספר השורות במערך השמאלי מכיוון שהמכפלה מתבצעת בין וקטורי השורה שמהם מורכב המערך הימני ווקטורי העמודה של המערך השמאלי.
התנאי להכפלה של מערכים היא שמספר העמודות במערך הימני יהיה שווה למספר השורות במערך השמאלי
בהתחשב בכך שתפקיד פעולת הטרנספוזיציה הוא להפוך וקטורים של שורה לוקטורים של עמודה והפוך, לפעמים נוכל להשתמש בטרנספוזיציה כתנאי מקדים לביצוע מכפלות. דרך נוספת היא שימוש בפעולת reshape של python:
לדוגמה:
m1 = np.array([[-5, 8], [2,1], [-7, 3]])
m2 = np.array([[2, 3], [6, -4], [7, 42]])
np.dot(m1,m2)
יגרום להודעת השגיאה הבאה:
ValueError Traceback (most recent call last)in () 2 m2 = np.array([[2, 3], [6, -4], [7, 42]]) 3 ----> 4 np.dot(m1,m2) ValueError: shapes (3,2) and (3,2) not aligned: 2 (dim 1) != 3 (dim 0)
כיוון שמספר העמודות במטריקס הימני שונה ממספר השורות במטריקס השמאלי.
הפתרון במקרה זה הוא לשנות את הצורה של המטריקס הימני כדי שיכיל 2 וקטורים של שורה שבכל אחד 3 ערכים, ורק אז לבצע את המכפלה:
m1 = np.array([[-5, 8], [2,1], [-7, 3]])
m1_reshape = m1.reshape(2,3)
m2 = np.array([[2, 3], [6, -4], [7, 42]])
np.dot(m1_reshape, m2)
array([[ 52, 37], [-19, 157]])
גם בדוגמה הבאה מספר העמודות במטריקס הימני שווה למספר השורות בשמאלי מה שמאפשר את כפל המטריצות:
x = np.zeros((1,2))
y = np.zeros((2,3))
np.dot(x,y)
* מי שלמד אלגברה לינארית עלול להתבלבל עם מכפלה סקלרית הידועה כ-dot product אלא שב-Numpy המתודה np.dot() מסיקה את המימדים באופן אוטומטי כדי לעשות כפל מטריצות (מטריקס X מטריקס), מכפלה סקלרית (וקטור X וקטור) או כפל מטריצה בוקטור.
13. איך לעשות חיתוך של מערך Numpy?
הכי קל להדגים חיתוך של מערך Numpy עם תמונה כי תמונה היא מערך בשלושה ממדים. אורך גובה וצבע.
נייבא את התמונה:
from skimage import io
photo = io.imread('donkey.jpg')
type(photo)
imageio.core.util.Array
מה צורתו של המערך?
photo.shape
(640, 960, 3)
נציג את התמונה:
import matplotlib.pyplot as plt
plt.imshow(photo)
נהפוך את השורות:
plt.imshow(photo[::-1])
נהפוך את העמודות:
plt.imshow(photo[:,::-1])
נעשה חיתוך ונציג רק חלק של התמונה.
plt.imshow(photo[100:600,200:600])
נציג כל שורה חמישית וכל עמודה חמישית.
plt.imshow(photo[::5,::5])
שילוב של טכניקת mask עם np.where ייתן את התוצאה הבא:
# use where and masked np array
masked_photo = np.where(photo > 100, 255, 0)
plt.imshow(masked_photo)
14. פעולות חשבוניות על כל אחד מפריטי המערך
בפייתון רגיל צריך לעבור עם לולאות על כל פריטי המערך כדי לבצע פעולות חשבוניות הבעיה היא שאם המערכים הם מספיק גדולים (שזה המצב בד"כ בלמידת מכונה) משך הביצוע הוא ארוך מאוד. Numpy מאפשר לנו לבצע את הפעולות החשבוניות בזמן קצר בהרבה באמצעות פונקציות UFuncs.
נעשה כמה פעולות חשבון על המערך הבא:
x = np.arange(5)
print(x)
array([0, 1, 2, 3, 4])
אנחנו יכולים להוסיף סקלר לכל פריט, לדוגמה:
print(x+3)
[3 4 5 6 7]
אפשר להפחית, לכפול, לחלק:
print("x - 3 = ", x-3)
print("x * 3 = ", x*3)
print("x / 3 = ", x/3)
x - 3 = [-3 -2 -1 0 1] x * 3 = [ 0 3 6 9 12] x / 3 = [0. 0.33333333 0.66666667 1. 1.33333333]
אפשר גם לחלק ולעגל כלפי מטה:
print("x // 3 = ", x//3)
אפשר לעשות מודולוס %, להעלות בחזקה ** ולהפוך את הסימן של כל פריטי המערכים:
print("x % 3 = ", x-3)
print("x ** 3 = ", x*3)
print("-x = ", -x)
x % 3 = [-3 -2 -1 0 1] x ** 3 = [ 0 3 6 9 12] -x = [ 0 -1 -2 -3 -4]
ואפשר גם לשלב את הפונקציות, לדוגמה:
print("-4(3x+2)**3 = ", -4*(3*x+2)**3)
-4(3x+2)**3 = [ -32 -500 -2048 -5324 -10976]
התחביר הפשוט שראינו לביצוע פעולות חשבון על מערכים של Numpy אינו הדרך היחידה. קיים תחביר מפורש יותר הקורא בשם לפונקציות. לדוגמה, כדי להוסיף למערך:
np.add(x, 3)
כדי להפחית:
np.subtract(x, 3)
וכדי לקבל ערך אבסולוטי:
np.absolute(x)
נחזור לתמונה.
אפשר להפעיל פונקציות טריגונומטריות. לדוגמה, פונקציית סינוס:
np.sin(photo)
התוצאה היא מערך ארוך מאוד. אתם מוזמנים לנסות.
או להפעיל פונקציות של סטטיסטיקה:
np.mean(photo)
92.74366753472222
סטיית תקן:
np.std(photo)
51.88792260858262
שונות סטטיסטית:
np.var(photo)
2042.3350509357754
ישנם פונקציות נוספות שכדאי להכיר.
סכום:
np.sum(photo)
170945128
מינימום ומקסימום:
np.min(photo) #0
np.max(photo) #255
פונקציה של Numpy למציאת מספר האינדקס של הערך המינימלי והמקסימלי:
np.argmin(photo) #904526
np.argmax(photo) #324854
לכל המדריכים בנושא של למידת מכונה
אהבתם? לא אהבתם? דרגו!
0 הצבעות, ממוצע 0 מתוך 5 כוכבים
המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.
למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.
שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.
המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?
השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.