在資料科學領域,使用 pandas 處理 DataFrame 是家常便飯。熟練掌握資料篩選和指定技巧能大幅提升程式碼效率和可讀性。本文將介紹如何混合使用 .loc.iloc 進行標籤和位置篩選,並探討 filter 方法的應用。此外,我們還會深入研究如何利用 get_indexer 最佳化效能,並解析布林陣列、MultiIndex 和布林運算元在資料篩選中的應用場景。最後,文章將示範如何在 DataFrame 中進行欄位指定,包含單層索引和多層索引的情況,並介紹如何使用 assign 方法實作優雅的鏈式操作。

資料篩選與指定:混合使用標籤與位置篩選

在 pandas 的資料處理中,經常需要在 DataFrame 中進行資料篩選與指定。pandas 提供了 .loc.iloc 兩種方法,分別用於標籤式和位置式篩選。本章節將探討如何混合使用這兩種方法,以及如何使用 DataFrame.filter 方法進行資料篩選。

混合使用標籤與位置篩選

當需要根據標籤篩選某一維度,而根據位置篩選另一維度時,使用者必須採取額外步驟。因為 .iloc 是用於位置式篩選,而 .loc 是用於標籤式篩選。

程式碼範例:

import pandas as pd

# 建立一個 DataFrame
df = pd.DataFrame([
    [24, 180, "blue"],
    [42, 166, "brown"],
    [22, 160, "green"],
], columns=["age", "height_cm", "eye_color"])

# 使用 get_indexer 將標籤轉換為位置索引
col_idxer = df.columns.get_indexer(["age", "eye_color"])
print(col_idxer)  # 輸出:array([0, 2])

# 使用 .iloc 進行位置式篩選
print(df.iloc[[0, 1], col_idxer])

#### 內容解密:
- `df.columns.get_indexer(["age", "eye_color"])` 用於取得 "age"  "eye_color" 欄位的位置索引
- `df.iloc[[0, 1], col_idxer]` 使用取得的位置索引對 DataFrame 進行篩選選取第 0 和第 1 以及 "age"  "eye_color" 欄位

輸出結果:

   age eye_color
0   24      blue
1   42     brown

使用 DataFrame.filter 方法

DataFrame.filter 是一個專門用於從 DataFrame 的列或欄中篩選資料的方法。

程式碼範例:

# 建立一個具有自定義索引的 DataFrame
df = pd.DataFrame([
    [24, 180, "blue"],
    [42, 166, "brown"],
    [22, 160, "green"],
], columns=["age", "height_cm", "eye_color"], index=["Jack", "Jill", "Jayne"])

# 使用 filter 方法篩選欄位
print(df.filter(["age", "eye_color"]))

#### 內容解密:
- `df.filter(["age", "eye_color"])` 用於篩選出 "age"  "eye_color" 欄位
- `df.filter(["Jack", "Jill"], axis=0)` 用於篩選出 "Jack"  "Jill" 

輸出結果:

      age eye_color
Jack   24      blue
Jill   42     brown
Jayne  22     green

效能比較

使用 get_indexer 方法與分步篩選方法的效能比較:

import timeit

def get_indexer_approach():
    col_idxer = df.columns.get_indexer(["age", "eye_color"])
    return df.iloc[[0, 1], col_idxer]

def two_step_approach():
    return df[["age", "eye_color"]].iloc[[0, 1]]

print(timeit.timeit(get_indexer_approach, number=10000))
print(timeit.timeit(two_step_approach, number=10000))

#### 內容解密:
- `get_indexer_approach` 使用 `get_indexer` 方法進行混合式篩選
- `two_step_approach` 使用分步篩選方法
- `timeit.timeit` 用於比較兩種方法的執行時間

結果分析:

使用 get_indexer 方法通常比分步篩選方法更高效,因為它避免了建立中間 DataFrame。

  graph LR;
    A[開始] --> B[建立 DataFrame];
    B --> C[使用 get_indexer 方法];
    C --> D[使用 .iloc 方法];
    D --> E[輸出結果];
    B --> F[使用 filter 方法];
    F --> G[輸出結果];

