מה זה?

נתונים אמיתיים (לא הדוגמאות הנקיות ששימשו עד עכשיו) כמעט תמיד “מלוכלכים” — ערכים חסרים (NaN), עמודות עם שמות לא-עקביים ("cust_name" לעומת "CustomerName"), תאריכים שנשמרו כטקסט חופשי. עיבוד/ניתוח על נתונים כאלה בלי ניקוי קודם מייצר תוצאות שגויות בשקט — בדיוק כמו ה--1 המסוכן ל"ערך ריק" ב-C# (מיחידת ה-Nullable). ניקוי נתונים הוא תמיד השלב שקודם לכל ניתוח אמיתי.

מילות מפתח שחשוב לזכור

• NaN (Not a Number) — הייצוג של Pandas ל"ערך חסר", מקביל ל-null/None

• fillna() — ממלא ערכים חסרים בערך ברירת מחדל (0, ממוצע, מחרוזת קבועה)

• dropna() — מסיר שורות (או עמודות) עם ערכים חסרים לחלוטין

• rename() — משנה שמות עמודות לעקביים

• to_datetime() — ממיר עמודת טקסט/מספר לטיפוס תאריך אמיתי, מאפשר פעולות תאריך (מיון כרונולוגי, חישוב הפרשים)

# NaN — actual missing values
df.isna().sum()          # how many missing values are in each column — always check this first

# fillna — filling with a default value
df["Score"] = df["Score"].fillna(0)                    # a fixed value
df["Age"] = df["Age"].fillna(df["Age"].mean())          # the column's mean — more common in practice
# dropna — removing rows with missing values
df_clean = df.dropna()                        # removes a row if **every** column in it is missing something
df_clean = df.dropna(subset=["Email"])         # removes only if Email specifically is missing
# rename — consistent column names
df = df.rename(columns={"cust_name": "CustomerName", "amt": "Amount"})

# to_datetime — converting to a real date type
df["OrderDate"] = pd.to_datetime(df["OrderDate"])   # from "2026-01-15" (text) to a date type
df_sorted = df.sort_values("OrderDate")             # now a real chronological sort, not by text

הסבר עיקרי

fillna מול dropna — בחירה עסקית, לא רק טכנית — dropna מוחק מידע (השורה כולה נעלמת), מתאים כשמעט שורות מושפעות ואי אפשר “לנחש” ערך הגיוני. fillna שומר את השורה ומחליף רק את הערך החסר — מתאים כשיש דרך הגיונית להשלים (ממוצע, 0, “לא ידוע”) בלי לאבד מידע אחר בשורה. הבחירה תלויה בהקשר העסקי, לא בכלל אוטומטי.

to_datetime הוא לא “עוד המרת טיפוס” — בלי המרה, עמודת תאריכים שמורה כטקסט ממוינת לקסיקוגרפית (“2026-1-5” לפני “2026-12-1” למרות שכרונולוגית זה הפוך) — בעיה שקטה ומסוכנת. אחרי to_datetime, sort_values על אותה עמודה ממיין באמת כרונולוגית, ואפשר גם לחשב הפרשי זמן (df["Date"].max() - df["Date"].min()).

למה rename חשוב לפני שילוב מקורות — כשמאחדים כמה DataFrames ממקורות שונים (כמו בפרויקט ה-ETL הקרוב), כל אחד לרוב מגיע עם מוסכמת שמות שונה. rename מאחד אותם לשמות עקביים לפני merge/groupby — אחרת merge(on="CustomerId") פשוט נכשל בשקט אם עמודה אחת נקראת customer_id.

יתרונות

fillna/dropna נותנים שליטה מפורשת בטיפול בנתונים חסרים, במקום שהם “יזהמו” חישובים בשקט; to_datetime מאפשר מיון וחישובי תאריך נכונים; rename מבטיח עקביות לפני שילוב מקורות.

חסרונות

fillna עם ערך שגוי (למשל 0 כשזה לא הגיוני עסקית) עלול לעוות תוצאות בדיוק כמו הבעיה שניסה לפתור; dropna שמסיר יותר מדי שורות עלול לאבד מידע רב אם לא נבדק היקף ההשפעה קודם (isna().sum()).

נקודות חשובות

• NaN = “ערך חסר” ב-Pandas, מקביל ל-null ב-C#

• fillna שומר שורה וממלא ערך; dropna מסיר שורה שלמה

• to_datetime הכרחי לפני מיון/חישוב כרונולוגי נכון על עמודת תאריכים

• rename מאחד שמות עמודות עקביים, קריטי לפני merge בין מקורות שונים

טעויות נפוצות

• להשתמש ב-dropna() בלי subset ולאבד שורות בגלל עמודה לא-רלוונטית שחסרה בהן

• לשכוח to_datetime ולמיין תאריכים כטקסט — סדר שגוי בלי שגיאה גלויה

• למלא fillna בערך “שרירותי” (כמו 0) שמעוות ממוצעים וחישובים סטטיסטיים אחרים

סיכום

ניקוי נתונים הוא תמיד השלב שקודם לניתוח אמיתי. NaN מייצג ערכים חסרים; fillna ממלא אותם בערך הגיוני (שומר שורה), dropna מסיר שורות/עמודות עם חוסר (מאבד מידע במודע). rename מאחד שמות עמודות לעקביים, קריטי לפני שילוב מקורות. to_datetime הופך טקסט תאריך לטיפוס תאריך אמיתי, מאפשר מיון כרונולוגי וחישובי הפרשים — בלעדיו, “מיון תאריכים” עלול להיות שגוי בשקט.

דוקומנטציה רשמית

Pandas — Working with missing data

Pandas — to_datetime


תרגילים

תרגיל 1 — בדיקה וטיפול בערכים חסרים

המשימה: טענו DataFrame עם כמה ערכי NaN מכוונים, ספרו כמה חסרים יש בכל עמודה (isna().sum()), ואז מלאו עמודה מספרית בממוצע ומחקו שורות שחסר בהן שדה קריטי.

בדיקה: לפני הניקוי, isna().sum() מציג ערכים חסרים; אחרי הניקוי, אין NaN בעמודה שמולאה, והשורות עם השדה הקריטי החסר נעלמו.

תרגיל 2 — to_datetime ומיון

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

בדיקה: לפני ההמרה, מיון לפי הטקסט נותן סדר שגוי; אחרי ההמרה, הסדר כרונולוגי נכון.


פרויקט מסכם

המשימה: נקו DataFrame “מלוכלך” של הזמנות ממקור חיצוני מדומה.

דרישות:

  1. עמודות עם שמות לא-עקביים (cust_id, amt, dt) — אחדו עם rename לשמות ברורים
  2. עמודת סכום עם כמה ערכים חסרים — מלאו עם הממוצע (fillna)
  3. עמודת תאריך כטקסט — המירו עם to_datetime ומיינו כרונולוגית
  4. שורות עם CustomerId חסר לגמרי (שדה קריטי) — הסירו עם dropna(subset=...)

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

מה בפרק הבא

בפרק הבא — Extract + Load: איך שולפים נתונים ממקורות אמיתיים (API עם requests) וכותבים תוצאות בחזרה למסד נתונים (to_sql) — הצעדים הראשון והאחרון של כל צנרת ETL.