iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
自我挑戰組

數據新手村:統計系畢業生 30 天打怪升級之旅系列 第 18

Day 18 - Pandas 的資料篩選與條件過濾

  • 分享至 

  • xImage
  •  

大家好,歡迎來到數據新手村的第十八天!昨天我們成功將 Olist 訂單資料載入到 DataFrame 中,並對它進行了初步的「健康檢查」。

我們手上現在有近 10 萬筆訂單,但通常我們只對「符合特定條件」的子集感興趣。例如,身為一個數據分析師,老闆可能會問你:

  • 「我們『已送達』的訂單有多少?」
  • 「『聖保羅州 (SP)』的客戶貢獻了多少訂單?」
  • 「那麼,『來自聖保羅州且已送達』的訂單又有多少?」

今天,我們就要來學習如何使用 Pandas 強大的布林索引 (Boolean Indexing),像一個精準的外科醫生一樣,從龐大的數據中,切出我們想要的任何部分。


1. 布林索引 - Pandas 的超級篩網

這個概念在 Day 11 的 NumPy 中已經學過了,在 Pandas 中完全一樣,但威力更強大,因為我們可以針對帶有欄位名稱的資料進行篩選。

它的運作分為兩步驟:

  1. 建立條件 (產生篩網): 建立一個或多個條件,這會產生一個由 True/False 組成的 Series
  2. 應用篩網: 將這個 Series 篩網放回 df[] 中,就能篩選出所有條件為 True列 (row)

單一條件篩選

讓我們來找出所有「已送達 (delivered)」的訂單。

# --- 正確讀取資料 ---
# (請根據實際檔案路徑修改)
orders_df = pd.read_csv("D:/Datasets/Olist/olist_orders_dataset.csv")
customers_df = pd.read_csv("D:/Datasets/Olist/olist_customers_dataset.csv")

# --- 正確合併 ---
# 用正確的訂單表和客戶表進行合併
orders_with_customer_df = pd.merge(orders_df, customers_df, on='customer_id')

# --- 範例 1:單一條件篩選 ---
print("### 範例 1:單一條件篩選 ###")
condition = orders_with_customer_df['order_status'] == 'delivered'
delivered_orders_df = orders_with_customer_df[condition]
print(f"已送達的訂單總數: {len(delivered_orders_df)}")
print("已送達訂單的前五筆:")
print(delivered_orders_df.head())
print("\n" + "="*30 + "\n")

輸出結果:

範例 1:單一條件篩選

已送達的訂單總數: 96478
已送達訂單的前五筆:
order_id customer_id
0 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d
1 53cdb2fc8bc7dce0b6741e2150273451 b0830fb4747a6c6d20dea0b8c802d7ef
2 47770eb9100c2d0c44946d9cf07ec65d 41ce2a54c0b03bf3443c3d931a367089
3 949d5b44dbf5de918fe9c16f97b45f8a f88197465ea7920adcdbec7375364d82
4 ad21c59c0840e6cb83a9ceb5573f8159 8ab97904e6daea8866dbdbc4fb7aad2c

order_status order_purchase_timestamp order_approved_at
0 delivered 2017-10-02 10:56:33 2017-10-02 11:07:15
1 delivered 2018-07-24 20:41:37 2018-07-26 03:24:27
2 delivered 2018-08-08 08:38:49 2018-08-08 08:55:23
3 delivered 2017-11-18 19:28:06 2017-11-18 19:45:59
4 delivered 2018-02-13 21:18:39 2018-02-13 22:20:29

order_delivered_carrier_date order_delivered_customer_date
0 2017-10-04 19:55:00 2017-10-10 21:25:13
1 2018-07-26 14:31:00 2018-08-07 15:27:45
2 2018-08-08 13:50:00 2018-08-17 18:06:29
3 2017-11-22 13:39:59 2017-12-02 00:28:42
4 2018-02-14 19:46:34 2018-02-16 18:17:02