圖表翻譯: 此圖表呈現了兩種不同的資料篩選流程。首先,建立一個 DataFrame,然後可以選擇使用 get_indexer 方法結合 .iloc 方法進行混合式篩選,或者直接使用 filter 方法進行資料篩選,最終輸出結果。兩種方法都能有效地達到資料篩選的目的,但在使用大型資料集時,get_indexer 方法具有更好的效能表現。

資料篩選與指定

使用標籤進行篩選

在 pandas 中,可以使用 like=regex= 引數根據標籤進行篩選。

使用 like= 引數

df.filter(like="_")

這個範例會選取任何包含底線(_)的欄位標籤。

使用 regex= 引數

df.filter(regex=r"^Ja.*(?<!e)$", axis=0)

這個範例會選取任何以 “Ja” 開頭但不以 “e” 結尾的列標籤。

根據資料型別進行篩選

可以使用 select_dtypes 方法根據資料型別進行篩選。

範例程式碼

df = pd.DataFrame([
    [0, 1.0, "2"],
    [4, 8.0, "16"],
], columns=["int_col", "float_col", "string_col"])

# 選取整數型別的欄位
df.select_dtypes("int")

內容解密:

  • pd.DataFrame.select_dtypes("int") 用於選取整數型別的欄位。
  • 可以透過傳遞列表引數來選取多種型別的欄位,例如 df.select_dtypes(include=["int", "float"])
  • 使用 exclude= 引數可以排除特定型別的欄位,例如 df.select_dtypes(exclude=["int", "float"])

使用布林陣列進行篩選

可以使用布林陣列(也稱為遮罩)來選取資料的子集。

範例程式碼

mask = [True, False, True]
ser = pd.Series(range(3))

# 使用遮罩選取資料
ser[mask]

內容解密:

  • ser[mask] 會傳回遮罩中對應值為 True 的列。
  • pd.Series.loc 方法與 pd.Series[] 在此例中的行為相同。
  • 當使用布林陣列作為 pd.DataFrame[] 的引數時,會沿著列進行匹配。

組合布林運算元進行篩選

可以使用布林運算元(如 OR、AND、INVERT)來組合遮罩。

範例程式碼

blue_eyes = df["eye_color"] == "blue"
green_eyes = df["eye_color"] == "green"
mask = blue_eyes | green_eyes

# 使用組合遮罩選取資料
df[mask]

內容解密:

  • blue_eyes | green_eyes 使用 OR 運算元組合兩個遮罩,選取眼睛顏色為藍色或綠色的記錄。
  • 可以使用 AND 運算元 & 來選取滿足多個條件的記錄。
  • INVERT 運算元 ~ 可以用來反轉遮罩,選取不滿足特定條件的記錄。

使用 MultiIndex 進行篩選

pd.MultiIndex 支援階層式標籤,可以使用 pd.DataFrame.loc 方法進行篩選。

範例程式碼

index = pd.MultiIndex.from_tuples([
    ("John", "Smith"),
    ("Jane", "Doe"),
])

# 建立一個簡單的 Series
ser = pd.Series(range(2), index=index)

# 使用 loc 方法進行篩選
ser.loc[("John", "Smith")]

內容解密:

  • pd.MultiIndex.from_tuples 用於從元組列表建立 pd.MultiIndex
  • 使用 pd.DataFrame.loc 方法可以避免使用 pd.DataFrame[] 時可能出現的歧義。

使用 pd.MultiIndex 進行資料選擇與指定

在 pandas 中,pd.MultiIndex 提供了一種強大的方式來處理具有多層索引的資料。本文將探討如何使用 pd.MultiIndex 進行資料選擇與指定。

建立 pd.MultiIndex

首先,我們需要建立一個 pd.MultiIndex 物件。這可以透過 pd.MultiIndex.from_tuples() 方法實作:

