# סינון ומיון ב-Pandas📖 שיעור

## מה זה?

זוכרים `Where`/`OrderBy` מיחידת ה-LINQ ב-C#? Pandas עושה בדיוק אותו דבר, אבל בתחביר שונה לגמרי — Boolean Indexing (סינון עם "מסכת אמת/שקר") במקום Lambda. הרעיון זהה: מתוך DataFrame גדול, רוצים רק את השורות הרלוונטיות, ובסדר מסוים.

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

• Boolean Indexing — סינון על ידי "מסכה" של True/False לכל שורה — `df[condition]` מחזיר רק שורות עם True

• `sort_values()` — ממיין DataFrame לפי עמודה אחת או יותר

• `&` / `|` — "וגם" / "או", לשילוב כמה תנאי סינון יחד (לא `and`/`or` הרגילים של פייתון!)

```python
# Boolean Indexing — the basic filtering syntax in Pandas
adults = df[df["Age"] >= 18]        # only rows with Age >= 18
# df["Age"] >= 18 produces a Series of True/False; df[...] uses it to filter

# Combining conditions — & and | (not and/or!), each condition in its own parentheses
adults_high_score = df[(df["Age"] >= 18) & (df["Score"] > 80)]
```

```python
# sort_values — sorting
df_sorted = df.sort_values("Score", ascending=False)   # from highest to lowest

# sorting by multiple columns
df_multi_sort = df.sort_values(["ClassName", "Score"], ascending=[True, False])
# first by ClassName (ascending), and within each class — by Score (descending)
```

```python
# Chaining — filter then sort, like Where().OrderBy() in LINQ
result = df[df["Age"] >= 18].sort_values("Score", ascending=False)
```

## הסבר עיקרי

`df["Age"] >= 18` לא מסנן — הוא מייצר Series בוליאני — לפני שמבינים סינון, חשוב להבין ש-`df["Age"] >= 18` בעצמה היא **לא** הסינון עצמו — היא מייצרת `Series` חדשה עם `True`/`False` לכל שורה (בדיוק כמו lambda ב-LINQ שמחזירה `bool`). `df[...]` הוא מה שבפועל "מיישם" את המסכה הזו ומחזיר רק את השורות עם `True`.

למה `&`/`|` ולא `and`/`or` — זו טעות נפוצה מאוד למתחילים: `and`/`or` הרגילים של פייתון עובדים על ערך בוליאני **בודד**, לא על Series שלמה. `&`/`|` הם האופרטורים ש-Pandas מגדירה מחדש כדי לעבוד "איבר-איבר" על כל ה-Series בבת אחת — וכל תנאי חייב סוגריים משלו (`(df["Age"] >= 18) & (df["Score"] > 80)`) בגלל סדר קדימות אופרטורים בפייתון.

מיון לפי כמה עמודות = "מיון משני" — `sort_values(["ClassName", "Score"], ascending=[True, False])` ממיין קודם לפי `ClassName` (א'-ב'), ובתוך כל קבוצת `ClassName` זהה — לפי `Score` יורד. זה מקביל בדיוק ל-`OrderBy(...).ThenByDescending(...)` ב-LINQ, רק בתחביר של רשימת עמודות.

## יתרונות

Boolean Indexing מתמצת סינון מורכב לשורה אחת קצרה; שילוב `&`/`|` מאפשר תנאים מרובים בלי `if` מקונן; `sort_values` עם רשימת עמודות נותן מיון רב-שכבתי בקריאה אחת.

## חסרונות

`&`/`|` במקום `and`/`or` הוא תחביר לא-אינטואיטיבי שקל לטעות בו במיוחד למי שבא מפייתון "רגיל"; שכחת סוגריים סביב כל תנאי גורמת לשגיאות עדיפות אופרטורים מבלבלות.

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

• `df[condition]` = Boolean Indexing — `condition` היא Series של True/False

• `&`/`|` (לא `and`/`or`) לשילוב תנאים ב-Pandas, כל תנאי בסוגריים נפרדים

• `sort_values(col, ascending=False)` ממיין; רשימת עמודות = מיון רב-שכבתי

• סינון ומיון ניתנים לשרשור, בדיוק כמו `Where().OrderBy()` ב-LINQ

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

• להשתמש ב-`and`/`or` במקום `&`/`|` ולקבל שגיאה ("truth value of a Series is ambiguous")

• לשכוח סוגריים סביב כל תנאי בשילוב (`df["Age"] >= 18 & df["Score"] > 80` בלי סוגריים — שגיאה או תוצאה שגויה)

• לבלבל בין `ascending=True` (ברירת מחדל, עולה) ל-`ascending=False` (יורד)

## סיכום

Boolean Indexing (`df[condition]`) הוא תחביר הסינון המרכזי ב-Pandas — מקביל ל-`Where` ב-LINQ, אך דורש `&`/`|` (לא `and`/`or`) לשילוב תנאים, עם סוגריים לכל תנאי בנפרד. `sort_values` ממיין לפי עמודה אחת או כמה, עם `ascending` לקביעת כיוון — מקביל ל-`OrderBy`/`OrderByDescending`. סינון ומיון ניתנים לשרשור לתוצאה אחת קריאה.

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

[Pandas — Boolean indexing](https://pandas.pydata.org/docs/user_guide/indexing.html#boolean-indexing)

[Pandas — sort_values](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html)

---

## תרגילים

### תרגיל 1 — סינון בסיסי

**המשימה:** מתוך DataFrame עם עמודת `Price`, סננו רק מוצרים במחיר מעל 100.

**בדיקה:** רק שורות עם `Price > 100` מופיעות בתוצאה.

### תרגיל 2 — סינון משולב ומיון

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

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

---

## פרויקט מסכם

**המשימה:** בנו דוח "תלמידים מצטיינים" מתוך DataFrame של ציונים.

**דרישות:**
1. סננו רק תלמידים עם ציון מעל 85 **וגם** נוכחות מעל 90% (שתי עמודות, שילוב `&`)
2. מיינו את התוצאה לפי ציון יורד
3. הציגו רק את העמודות הרלוונטיות (`Name`, `Score`) בתוצאה הסופית (בחירת עמודות עם `df[["Name", "Score"]]`)

**בדיקה:** התוצאה מכילה רק תלמידים שעונים על שני התנאים, ממוינת נכון, עם שתי העמודות בלבד.

## מה בפרק הבא

בפרק הבא — **Transform: merge, groupby, agg, apply**: איך משלבים כמה DataFrames יחד וסופרים/מחשבים לפי קבוצות — הכלים האנליטיים המרכזיים של Pandas.
