過濾資料的範例說明
# Pandas 過濾資料範例與常用技巧
Pandas 中最常見的資料過濾方式,是建立一個由 `True` / `False` 組成的條件,再將條件傳入 `DataFrame`。
以下使用範例資料:
```python
import pandas as pd
import numpy as np
df = pd.DataFrame({
"name": ["Alice", "Bob", "Charlie", "David", "Eva"],
"age": [25, 32, 28, 40, 22],
"city": ["Taipei", "Taichung", "Taipei", "Kaohsiung", "Taipei"],
"score": [88, 75, 92, 60, np.nan],
"date": pd.to_datetime([
"2024-01-10",
"2024-02-15",
"2024-03-20",
"2024-04-05",
"2024-05-12"
])
})
print(df)
```
資料如下:
```text
name age city score date
0 Alice 25 Taipei 88.0 2024-01-10
1 Bob 32 Taichung 75.0 2024-02-15
2 Charlie 28 Taipei 92.0 2024-03-20
3 David 40 Kaohsiung 60.0 2024-04-05
4 Eva 22 Taipei NaN 2024-05-12
```
---
## 1. 根據單一條件過濾
例如,篩選年齡大於 30 歲的資料:
```python
result = df[df["age"] > 30]
print(result)
```
結果:
```text
name age city score date
1 Bob 32 Taichung 75.0 2024-02-15
3 David 40 Kaohsiung 60.0 2024-04-05
```
也可以使用其他比較運算子:
```python
df[df["age"] == 25] # 等於 25
df[df["age"] != 25] # 不等於 25
df[df["age"] >= 30] # 大於等於 30
df[df["age"] < 30] # 小於 30
```
### 注意
Pandas 中的等於是 `==`,不是單一的 `=`:
```python
df[df["age"] == 25] # 正確
```
---
## 2. 多個條件:AND、OR、NOT
### AND:同時符合多個條件
篩選年齡大於 25,且分數至少為 80:
```python
result = df[(df["age"] > 25) & (df["score"] >= 80)]
```
結果:
```text
name age city score date
2 Charlie 28 Taipei 92.0 2024-03-20
```
### OR:符合其中一個條件
篩選城市為 Taipei 或 Taichung:
```python
result = df[
(df["city"] == "Taipei") |
(df["city"] == "Taichung")
]
```
### NOT:排除某個條件
排除 Taipei:
```python
result = df[df["city"] != "Taipei"]
```
也可以使用 `~` 反轉條件:
```python
result = df[~(df["city"] == "Taipei")]
```
### 非常重要:條件必須加括號
錯誤寫法:
```python
df[df["age"] > 25 & df["score"] >= 80]
```
正確寫法:
```python
df[(df["age"] > 25) & (df["score"] >= 80)]
```
Pandas 使用:
- `&` 代表 AND
- `|` 代表 OR
- `~` 代表 NOT
不要使用 Python 的 `and`、`or`:
```python
# 錯誤
df[(df["age"] > 25) and (df["score"] >= 80)]
# 正確
df[(df["age"] > 25) & (df["score"] >= 80)]
```
---
## 3. 使用 `isin()` 篩選多個值
如果要篩選多個城市,可以使用 `isin()`:
```python
result = df[df["city"].isin(["Taipei", "Kaohsiung"])]
```
這相當於:
```python
(df["city"] == "Taipei") | (df["city"] == "Kaohsiung")
```
排除這些城市:
```python
result = df[~df["city"].isin(["Taipei", "Kaohsiung"])]
```
也可以套用在數字欄位:
```python
result = df[df["age"].isin([22, 25, 32])]
```
---
## 4. 使用 `between()` 篩選範圍
例如,篩選年齡介於 25 到 35 歲:
```python
result = df[df["age"].between(25, 35)]
```
`between()` 預設包含兩端:
```python
df["age"].between(25, 35)
```
相當於:
```python
(df["age"] >= 25) & (df["age"] <= 35)
```
如果不包含邊界,可以指定:
```python
result = df[df["age"].between(25, 35, inclusive="neither")]
```
其他選項包括:
```python
inclusive="both" # 預設,包含上下界
inclusive="left" # 包含左界
inclusive="right" # 包含右界
inclusive="neither" # 都不包含
```
---
## 5. 字串過濾:`str.contains()`
篩選城市名稱中包含 `"Tai"` 的資料:
```python
result = df[df["city"].str.contains("Tai")]
```
結果會包含:
```text
name age city score date
0 Alice 25 Taipei 88.0 2024-01-10
2 Charlie 28 Taipei 92.0 2024-03-20
4 Eva 22 Taipei NaN 2024-05-12
```
### 忽略大小寫
```python
result = df[
df["city"].str.contains("tai", case=False, na=False)
]
```
### 避免缺失值造成錯誤
如果字串欄位可能含有 `NaN`,建議使用:
```python
df["city"].str.contains("Tai", na=False)
```
`na=False` 表示缺失值視為不符合條件。
### 其他常用字串方法
```python
df[df["name"].str.startswith("A")] # 以 A 開頭
df[df["name"].str.endswith("e")] # 以 e 結尾
df[df["name"].str.len() > 4] # 字串長度大於 4
df[df["name"].str.lower() == "alice"] # 忽略大小寫比較
```
---
## 6. 處理缺失值:`isna()` 與 `notna()`
篩選分數缺失的資料:
```python
result = df[df["score"].isna()]
```
結果:
```text
name age city score date
4 Eva 22 Taipei NaN 2024-05-12
```
篩選分數不缺失的資料:
```python
result = df[df["score"].notna()]
```
也可以使用:
```python
df[df["score"].isnull()] # 等同於 isna()
df[df["score"].notnull()] # 等同於 notna()
```
不要使用:
```python
df[df["score"] == np.nan]
```
因為 `NaN` 不等於任何值,包括它自己。應使用 `isna()`。
---
## 7. 日期篩選
由於 `date` 欄位已經是日期型別,可以直接比較:
```python
result = df[df["date"] >= "2024-03-01"]
```
篩選日期區間:
```python
result = df[
(df["date"] >= "2024-02-01") &
(df["date"] <= "2024-04-30")
]
```
也可以使用 `between()`:
```python
result = df[
df["date"].between("2024-02-01", "2024-04-30")
]
```
### 篩選特定年份或月份
```python
df[df["date"].dt.year == 2024]
df[df["date"].dt.month == 3]
df[df["date"].dt.day == 20]
```
### 日期欄位不是日期型別時
讀取資料後,先轉換:
```python
df["date"] = pd.to_datetime(df["date"], errors="coerce")
```
`errors="coerce"` 會將無法解析的值轉成 `NaT`。
---
## 8. 使用 `.loc[]` 過濾列與欄位
以下篩選年齡大於 25 的資料,並只取 `name`、`age`、`score` 三欄:
```python
result = df.loc[
df["age"] > 25,
["name", "age", "score"]
]
```
這是非常推薦的寫法,因為可以同時指定:
1. 哪些列
2. 哪些欄位
語法:
```python
df.loc[列條件, 欄位名稱]
```
例如:
```python
df.loc[df["city"] == "Taipei", "name"]
```
只取得 Taipei 的姓名。
同時修改過濾後的資料:
```python
df.loc[df["score"] < 70, "score"] = 70
```
這會把分數低於 70 的值改成 70。
---
## 9. 使用 `.query()` 篩選
`query()` 可以讓條件更接近自然語言:
```python
result = df.query("age > 25 and score >= 80")
```
篩選城市:
```python
result = df.query("city == 'Taipei'")
```
多個城市:
```python
result = df.query("city in ['Taipei', 'Taichung']")
```
使用外部變數時,要加上 `@`:
```python
min_age = 25
result = df.query("age >= @min_age")
```
也可以搭配日期:
```python
start_date = "2024-02-01"
result = df.query("date >= @start_date")
```
### `query()` 的優點
```python
df.query("age > 25 and city == 'Taipei'")
```
通常比以下寫法更容易閱讀:
```python
df[(df["age"] > 25) & (df["city"] == "Taipei")]
```
### `query()` 的限制
欄位名稱若包含空格或特殊字元,需要使用反引號:
```python
df.query("`customer name` == 'Alice'")
```
---
## 10. 過濾後重新設定索引
過濾後,原本的索引通常會保留:
```python
result = df[df["age"] > 25]
print(result.index)
```
結果可能是:
```text
Index([1, 2, 3], dtype='int64')
```
如果希望索引重新從 0 開始:
```python
result = df[df["age"] > 25].reset_index(drop=True)
```
`drop=True` 表示不要將舊索引保留成新欄位。
---
## 11. 過濾後修改資料與 `SettingWithCopyWarning`
以下寫法可能產生警告:
```python
result = df[df["age"] > 25]
result["group"] = "adult"
```
建議使用 `.loc[]` 或 `.copy()`:
```python
result = df.loc[df["age"] > 25].copy()
result["group"] = "adult"
```
如果要直接修改原始 `df`:
```python
df.loc[df["age"] > 25, "group"] = "adult"
```
### 建議原則
- 只想取得資料:可以直接過濾
- 過濾後還要修改:使用 `.copy()`
- 想修改原始資料:使用 `.loc[]`
---
## 12. 複合範例
篩選:
- 城市為 Taipei 或 Taichung
- 年齡至少 25 歲
- 分數不為缺失值
- 分數至少 80
```python
result = df.loc[
df["city"].isin(["Taipei", "Taichung"]) &
(df["age"] >= 25) &
df["score"].notna() &
(df["score"] >= 80),
["name", "age", "city", "score"]
]
print(result)
```
結果:
```text
name age city score
0 Alice 25 Taipei 88.0
2 Charlie 28 Taipei 92.0
```
用 `query()` 表示:
```python
result = df.query(
"city in ['Taipei', 'Taichung'] "
"and age >= 25 "
"and score >= 80"
)
```
---
## 13. 讀取 CSV 後直接過濾
實務上通常是讀取 CSV,再進行篩選:
```python
df = pd.read_csv("sales.csv")
result = df.loc[
(df["amount"] > 1000) &
(df["status"] == "completed")
]
```
如果資料量很大,也可以在讀取時只選取需要的欄位:
```python
df = pd.read_csv(
"sales.csv",
usecols=["customer", "amount", "status"]
)
```
這可以減少記憶體使用量。
---
## 14. 過濾技巧整理
### 技巧一:先確認欄位型別
```python
print(df.dtypes)
```
例如日期欄位若是 `object`,應先轉換:
```python
df["date"] = pd.to_datetime(df["date"])
```
### 技巧二:查看條件結果
可以先將條件存成變數,方便除錯:
```python
condition = (
(df["age"] >= 25) &
(df["score"] >= 80)
)
print(condition)
result = df[condition]
```
### 技巧三:使用 `value_counts()` 了解資料內容
```python
print(df["city"].value_counts())
```
這有助於確認實際資料中有哪些分類值。
### 技巧四:注意字串空白
如果資料可能有前後空白:
```python
df["city"] = df["city"].str.strip()
```
再進行篩選:
```python
df[df["city"] == "Taipei"]
```
### 技巧五:欄位名稱包含空格時
使用 `.loc[]`:
```python
df.loc[df["customer name"] == "Alice"]
```
或在 `query()` 中使用反引號:
```python
df.query("`customer name` == 'Alice'")
```
---
## 15. 常見錯誤總結
### 錯誤一:使用 `and`、`or`
```python
# 錯誤
df[(df["age"] > 20) and (df["score"] > 80)]
```
```python
# 正確
df[(df["age"] > 20) & (df["score"] > 80)]
```
### 錯誤二:多條件沒有括號
```python
# 錯誤
df[df["age"] > 20 & df["score"] > 80]
```
```python
# 正確
df[(df["age"] > 20) & (df["score"] > 80)]
```
### 錯誤三:用 `== np.nan`
```python
# 錯誤
df[df["score"] == np.nan]
```
```python
# 正確
df[df["score"].isna()]
```
### 錯誤四:修改過濾結果時出現警告
```python
# 建議
result = df.loc[df["age"] > 25].copy()
result["category"] = "adult"
```
---
## 最常用的寫法速查
```python
# 單一條件
df[df["age"] > 30]
# AND
df[(df["age"] > 25) & (df["score"] >= 80)]
# OR
df[(df["city"] == "Taipei") | (df["city"] == "Taichung")]
# NOT
df[df["city"] != "Taipei"]
# 多個值
df[df["city"].isin(["Taipei", "Kaohsiung"])]
# 範圍
df[df["age"].between(20, 30)]
# 字串包含
df[df["name"].str.contains("a", case=False, na=False)]
# 缺失值
df[df["score"].isna()]
# 非缺失值
df[df["score"].notna()]
# 日期
df[df["date"] >= "2024-03-01"]
# 過濾列與欄位
df.loc[df["age"] > 25, ["name", "age"]]
# query
df.query("age > 25 and score >= 80")
# 過濾後重設索引
df[df["age"] > 25].reset_index(drop=True)
```
實務上,最推薦優先掌握:
1. `df[條件]`
2. `df.loc[條件, 欄位]`
3. `isin()`
4. `between()`
5. `str.contains()`
6. `isna()` / `notna()`
7. `query()`
相關學習地圖、教學課程
Python 資料工程