index = pd.MultiIndex.from_tuples([
    ("John", "Smith"),
    ("John", "Doe"),
    ("Jane", "Doe"),
    ("Stephen", "Smith"),
], names=["first_name", "last_name"])

內容解密:

  • pd.MultiIndex.from_tuples():用於從元組列表建立 pd.MultiIndex
  • names 引數:用於指定各層索引的名稱。

使用 pd.Series.loc 進行選擇

建立好 pd.MultiIndex 後,我們可以將其用於 pd.Series 的索引:

ser = pd.Series(range(4), index=index)
ser

輸出結果:

first_name  last_name
John        Smith      0
            Doe        1
Jane        Doe        2
Stephen     Smith      3
dtype: int64

內容解密:

  • ser.loc["John"]:選擇第一層索引為 “John” 的所有資料。
  • ser.loc[["John"]]:同樣選擇第一層索引為 “John” 的資料,但保留 pd.MultiIndex 的結構。

多層索引的選擇

我們可以透過傳遞元組給 loc 方法來選擇多層索引的資料:

ser.loc[("Jane", "Doe")]

輸出結果:2

內容解密:

  • ser.loc[("Jane", "Doe")]:選擇第一層索引為 “Jane” 且第二層索引為 “Doe” 的資料。
  • ser.loc[(["Jane"], "Doe")]:同樣選擇上述資料,但保留 pd.MultiIndex 的結構。

使用 slice(None) 進行選擇

有時,我們需要選擇某一層索引的所有資料,可以使用 slice(None)

ser.loc[(slice(None), "Doe")]

輸出結果:

first_name
John    1
Jane    2
dtype: int64

內容解密:

  • slice(None):代表選擇該層索引的所有資料。
  • ser.loc[(slice(None), ["Doe"])]:選擇第二層索引為 “Doe” 的所有資料,並保留 pd.MultiIndex 的結構。

使用 pd.IndexSlice 簡化語法

為了簡化使用 slice(None) 的語法,pandas 提供了 pd.IndexSlice

ixsl = pd.IndexSlice
ser.loc[ixsl[:, ["Doe"]]]

輸出結果:

first_name  last_name
John        Doe        1
Jane        Doe        2
dtype: int64

內容解密:

  • pd.IndexSlice:提供了一種更自然的方式來進行切片操作。

pd.DataFrame 中使用 pd.MultiIndex

我們也可以在 pd.DataFrame 中使用 pd.MultiIndex 作為行索引和列索引:

row_index = pd.MultiIndex.from_tuples([
    ("John", "Smith"),
    ("John", "Doe"),
    ("Jane", "Doe"),
    ("Stephen", "Smith"),
], names=["first_name", "last_name"])

col_index = pd.MultiIndex.from_tuples([
    ("music", "favorite"),
    ("music", "last_seen_live"),
    ("art", "favorite"),
], names=["art_type", "category"])

df = pd.DataFrame([
    ["Swift", "Swift", "Matisse"],
    ["Mozart", "T. Swift", "Van Gogh"],
    ["Beatles", "Wonder", "Warhol"],
    ["Jackson", "Dylan", "Picasso"],
], index=row_index, columns=col_index)

內容解密:

  • 建立了一個具有多層行索引和列索引的 pd.DataFrame

使用 .loc.iloc 進行指定

pandas 主要最佳化了資料的讀取和探索,但我們仍然可以使用 .loc.iloc 進行資料的修改:

ser = pd.Series(range(3), index=list("abc"))
ser.loc["b"] = 42

輸出結果:

a      0
b     42
c      2
dtype: int64

內容解密:

  • 使用 .loc 將索引為 “b” 的值修改為 42。

總之,本文介紹瞭如何使用 pd.MultiIndex 在 pandas 中進行資料選擇和指定。無論是單層還是多層索引,pandas 都提供了靈活的方法來操作資料。透過瞭解這些方法,我們可以更有效地處理和分析具有複雜索引結構的資料。

資料選擇與指定

在 pandas 中,資料的選擇與指定是常見的操作。本章節將介紹如何使用 pd.Seriespd.DataFrame 進行資料的選擇與指定。

