עריכת טבלאות מתקדמת

שימו לב: על מנת להריץ את התאים ב-Live Code, יש לייבא תחילה את ספרית pandas ע”י הרצת השורת הראשונה בתא למטה.
בנוסף, נגביל את מספר השורות והעמודות שתופענה בהדפסת הטבלאות ע”י שורות הקוד השניה והשלישית:

import pandas as pd
pd.options.display.max_rows=5
pd.options.display.max_columns=5

עריכת טבלאות מתקדמת#

בפרק זה נעמיק עוד את היכרותנו עם פעולות העריכה בpandas ונלמד שתי טכניקות מתקדמות יותר לעבודה עם טבלאות. הראשונה היא שימוש במיסוך (Masking), המאפשר לסנן ולעדכן נתונים בטבלה לפי תנאים מסוימים.

לאחר מכן נכיר את המתודה החזקה apply, המאפשרת לבצע חישובים מורכבים או ליצור עמודות חדשות מבלי להשתמש בלולאות מפורשות, על-ידי הפעלת פונקציה על שורות או עמודות שלמות.

הפעולות להלן יודגמו טבלת הציונים "files/StudentsGrades.csv":

inputFileName = "files/StudentsGrades.csv"
df = pd.read_csv(inputFileName)
display(df)
Name Programming ... Planet Survival Art
0 Yael 50 ... 65 91
1 Nadav 61 ... 52 88
... ... ... ... ... ...
11 Tom 98 ... 92 80
12 Adi 76 ... 84 70

13 rows × 8 columns

מיסוך (Masking) בpandas#

כעת נבחן כמה דוגמאות לשימוש ב-Masking על DataFrame:

נניח כי נרצה להסיר את כל הסטודנטיות ששמן יעל.

תחילה ניצור מסיכה:

msk=df['Name'] != 'Yael'
display(msk)
0     False
1      True
      ...  
11     True
12     True
Name: Name, Length: 13, dtype: bool

ולאחר מכן נסנן את הטבלה:

display(df[msk])
Name Programming ... Planet Survival Art
1 Nadav 61 ... 52 88
2 Michal 81 ... 81 78
... ... ... ... ... ...
11 Tom 98 ... 92 80
12 Adi 76 ... 84 70

12 rows × 8 columns

ניתן גם להשאיר רק סטודנטיות ששמן יעל באמצעות יצירת מסיכה של התנאי ההפוך.

נראה כיצד לעשות זאת בשורה אחת בלבד:

df[df['Name'] == 'Yael']
Name Programming ... Planet Survival Art
0 Yael 50 ... 65 91

1 rows × 8 columns

בדומה לאופרטורים אריתמטיים, ניתן להפעיל פעולות and, or ו-not עבור סדרות וטבלאות בוליאניות (או במילים אחרות, עבור מסיכות).
אך בשונה מערכים בוליאניים רגילים, כאן לאופרטורים אלו יש סימון אחר:

  • and –> &

  • or –> |

  • not –> ~

נדגים זאת באמצעות החזרת כל הסטודנטים שקיבלו מעל 70 מתמטיקה ומעל 90 באומנות:

display(df[(df['Math'] > 70) & (df['Art'] > 90)])
Name Programming ... Planet Survival Art
6 Yarden 63 ... 98 94
7 Avi 78 ... 76 100

2 rows × 8 columns

שימו לב

בpandas האופרטורים הלוגיים & | ~ קודמים להשוואתיים (כמו < > ==). לכן חשוב לעטוף כל מסיכה בסוגריים על מנת למנוע מהאופרטור הלוגי לפעול לפני האופרטור ההשוואתי.

באמצעות Masking ניתן גם לבצע השמה מותנית, כלומר, לבצע השמה רק במקומות בהם יש במסיכה True.

לדוגמא, בשורה הבאה “נעביר” את כל הסטודנטים שנכשלו בתכנות, וניתן להם את הציון 60 (אתם יכולים לראות שהציון של יעל התחלף מ50 ל60).

