Pandas 提供了多種資料選擇方法,靈活運用這些技巧可以大幅提升資料處理效率。[] 運算元適用於 DataFrame 的欄位選擇,而 Series 則可利用切片和列表進行多重選擇。然而,當索引非唯一或包含整數時,使用 [] 可能導致混淆,建議採用 .loc.iloc 進行根據標籤和位置的選擇,確保程式碼清晰易懂,避免非預期結果。理解不同選擇方法的特性,才能根據實際需求選擇最佳方案,精確地操作和分析資料。

從Series中進行基本選擇

在前一章中,我們瞭解瞭如何建立pd.Seriespd.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” 列。