Pandas 提供了多種資料選擇方法,靈活運用這些技巧可以大幅提升資料處理效率。[]
運算元適用於 DataFrame 的欄位選擇,而 Series 則可利用切片和列表進行多重選擇。然而,當索引非唯一或包含整數時,使用 []
可能導致混淆,建議採用 .loc
和 .iloc
進行根據標籤和位置的選擇,確保程式碼清晰易懂,避免非預期結果。理解不同選擇方法的特性,才能根據實際需求選擇最佳方案,精確地操作和分析資料。
從Series中進行基本選擇
在前一章中,我們瞭解瞭如何建立pd.Series
和pd.DataFrame
,並探討了它們與pd.Index
的關係。在掌握了建構函式的基礎後,我們現在將重點轉移到至關重要的選擇和指定過程。選擇,也被稱為索引,是用來從pandas物件中檢索值的「getter」。相比之下,指定是一種「setter」,用於更新值。
如何進行基本選擇
讓我們從一個非常簡單的pd.Series
開始:
ser = pd.Series(list("abc") * 3)
ser
輸出:
0 a
1 b
2 c
3 a
4 b
5 c
6 a
7 b
8 c
dtype: object
在Python中,你已經知道可以使用[]
運算元從容器中選擇元素。例如,ser[3]
會傳回與標籤3相關聯的值。
ser[3]
輸出:
'a'
使用表示式ser[3]
時,pandas會嘗試在pd.Series
的索引中查詢標籤3,並傳回與該標籤相關聯的值。
使用列表引數進行多重選擇
你可以透過提供一個包含多個元素的列表引數來選擇多個值:
ser[[0, 2]]
輸出:
0 a
2 c
dtype: object
使用切片進行選擇
假設你使用預設索引,你可以使用與切片Python列表非常相似的切片引數。例如,要取得直到(但不包括)位置3的元素,你可以使用:
ser[:3]
輸出:
0 a
1 b
2 c
dtype: object
你甚至可以提供帶有起始和停止引數的切片。以下程式碼將檢索pd.Series
的所有元素,從位置2開始,直到(但不包括)位置6:
ser[2:6]
輸出:
2 c
3 a
4 b
5 c
dtype: object
最後一個關於切片的例子使用了起始、停止和步長引數來抓取每第三個元素,從位置1開始,並在遇到位置8時停止:
ser[1:8:3]
輸出:
1 b
4 b
7 b
dtype: object
自定義索引的選擇
當你提供自己的pd.Index
值時,選擇仍然有效。讓我們建立一個具有字串索引標籤的小型pd.Series
來說明:
ser = pd.Series(range(3), index=["Jack", "Jill", "Jayne"])
ser
輸出:
Jack 0
Jill 1
Jayne 2
dtype: int64
透過ser["Jill"]
進行選擇將掃描索引中的字串“Jill”並傳回相應的元素:
ser["Jill"]
輸出:
1
更多注意事項…
使用[]
運算元時的一個常見陷阱是假設具有整數引數的選擇與從Python列表中選擇時的工作方式相同。只有當你使用預設的pd.Index
(技術上稱為pd.RangeIndex
)時,這才是正確的。
當不使用pd.RangeIndex
時,必須特別注意其行為。為了說明,讓我們從一個小型的pd.Series
開始,它仍然在其pd.Index
中使用整數,但不使用從0開始的自動遞增序列:
ser = pd.Series(list("abc"), index=[2, 42, 21])
ser
輸出:
2 a
42 b
21 c
dtype: object
需要注意的是,整數引數按標籤選擇,而不是按位置選擇。也就是說,以下程式碼將傳回與標籤2相關聯的值,而不是位置2處的值:
ser[2]
輸出:
'a'
雖然整數引數按標籤匹配而不是按位置匹配,但切片仍然按位置工作。以下示例不會在遇到數字2時停止,而是傳回前兩個元素:
ser[:2]
輸出:
2 a
42 b
dtype: object
處理非唯一索引的選擇行為
讓我們建立一個小的 pd.Series
,其中數字1在我們的行索引中出現兩次:
ser = pd.Series(["apple", "banana", "orange"], index=[0, 1, 1])
ser
輸出:
0 apple
1 banana
1 orange
dtype: object
使用這個 pd.Series
,嘗試選擇數字1不會傳回單個值,而是傳回另一個 pd.Series
:
ser[1]
輸出:
1 banana
1 orange
dtype: object
使用.loc
和.iloc
方法避免歧義
為了避免在想要按標籤或按位置選擇時的歧義,強烈建議使用本章稍後介紹的.loc
和.iloc
方法。
圖表說明:索引與選擇過程
graph LR; A["建立Series"] --> B["使用[]運算元進行選擇"]; B --> C["按標籤選擇"]; B --> D["按位置切片"]; C --> E["傳回單個值或Series"]; D --> E; E --> F["處理非唯一索引"]; F --> G["傳回包含多個值的Series"];
**圖表翻譯:**此圖示展示了從建立Series到使用不同方法進行選擇的過程,包括按標籤選擇和按位置切片,以及處理非唯一索引時的傳回結果。
資料選擇與指定
在 pandas 中,資料選擇是資料操作和分析的基礎。本章節將介紹如何使用不同的選擇方法來存取和操作 DataFrame 和 Series 中的資料。
從 DataFrame 進行基本選擇
使用 []
運算元對 DataFrame 進行簡單選擇時,通常是根據欄位索引(column index)而非列索引(row index)進行選擇。這種區別對於有效地操作和分析資料至關重要。
如何操作
首先,建立一個簡單的 3x3 DataFrame,並指定欄位標籤:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.arange(9).reshape(3, -1), columns=["a", "b", "c"])
print(df)
輸出:
a b c
0 0 1 2
1 3 4 5
2 6 7 8
選擇單一欄位:
print(df["a"])
輸出:
0 0
1 3
2 6
Name: a, dtype: int64
若要選擇單一欄位但仍傳回 DataFrame 而非 Series,則傳遞單元素列表:
print(df[["a"]])
輸出:
a
0 0
1 3
2 6
使用列表選擇多個欄位:
print(df[["a", "b"]])
輸出:
a b
0 0 1
1 3 4
2 6 7
更多說明
使用列表作為 []
的引數時,可以自訂輸出欄位的順序。例如:
print(df[["b", "a"]])
輸出:
b a
0 1 0
1 4 3
2 7 6
這項功能在需要重新排序欄位以符合特定需求時非常有用。
使用位置索引選擇 Series
使用 iloc
可以根據位置索引進行選擇,避免標籤索引的混淆。
如何操作
建立一個具有非唯一整數標籤索引的 Series:
ser = pd.Series(["apple", "banana", "orange"], index=[0, 1, 1])
print(ser)
輸出:
0 apple
1 banana
1 orange
dtype: object
使用 iloc
以整數引數選擇標量:
print(ser.iloc[1])
輸出:
banana
使用列表包含單一元素傳回 Series:
print(ser.iloc[[1]])
輸出:
1 banana
dtype: object
更多說明
切片是表達選擇範圍的自然方式,與 iloc
結合使用非常方便。例如:
print(ser.iloc[:2])
輸出:
0 apple
1 banana
dtype: object
使用位置索引選擇 DataFrame
與 Series 相似,DataFrame 的 iloc
也接受整數、整數列表和切片物件,但需要兩個引數:第一個處理列選擇,第二個處理欄位選擇。
如何操作
建立一個具有五列四欄的 DataFrame:
df = pd.DataFrame(np.arange(20).reshape(5, -1), columns=list("abcd"))
print(df)
輸出:
a b c d
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
4 16 17 18 19
使用兩個整數引數傳回特定列和欄位位置的標量:
print(df.iloc[2, 2])
輸出:
10
使用空切片物件 :
可以選擇某個軸上的所有元素。例如,選擇第一欄的所有列:
print(df.iloc[:, 0])
輸出:
0 0
1 4
2 8
3 12
4 16
Name: a, dtype: int64
更多說明
空切片是有效的 iloc
引數,ser.iloc[:]
和 df.iloc[:, :]
將傳回每個軸上的所有元素,等同於複製該物件。
重點整理
- 使用
[]
對 DataFrame 的欄位進行選擇。 - 使用
iloc
對 Series 和 DataFrame 的元素進行位置索引選擇。 - 可以透過列表和切片自訂選擇結果的順序和範圍。
- 空切片可以用於選取某個軸上的所有元素。
使用標籤從 Series 中進行選擇
在 pandas 中,pd.Series.loc
用於根據標籤而非位置進行選擇。當你將 pd.Series
的索引視為查詢值時,這個方法尤其有用,就像 Python 字典中的鍵一樣,而不是重視資料在 pd.Series
中的順序或位置。
如何實作
讓我們建立一個 pd.Series
,其中行索引使用非唯一的整數標籤:
ser = pd.Series(["apple", "banana", "orange"], index=[0, 1, 1])
ser
輸出:
0 apple
1 banana
1 orange
dtype: object
pd.Series.loc
將選擇索引標籤為 1 的所有行:
ser.loc[1]
輸出:
1 banana
1 orange
dtype: object
當然,你不限於在 pandas 中使用整數標籤。讓我們看看使用由字串值組成的 pd.Index
時的情況:
ser = pd.Series([2, 2, 4], index=["dog", "cat", "human"], name="num_legs")
ser
輸出:
dog 2
cat 2
human 4
Name: num_legs, dtype: int64
pd.Series.loc
可以選擇索引標籤為 “dog” 的所有行:
ser.loc["dog"]
輸出:
2
要選擇索引標籤為 “dog” 或 “cat” 的所有行:
ser.loc[["dog", "cat"]]
輸出:
dog 2
cat 2
Name: num_legs, dtype: int64
最後,要選擇直到並包括標籤 “cat” 的所有行:
ser.loc[:"cat"]
輸出:
dog 2
cat 2
Name: num_legs, dtype: int64
詳細解說
瞭解根據標籤的選擇與 pd.Series.loc
提供了強大的功能來存取和操作 pd.Series
中的資料。雖然這個方法看起來很簡單,但它提供了一些重要的細微差別和行為,需要掌握才能有效地處理資料。
一個非常常見的錯誤是忽略了使用 pd.Series.loc
切片與標準 Python 和 pd.Series.iloc
切片之間的行為差異。
讓我們建立一個小的 Python 列表和一個具有相同資料的 pd.Series
:
values = ["Jack", "Jill", "Jayne"]
ser = pd.Series(values)
ser
輸出:
0 Jack
1 Jill
2 Jayne
dtype: object
正如你已經看到的,使用列表和其他內建於 Python 語言中的容器,切片傳回的值直到但不包括提供的位置:
values[:2]
輸出:
['Jack', 'Jill']
使用 pd.Series.iloc
切片與此行為相匹配,傳回一個具有與 Python 列表相同長度和元素的 pd.Series
:
ser.iloc[:2]
輸出:
0 Jack
1 Jill
dtype: object
但是使用 pd.Series.loc
切片實際上會產生不同的結果:
ser.loc[:2]
輸出:
0 Jack
1 Jill
2 Jayne
dtype: object
這裡發生了什麼?要嘗試理解這一點,重要的是要記住 pd.Series.loc
是根據標籤匹配,而不是根據位置。pandas 函式庫做了類別似於迴圈遍歷 pd.Series
中的每個元素及其伴隨的 pd.Index
的事情,在找到索引中值為 2 的點時停止。但是,pandas 無法保證 pd.Index
中只有一個值為 2,因此它必須繼續進行直到找到其他東西。
程式碼解密:
ser = pd.Series(values)
:建立一個pd.Series
物件,包含列表values
中的資料。ser.iloc[:2]
:使用位置索引選擇前兩個元素。ser.loc[:2]
:使用標籤索引選擇直到標籤為 2 的所有元素。
從 DataFrame 中進行根據標籤的選擇
正如我們在“DataFrame 的位置選擇”一節中所討論的,使用 pd.DataFrame
時最常見的使用案例是使用根據標籤的選擇來參照列,使用根據位置的選擇來參照行。然而,這不是絕對的要求,pandas 允許你同時使用根據標籤的選擇來選擇行和列。
與其他資料分析工具相比,從 pd.DataFrame
的行中選擇根據標籤的能力是 pandas 獨有的優勢。對於熟悉 SQL 的使用者來說,語言中沒有提供與此相當的功能;列很容易在 SELECT 子句中選擇,但行只能透過 WHERE 子句過濾。對於擅長 Microsoft Excel 的使用者,可以使用透視表建立二維結構,具有行標籤和列標籤,但您在該透視表中選擇或參照資料的能力實際上是有限的。
如何實作
讓我們建立一個 pd.DataFrame
,其中行和列的索引都由字串組成:
df = pd.DataFrame([
[24, 180, "blue"],
[42, 166, "brown"],
[22, 160, "green"],
], columns=["age", "height_cm", "eye_color"], index=["Jack", "Jill", "Jayne"])
df
輸出:
age height_cm eye_color
Jack 24 180 blue
Jill 42 166 brown
Jayne 22 160 green
pd.DataFrame.loc
可以根據行和列標籤進行選擇:
df.loc["Jayne", "eye_color"]
輸出:
'green'
要從標籤為 “age” 的列中選擇所有行:
df.loc[:, "age"]
輸出:
Jack 24
Jill 42
Jayne 22
Name: age, dtype: int64
圖表示例:DataFrame 結構圖示
graph LR; A[DataFrame] --> B[行索引]; A --> C[列索引]; B --> D[Jack]; B --> E[Jill]; B --> F[Jayne]; C --> G[age]; C --> H[height_cm]; C --> I[eye_color];
圖表翻譯: 上圖展示了 DataFrame 的基本結構,包括行索引和列索引。行索引包含 [“Jack”, “Jill”, “Jayne”],列索引包含 [“age”, “height_cm”, “eye_color”]。
程式碼解密:
df = pd.DataFrame([...])
:建立一個pd.DataFrame
物件,包含指定的資料和索引。df.loc["Jayne", "eye_color"]
:使用根據標籤的索引選擇特定單元格的值。df.loc[:, "age"]
:使用根據標籤的索引選擇整個 “age” 列。