df.loc[df["Programming"] < 60, "Programming"] = 60
display(df)
Name Programming ... Planet Survival Art
0 Yael 60 ... 65 91
1 Nadav 61 ... 52 88
... ... ... ... ... ...
11 Tom 98 ... 92 80
12 Adi 76 ... 84 70

13 rows × 8 columns

מתודת apply#

המתודה apply מאפשרת להפעיל פונקציה על עמודה או על כל שורה ב-DataFrame. במקום להשתמש בלולאות, apply מאפשרת לנו לבצע חישוב או שינוי על כל השורות או העמודות של הטבלה בבת אחת.
באופן דומה, עבור סדרה, apply תבצע את הפעולה על כל ערכים בסדרה בבת אחת.

לדוגמא, אם נרצה להעלות את כל הציונים בעמודה Art ב10, אבל לוודא שלא יהיו ציונים גבוהים מ100, נוכל לעשות זאת באופן הבא:

df["Art"]=df["Art"].apply(lambda a: min(100, a+10))

display(df)
Name Programming ... Planet Survival Art
0 Yael 60 ... 65 100
1 Nadav 61 ... 52 98
... ... ... ... ... ...
11 Tom 98 ... 92 90
12 Adi 76 ... 84 80

13 rows × 8 columns

בדומה לחישוב ממוצע המקצועות שראינו קודם, גם לapply, יש פרמטר בשם axis:
בברירת מחדל, apply מופעל עם הקלט axis=0. כלומר, מתבצעת פעולה בין השורות השונות, או במילים אחרות - על כל עמודה בנפרד. ולהיפך, הפעלת apply עם axis=1 תבצע פעולה בין העמודות השונות, או במילים אחרות - על כל שורה בנפרד.

נדגים זאת באמצעות ממוצע באמצעות apply:

display(df.iloc[:,1:].apply(lambda a: a.mean(), axis=1))
0     71.142857
1     71.571429
        ...    
11    93.142857
12    77.000000
Length: 13, dtype: float64

שימו לב שapply מחלץ ממוצע עבור כל סטודנט בנפרד (כלומר, עבור כל שורה), והתוצאה היא סדרה במבנה על עמודה בטבלה, בה קיים ממוצע כל סטודנט.

מה היה קורה אם היינו מפעילים את אותה השורה עם axis=0?

היינו מחלצים ממוצע עבור כל מקצוע בנפרד (כלומר, עבור כל טור), מה שהיה מסתכם לכדי שורה חדשה (ללא עמודת שם הסטודנט, אותה חתכנו באמצעות iloc).

נחזור למשימת הוספת פקטור של 10 נקודות. מה אם במקום להוסיף זאת רק עבור אומנות, היינו מוסיפים זאת לכלל המקצועות?
היינו חושבים אולי לבצע משהו כזה:

(df.iloc[:,1:]=df.iloc[:,1:].apply(lambda a: min(100, a+10)

אבל מכיוון שאנו פועלים כעת על טבלה (ולא על סדרה כמו בדוגמא הראשונה), a בפונקציית הlambda ייצג שורה שלמה. כלומר פעולת המינימום לא תתבצע עבור כל תא בנפרד, כפי שהיינו רוצים.

לכן, נעזר במתודה clip של pandas המאפשרת לקטום ערכים בכל תא בנפרד. במקרה שלנו, היינו צריכים להגביל את הערכים רק מלמעלה, ולכן נשתמש בפרמטר upper=100.

כעת המימוש יראה כך:

df.iloc[:,1:].apply(lambda a: a+10).clip(upper=100)
Programming Marine Biology ... Planet Survival Art
0 70 66 ... 75 100
1 71 87 ... 62 100
... ... ... ... ... ...
11 100 86 ... 100 100
12 86 97 ... 94 90

13 rows × 7 columns

הערה

במקרה הזה, אין חשיבות לפרמטר axis בapply מכיוון שהפעולה נעשית בסופו של דבר על כל ערך בנפרד, והם אינם “נסכמים” ביחד.

תרגיל#

בשורה אחת בלבד, שנו באמצעות המתודה apply את שמות הסטודנטים כך שכולם יהיו באותיות גדולות.

# Write your code here
display(df)