order_estimated_delivery_date customer_unique_id
0 2017-10-18 00:00:00 7c396fd4830fd04220f754e42b4e5bff
1 2018-08-13 00:00:00 af07308b275d755c9edb36a90c618231
2 2018-09-04 00:00:00 3a653a41f6f9fc3d2a113cf8398680e8
3 2017-12-15 00:00:00 7c142cf63193a1473d2e66489a9ae977
4 2018-02-26 00:00:00 72632f0f9dd73dfee390c9b22eb56dd6

customer_zip_code_prefix customer_city customer_state
0 3149 sao paulo SP
1 47813 barreiras BA
2 75265 vianopolis GO
3 59296 sao goncalo do amarante RN
4 9195 santo andre SP

==============================

  1. 組合多重條件 (本篇核心)
    當我們需要同時滿足多個條件時,需要使用邏輯運算子 & (AND) 和 | (OR)。

⚠️ 新手最常見的錯誤:

在 Pandas 中,必須使用 & 和 |,不能使用 and 和 or。

每一個獨立的條件,都必須用小括號 () 包起來!

讓我們來找出所有「已送達」且「來自聖保羅州 (SP)」的訂單。

# --- 範例 2:多重條件篩選 ---
print("### 範例 2:多重條件篩選 ###")
# 建立兩個獨立的條件
condition1 = orders_with_customer_df['order_status'] == 'delivered'
condition2 = orders_with_customer_df['customer_state'] == 'SP'

# 使用 & 將兩個條件組合起來
sp_delivered_df = orders_with_customer_df[condition1 & condition2]

print(f"原始合併後的訂單總數: {len(orders_with_customer_df)}")
print(f"來自聖保羅州(SP)且已送達的訂單數: {len(sp_delivered_df)}")
print("來自聖保羅州(SP)且已送達訂單的前五筆:")
print(sp_delivered_df.head())
print("\n" + "="*30 + "\n")

輸出結果:

範例 2:多重條件篩選

原始合併後的訂單總數: 99441
來自聖保羅州(SP)且已送達的訂單數: 40501
來自聖保羅州(SP)且已送達訂單的前五筆:
order_id customer_id
0 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d
4 ad21c59c0840e6cb83a9ceb5573f8159 8ab97904e6daea8866dbdbc4fb7aad2c
9 e69bfb5eb88e0ed6a785585b27e16dbf 31ad1d1b63eb9962463f764d4e6e0c9d
11 34513ce0c4fab462a55830c0989c7edb 7711cf624183d843aafe81855097bc37
13 5ff96c15d0b717ac6ad1f3d77225a350 19402a48fe860416adf93348aba37740

order_status order_purchase_timestamp order_approved_at
0 delivered 2017-10-02 10:56:33 2017-10-02 11:07:15
4 delivered 2018-02-13 21:18:39 2018-02-13 22:20:29
9 delivered 2017-07-29 11:55:02 2017-07-29 12:05:32
11 delivered 2017-07-13 19:58:11 2017-07-13 20:10:08
13 delivered 2018-07-25 17:44:10 2018-07-25 17:55:14

order_delivered_carrier_date order_delivered_customer_date
0 2017-10-04 19:55:00 2017-10-10 21:25:13
4 2018-02-14 19:46:34 2018-02-16 18:17:02
9 2017-08-10 19:45:24 2017-08-16 17:14:30
11 2017-07-14 18:43:29 2017-07-19 14:04:48
13 2018-07-26 13:16:00 2018-07-30 15:52:25

order_estimated_delivery_date customer_unique_id
0 2017-10-18 00:00:00 7c396fd4830fd04220f754e42b4e5bff
4 2018-02-26 00:00:00 72632f0f9dd73dfee390c9b22eb56dd6
9 2017-08-23 00:00:00 299905e3934e9e181bfb2e164dd4b4f8
11 2017-08-08 00:00:00 782987b81c92239d922aa49d6bd4200b
13 2018-08-08 00:00:00 e2dfa3127fedbbca9707b36304996dab

customer_zip_code_prefix customer_city customer_state  

