ברוכים הבאים ליחידה הראשונה בנושא ניתוח נתונים עם Pandas! ספריית Pandas היא אבן יסוד בעולם ניתוח הנתונים בפייתון, ומספקת מבני נתונים עוצמתיים וכלים גמישים לעבודה עם נתונים מובנים. ביחידה זו נתמקד בשני מבני הנתונים הבסיסיים והחשובים ביותר של Pandas: Series ו-DataFrame. נלמד כיצד ליצור אותם, לגשת לנתונים בתוכם באמצעות אינדוקס מתקדם, וכיצד לטפל באחד האתגרים הנפוצים ביותר בניתוח נתונים – ערכים חסרים. הבנה מעמיקה של נושאים אלו חיונית להצלחה בקורס ובעבודה מעשית עם נתונים.
מבוא למבני נתונים ב-Pandas
Pandas בנויה על גבי ספריית NumPy ומספקת שכבת הפשטה עשירה יותר, המאפשרת טיפול נוח ויעיל בנתונים טבלאיים וסדרתיים. במקום לעבוד ישירות עם מערכי NumPy חסרי תווית, Pandas מציגה אינדקסים (תוויות) המקלים על הבנה, גישה ומניפולציה של נתונים.
Series
מבנה חד-ממדי. דומה לעמודה אחת. לכל ערך יש אינדקס (תווית) ייחודי. יכול להכיל סוג נתונים אחיד (לרוב).
DataFrame
מבנה דו-ממדי. דומה לטבלה. מורכב מעמודות (Series) ושורות. לכל שורה ולכל עמודה יש אינדקס (תווית).
יצירה ואינדוקס של Series ו-DataFrame
יצירת Series
- מתוך רשימה:
pd.Series([10, 20, 30])- יקבל אינדקס מספרי ברירת מחדל. - מתוך רשימה עם אינדקס מוגדר:
pd.Series([10, 20, 30], index=['a', 'b', 'c']). - מתוך מילון:
pd.Series({'a': 10, 'b': 20})- מפתחות המילון הופכים לאינדקס. - מתוך סקלר:
pd.Series(5, index=['x', 'y', 'z'])- יצור Series עם ערך 5 בכל המקומות.
יצירת DataFrame
- מתוך מילון של רשימות/Series: הדרך הנפוצה ביותר. מפתחות המילון הופכים לשמות העמודות.
data = {'שם': ['איתי', 'נועה'], 'גיל': [25, 30]} df = pd.DataFrame(data) - מתוך רשימה של מילונים: כל מילון מייצג שורה.
data = [{'שם': 'איתי', 'גיל': 25}, {'שם': 'נועה', 'גיל': 30}] df = pd.DataFrame(data) - מתוך מערך NumPy דו-ממדי: יש לספק שמות עמודות ואינדקס שורות בנפרד.
אינדוקס וגישה לנתונים
גישה לנתונים היא קריטית. Pandas מציעה מספר דרכים, כאשר loc ו-iloc הן החשובות ביותר.
- Series:
- גישה לפי אינדקס (תווית):
s['a'] - גישה לפי מיקום מספרי:
s[0] - חיתוך (slicing):
s[0:2]אוs['a':'c'](כולל את הקצה הימני בתוויות!)
- גישה לפי אינדקס (תווית):
- DataFrame:
- בחירת עמודה:
df['שם']אוdf.שם(אם שם העמודה חוקי). מחזיר Series. - בחירת מספר עמודות:
df[['שם', 'גיל']]. מחזיר DataFrame.
- בחירת עמודה:
.loc ל-.iloc: זהו נושא קריטי לבחינה ולעבודה מעשית!
.locמשמש לגישה מבוססת תוויות (labels). הוא כולל את הקצה הימני בחיתוך..ilocמשמש לגישה מבוססת מיקום מספרי (integer-location). הוא אינו כולל את הקצה הימני בחיתוך, בדומה לחיתוך ברשימות פייתון.
דוגמה: אם יש DataFrame עם אינדקס ['a', 'b', 'c']:
df.loc['a':'b']יחזיר שורות עם תוויות 'a' ו-'b'.df.iloc[0:2]יחזיר שורות במיקומים 0 ו-1 (לא כולל 2).
טיפול בערכים חסרים (Missing Values)
נתונים בעולם האמיתי כמעט תמיד מכילים ערכים חסרים, המיוצגים ב-Pandas לרוב כ-NaN (Not a Number) עבור נתונים מספריים, או None עבור אובייקטים.
זיהוי ערכים חסרים
df.isnull(): מחזיר DataFrame בוליאני המציין היכן יש ערכים חסרים (True).df.notnull(): ההפך מ-isnull().df.isnull().sum(): סופר את מספר הערכים החסרים בכל עמודה.
הסרת ערכים חסרים
df.dropna(): מסיר כל שורה המכילה לפחות ערך חסר אחד.df.dropna(axis=1): מסיר כל עמודה המכילה לפחות ערך חסר אחד.df.dropna(how='all'): מסיר רק שורות (או עמודות, עםaxis=1) שכל ערכיהן חסרים.df.dropna(thresh=N): משאיר רק שורות (או עמודות) שיש להן לפחות N ערכים לא חסרים.- זכרו להשתמש ב-
inplace=Trueכדי לשנות את ה-DataFrame המקורי, אחרת הפונקציה מחזירה DataFrame חדש.
מילוי ערכים חסרים
df.fillna(0): ממלא את כל הערכים החסרים ב-0.df.fillna(df.mean()): ממלא ערכים חסרים בכל עמודה בממוצע של אותה עמודה (עבור עמודות מספריות).df.fillna(method='ffill'): ממלא ערכים חסרים עם הערך הלא-חסר הקודם (forward fill).df.fillna(method='bfill'): ממלא ערכים חסרים עם הערך הלא-חסר הבא (backward fill).- גם כאן,
inplace=Trueמשנה את ה-DataFrame המקורי.
שאלות לדיון
- הסבירו את ההבדל המהותי בין אובייקט
SeriesלאובייקטDataFrameב-Pandas, ומתי נכון להשתמש בכל אחד מהם. - בהינתן
DataFrameבשםdf, תארו את ההבדל בשימוש ובתוצאה ביןdf.loc[0:5]לביןdf.iloc[0:5]. מתי תעדיפו להשתמש בכל אחת מהשיטות? - כיצד תזהו ותטפלו בערכים חסרים בעמודה ספציפית ב-
DataFrame, למשל, עמודה בשם 'גיל', אם ברצונכם למלא אותם בערך החציוני של אותה עמודה? כתבו קוד לדוגמה. - מהי המשמעות של הפרמטר
axisבפונקציות כמוdropna()אוmean()ב-Pandas, וכיצד הוא משפיע על הפעולה?
נקודות לתשובת מודל
- Series vs. DataFrame: Series הוא חד-ממדי עם אינדקס, מתאים לעמודה בודדת. DataFrame הוא דו-ממדי, אוסף של Series עם אינדקס שורות ועמודות, מתאים לטבלאות. בחירה תלויה במבנה הנתונים (עמודה בודדת מול טבלה).
- loc vs. iloc:
locמשתמש בתוויות (labels) וכולל את הקצה הימני בחיתוך.ilocמשתמש במיקומים מספריים (integers) ואינו כולל את הקצה הימני.locעדיף כאשר האינדקס בעל משמעות סמנטית (תאריכים, שמות),ilocכאשר רוצים גישה לפי מיקום מוחלט. - טיפול בערכים חסרים:
- זיהוי:
df['גיל'].isnull().sum(). - מילוי:
median_age = df['גיל'].median(), ואזdf['גיל'].fillna(median_age, inplace=True).
- זיהוי:
- הפרמטר axis: קובע אם הפעולה תתבצע לאורך השורות (
axis=0, ברירת מחדל) או לאורך העמודות (axis=1). לדוגמה,df.dropna(axis=0)מסיר שורות, בעודdf.dropna(axis=1)מסיר עמודות.df.mean(axis=0)מחשב ממוצע לכל עמודה,df.mean(axis=1)לכל שורה.