הפקולטה לפיזיקה, הטכניון. חורף 2013
מרצה: רונן אברבנאל
כבר ראינו כמה פעולות פשוטות עם מערכים של numpy אבל בכל זאת, נרצה להדגיש את היתרונות שלהם על פני טיפוסי הנתונים הרגילים של פייתון:
import numpy as np
חד ממדי:
a = np.array([0,1,2,3])
a
a.ndim
a.shape
len(a)
ממד גבוהה יותר:
b = np.array([[0,1,2],
[3,4,5]])
b
b.ndim
b.shape
len(b)
מחזיר רק את הממד הראשון!
c = np.array( [[[1],[2]],[[3],[4]]])
c
c.shape
בדרך כלל, זה לא כל כך כיף להכניס מידע למערכים בזה אחר זה. אז יש פנקציות שעושות את זה:
a = np.arange(10)
a
b = np.arange(1,9,2)
b
c = np.linspace(0,1,6)
c
d = np.linspace(0,1,5,endpoint=False)
d
a = np.ones((3,4))
a
b = np.zeros((2,3))
b
c = np.eye(3)
c
d = np.diag([1,2,3,4])
d
a = np.random.rand(4) #uniform in [0,1]
a
b = np.random.randn(3,4) # Normal distribution
b
בדרך כלל, טיפוסי הנתונים של פייתון הם משתמעים. כאשר עושים נומריקה, לפעמים נחמד להגדיר אותם במפורש.
עבור מעיף פייתוני, מוגדר "טיפוס הנתונים של המערך", dtype.
a = np.array([1,2,3])
a.dtype
b = np.array([1.,2.,3.])
b.dtype
אפשר גם להגדיר מפורשות:
c = np.array([1,2,3,], dtype=float)
c.dtype
סוגים נוספים במתשמע:
d = np.array([1+2j, 3+4j, 5+6*1j])
d.dtype
e = np.array([True, False, False, True])
e.dtype
f = np.array(['Bonjour', 'Hello', 'Hallo',])
f.dtype # <--- strings containing max. 7 letters
צורת הגישה לאיברים במערך של numpy שונה במעט מצורת הגישה לאיברים במערך (או: רשימה) פייתוני רגיל, בפרט, במערך רב ממדי, אם כי צורת הגישה הסטנדרטית עובדת גם היא.
a = np.diag(np.arange(3))
a
a[1, 1]
a[2, 1] = 10 # third line, second column
a
a[1]
נשים לב שבדו ממד, הממד הראשון מתייחס לשורות והשני לעמודות.
פריסה של מערך, כמו עבור רשימות:
a = np.arange(10)
a
a[2:9:3] # [start:end:step]
slicing
אם מציבים ערכים לתוך "חלק" ממערך, זה עובד יופי,
a
a[5:] = 10
a
b = np.arange(5)
a[5:] = b[::-1]
a
המושג המקצועי כאן הוא fancy indexing
מעבירים מערך אם אותו אורך כמו המערך המקורי, כך שערכי המערך הם True או False. מקבלים רק את האיברים מהמערך המקורי עבורם העברנו True.
np.random.seed(3)
a = np.random.random_integers(0,20,15)
a
mask = (a%3 == 0)
mask
כלומר, קיבלנו מערך המתאים למערך המקורי, אבכל מקום שבו במערך המקורי יש מספר שמתחלק ב-3, במסכה יש True.
extract_from_a = a[mask]
extract_from_a
אפשר גם לעשות את זה ישירות, ואפילו להציב מספרים.
נציב \(-1\) בכל המספרים שמתחלקים ב-3
a[a%3 == 0] = -1
a
np.random.seed(3)
b = np.random.random_integers(0, 20, (8,2))
b
b[b[:,0]>10,1] = -1
b
הצבנו 1 בעמודה השניה, בכל מקום בו העמודה הראשונה גדולה מ-10!
a = np.arange(0, 100, 10)
a
a[[2, 3, 2, 4, 2]] # note: [2, 3, 2, 4, 2] is a Python list
ניתן להציב גם ערכים חדשים:
a[[9, 7]] = -100
a
ניתן לבצע פעולות עם סקאלרים
a = np.array([1, 2, 3, 4])
a + 1
a**2
ולבצע פעולות אריתמטיות בין מערכים (איבר-איבר)
b = np.ones(4) + 1
a - b
a * b
j = np.arange(5)
2**(j + 1) - j
במקרה זה, חשוב לשים לב שהמערכים הם מאותו גודל!
פעולות אלו מהירות בהרבה מאשר ביצוע פעולות איבר-איבר על מערך באמצעות לולאות.
חשוב להבדיל בין כפל-מטריצות שמתבצע באמצעות dot,
לבית כפל איבר-איבר
c = np.ones((3,3))
c*c
c.dot(c)
השוואות
a = np.array([1, 2, 3, 4])
b = np.array([4, 2, 2, 4])
a == b
a > b
פעולות לוגיות
a = np.array([1, 1, 0, 0], dtype=bool)
b = np.array([1, 0, 1, 0], dtype=bool)
np.logical_or(a, b)
np.logical_and(a, b)
פונקציות שונות
a = np.arange(10)
np.sin(a)
np.log(a)
np.exp(a)
ושוב, הגודל חייב להיות תואם:
a = np.arange(4)
a + np.array([1, 2])
שחלוף:
a = np.triu(np.ones((3, 3)), 1)
a
a.T
x = np.array([1, 2, 3, 4])
np.sum(x)
x.sum()
במערכים רב ממדיים, ניתן לצמצם לפי ציר ספציפי,
x = np.array([[1, 1], [2, 2]])
x
x.sum(axis=0) # columns (first dimension)
x[:, 0].sum(), x[:, 1].sum()
x.sum(axis=1) # rows (second dimension)
x[0, :].sum(), x[1, :].sum()
אבל גם,
y = np.array([[1, 2, 3], [5, 6, 1]])
y.mean(0)
y.std(0)
y.min()
y.min(0)
ועוד
שיטוח
a = np.array([[1, 2, 3], [4, 5, 6]])
a.ravel()
a.T
a.T.ravel()
שינוי צורה
a.shape
b = a.ravel()
b = b.reshape((2, 3))
b
a.reshape((2, -1)) # unspecified (-1) value is inferred
שינוי גודל
a = np.arange(4)
a.resize((8,))
a
אבל אם המערך הוא הפניה למערך אחר, זה לא יעבוד:
b = a
a.resize((4,))