0 3149 sao paulo SP
4 9195 santo andre SP
9 18075 sorocaba SP
11 4278 sao paulo SP
13 4812 sao paulo SP

  1. .isin() 方法 - 一次篩選多個值
    如果我們想找出所有來自東南部三個主要州 (SP, RJ, MG) 的訂單,一個個用 | (OR) 來寫會很冗長。這時,.isin() 就派上用場了。
# --- 範例 3:.isin() 方法 ---
print("### 範例 3:.isin() 方法 ###")
# 定義一個我們想尋找的州列表
states_to_find = ['SP', 'RJ', 'MG']

# 使用 .isin() 進行篩選
southeast_orders_df = orders_with_customer_df[orders_with_customer_df['customer_state'].isin(states_to_find)]

print(f"東南部三州(SP, RJ, MG)的訂單總數: {len(southeast_orders_df)}")
print("東南部三州訂單的前五筆:")
print(southeast_orders_df.head())

輸出結果:

範例 3:.isin() 方法

東南部三州(SP, RJ, MG)的訂單總數: 66233
東南部三州訂單的前五筆:
order_id customer_id
0 e481f51cbdc54678b7cc49136f2d6af7 9ef432eb6251297304e76186b10a928d
4 ad21c59c0840e6cb83a9ceb5573f8159 8ab97904e6daea8866dbdbc4fb7aad2c
7 6514b8ad8028c9f2cc2374ded245783f 9bdf08b4b3b52b5526ff42d37d47f222
9 e69bfb5eb88e0ed6a785585b27e16dbf 31ad1d1b63eb9962463f764d4e6e0c9d
10 e6ce16cb79ec1d90b1da9085a6118aeb 494dded5b201313c64ed7f100595b95c

order_status order_purchase_timestamp order_approved_at
0 delivered 2017-10-02 10:56:33 2017-10-02 11:07:15
4 delivered 2018-02-13 21:18:39 2018-02-13 22:20:29
7 delivered 2017-05-16 13:10:30 2017-05-16 13:22:11
9 delivered 2017-07-29 11:55:02 2017-07-29 12:05:32
10 delivered 2017-05-16 19:41:10 2017-05-16 19:50:18

order_delivered_carrier_date order_delivered_customer_date
0 2017-10-04 19:55:00 2017-10-10 21:25:13
4 2018-02-14 19:46:34 2018-02-16 18:17:02
7 2017-05-22 10:07:46 2017-05-26 12:55:51
9 2017-08-10 19:45:24 2017-08-16 17:14:30
10 2017-05-18 11:40:40 2017-05-29 11:18:31

order_estimated_delivery_date customer_unique_id
0 2017-10-18 00:00:00 7c396fd4830fd04220f754e42b4e5bff
4 2018-02-26 00:00:00 72632f0f9dd73dfee390c9b22eb56dd6
7 2017-06-07 00:00:00 932afa1e708222e5821dac9cd5db4cae
9 2017-08-23 00:00:00 299905e3934e9e181bfb2e164dd4b4f8
10 2017-06-07 00:00:00 f2a85dec752b8517b5e58a06ff3cd937

customer_zip_code_prefix   customer_city customer_state  

0 3149 sao paulo SP
4 9195 santo andre SP
7 26525 nilopolis RJ
9 18075 sorocaba SP
10 20780 rio de janeiro RJ


結語
今天我們掌握了 Pandas 中數據篩選的利器——布林索引。透過組合 &, | 和 .isin(),我們幾乎可以從 DataFrame 中取出任何我們感興趣的資料子集。

但真實世界的數據並不總是那麼完美。如果資料中有些欄位是空白的呢?我們該如何處理?明天,Day 19,我們將正式進入數據分析師最重要的工作:資料清洗,並從如何處理缺失值 (Missing Values) 開始。


上一篇
Day 17 - 用 Pandas 讀取 Olist CSV 檔案
下一篇
Day 19 - 資料清洗(一):如何優雅地處理缺失值 (Missing Values)
系列文
數據新手村:統計系畢業生 30 天打怪升級之旅21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言