使用 pd.Series.locpd.Series.iloc 進行指定

當需要對 pd.Series 中的特定元素進行指定時,可以使用 lociloc 方法。loc 方法根據標籤進行指定,而 iloc 方法則根據位置進行指定。

ser = pd.Series([0, 1, 2], index=list("abc"))
ser.loc["b"] = 42
ser

內容解密:

  • ser.loc["b"] = 42ser 中標籤為 “b” 的元素指定為 42。
  • 使用 loc 方法可以根據標籤對特定的元素進行修改。
ser.iloc[2] = -42
ser

內容解密:

  • ser.iloc[2] = -42ser 中位置為 2 的元素(即第三個元素)指定為 -42。
  • 使用 iloc 方法可以根據位置對特定的元素進行修改。

資料框(DataFrame)的欄位指定

pd.DataFrame 指定新欄位是一項常見的操作。可以使用 pd.DataFrame[] 運算元來新增欄位。

df = pd.DataFrame({"col1": [1, 2, 3]})
df["new_column1"] = 42
df

內容解密:

  • df["new_column1"] = 42 新增了一個名為 “new_column1” 的欄位,並將所有元素指定為 42。
  • 這種操作會將標量值廣播到 DataFrame 的每一行。
df["new_column2"] = list("abc")
df["new_column3"] = pd.Series(["dog", "cat", "human"])
df

內容解密:

  • df["new_column2"] = list("abc") 新增了一個名為 “new_column2” 的欄位,並將其指定為列表 [“a”, “b”, “c”]。
  • df["new_column3"] = pd.Series(["dog", "cat", "human"]) 新增了一個名為 “new_column3” 的欄位,並將其指定為一個 Series。
  • 這兩種操作都需要確保指定的序列長度與 DataFrame 的行數相匹配。

對具有多層索引(MultiIndex)的 DataFrame 進行指定

當 DataFrame 具有多層索引時,可以透過傳遞 tuple 給 pd.DataFrame.loc 來進行指定。

row_index = pd.MultiIndex.from_tuples([
    ("John", "Smith"),
    ("John", "Doe"),
    ("Jane", "Doe"),
    ("Stephen", "Smith"),
], names=["first_name", "last_name"])

col_index = pd.MultiIndex.from_tuples([
    ("music", "favorite"),
    ("music", "last_seen_live"),
    ("art", "favorite"),
], names=["art_type", "category"])

df = pd.DataFrame([
    ["Swift", "Swift", "Matisse"],
    ["Mozart", "T. Swift", "Van Gogh"],
    ["Beatles", "Wonder", "Warhol"],
    ["Jackson", "Dylan", "Picasso"],
], index=row_index, columns=col_index)

df.loc[:, ("art", "museuems_seen")] = [1, 2, 4, 8]
df

內容解密:

  • df.loc[:, ("art", "museuems_seen")] = [1, 2, 4, 8] 在 “art” 索引下新增了一個名為 “museuems_seen” 的欄位,並將其指定為 [1, 2, 4, 8]。
  • 這種操作展示瞭如何對具有多層索引的 DataFrame 進行欄位新增和指定。

使用 pd.DataFrame.assign 方法進行鏈式操作

pd.DataFrame.assign 方法允許在鏈式操作中新增欄位,這對於保持程式碼的連貫性和可讀性非常有幫助。

df = pd.DataFrame([[0, 1], [2, 4]], columns=list("ab"))

(
    df
    .mul(2)
    .add(42)
    .assign(chained_c=lambda df: df["b"] - 3)
)

內容解密:

  • .mul(2) 將 DataFrame 中的每個元素乘以 2。
  • .add(42) 將上一步的結果加上 42。
  • .assign(chained_c=lambda df: df["b"] - 3) 新增了一個名為 “chained_c” 的欄位,其值為原 DataFrame 中 “b” 欄位的值減去 3。
  • 這種鏈式操作使得程式碼更加簡潔和易於理解。