Pandas 的 pd.Seriespd.DataFrame 物件的核心概念在於其索引系統。理解 pd.Index 如何運作對於資料操作至關重要。預設情況下,Pandas 使用 pd.RangeIndex,但實際應用中,自定義索引更具彈性。設定自定義索引能讓資料更具語義,方便後續的篩選、排序和分析。除了索引,瞭解 pd.Seriespd.DataFrame 的各種屬性,例如 dtypenameshapesize 等,能幫助開發者更有效率地檢視和操作資料。掌握這些基礎知識能為更進階的 Pandas 操作奠定堅實的基礎。

使用 Pandas 的索引與屬性

在使用 Pandas 構建 pd.Seriespd.DataFrame 物件時,你可能會注意到左側的數值從 0 開始,並在每一行資料中逐一遞增。這些數值由 pd.Index 物件負責。當你處理 pd.DataFrame 時,除了左側的行索引(row index)之外,還有一個上方的列索引(column index):

此圖示 此圖示展示了 pd.DataFrame 的行和列索引

除了特定指定之外,Pandas 會自動為你建立一個自動編號的 pd.Index(技術上是一個 pd.RangeIndex,這是 pd.Index 類別的子類別)。然而,在實務應用中,很少使用 pd.RangeIndex 作為列索引,因為直接使用類別似於「City」或「Date」的列名更具表達性。在行索引中,pd.RangeIndex 出現較為常見,但你可能仍希望在那裡顯示自定義標籤。

自定義索引

在構建 pd.Seriespd.DataFrame 的過程中,你可以選擇覆寫預設的行和列索引。以下是如何在構建過程中自訂索引:

import pandas as pd

自訂 pd.Series 的行索引

# 自訂行索引
custom_index = ["dog", "cat", "human"]
series_with_custom_index = pd.Series([4, 4, 2], index=custom_index)
print(series_with_custom_index)
dog      4
cat      4
human    2
dtype: int64

自訂 pd.DataFrame 的行和列索引

# 自訂行和列索引
data = {
    "age": [24, 42],
    "height_cm": [180, 166],
    "favorite_color": ["red", "blue"]
}
index = ["Jack", "Jill"]
columns = ["age", "height_cm", "favorite_color"]
df_with_custom_indices = pd.DataFrame(data, index=index, columns=columns)
print(df_with_custom_indices)
       age  height_cm favorite_color
Jack    24       180            red
Jill    42       166          blue

建立名稱為「animal」的索引並命名序列

index = pd.Index(["dog", "cat", "human"], name="animal")
named_series = pd.Series([4, 4, 2], name="num_legs", index=index)
print(named_series)
animal
dog       4
cat       4
human     2
Name: num_legs, dtype: int64

分析序列屬性

建立一個具有自定義索引並命名的 pd.Series

index = pd.Index(["dog", "cat", "human"], name="animal")
ser = pd.Series([4, 4, 2], name="num_legs", index=index)
print(ser)
animal
dog       4
cat       4
human     2
Name: num_legs, dtype: int64
屬性分析
  • 資料型別:使用 ser.dtype
  • 名稱:使用 ser.name
  • 索引:使用 ser.index
  • 形狀:使用 ser.shape
  • 大小:使用 ser.size
  • 長度:使用內建函式 len(ser)

分析資料框架屬性

建立一個具有自定義索引並命名的資料框架:

index = pd.Index(["Jack", "Jill"], name="person")
df = pd.DataFrame([
    [24, 180, "red"],
    [42, 166, "blue"]
], columns=["age", "height_cm", "favorite_color"], index=index)
print(df)
         age   height_cm favorite_color
person
Jack      24        180            red
Jill      42        166          blue
屬性分析
  • 每列的資料型別:使用 df.dtypes
  • 行索引:使用 df.index
  • 列索引:使用 df.columns
  • 形狀:使用 df.shape
  • 大小:使用 df.size
  • 長度:使用內建函式 len(df)

概述

透過以上方式,玄貓展示瞭如何在構建 Pandas 的序列和資料框架時自訂其索引並分析其屬性。這些技巧可以幫助你更好地理解和操作資料結構,從而更高效地進行資料處理與分析工作。

選擇與指定

在前一章中,我們學習瞭如何建立 pd.Seriespd.DataFrame,並且瞭解它們與 pd.Index 的關係。現在,我們將重點轉向選擇與指定這些關鍵過程。選擇,也稱為索引,被視為一種取得器,用於從 Pandas 物件中檢索值。相對地,指定則是一種設定器,用於更新值。

本章的範例將從如何從 pd.Seriespd.DataFrame 物件中檢索值開始,逐步增加複雜度。我們最終會介紹 pd.MultiIndex,這可以用於分層選擇資料,並以介紹指定運算元結束。Pandas API 在選擇和指定方面非常注重重用相同的方法,這最終使我們能夠在與資料互動時表達出自己的意圖。

在本章結束時,您將能夠高效地從 Pandas 物件中檢索資料並更新其值。我們將在本章中涵蓋以下範例:

  • 基本的 Series 選擇
  • 基本的 DataFrame 選擇
  • 根據位置的 Series 選擇
  • 根據位置的 DataFrame 選擇
  • 根據標籤的 Series 選擇
  • 根據標籤的 DataFrame 選擇
  • 混合根據位置和標籤的選擇
  • DataFrame.filter
  • 按資料型別選擇
  • 透過布林陣列進行選擇/篩選
  • 使用 MultiIndex – 單層級選擇
  • 使用 MultiIndex – 複數層級選擇
  • 使用 MultiIndex – 資料框

此外,還有一些關於指定運算元的介紹:

  • 使用 .loc.iloc 的專案指定
  • 資料框列指定

基本的 Series 選擇

pd.Series 中進行選擇涉及根據其位置或標籤來存取元素。這與透過索引存取列表中的元素或透過鍵存取字典中的元素類別似。pd.Series 物件的多功能性允許直觀且簡單地檢索資料,使其成為資料操作的重要工具。

pd.Series 被視為 Python 中的一個容器,就像內建的列表、元組和字典物件一樣。因此,對於簡單的選擇操作,使用者首先會考慮使用 Python 的索引操作員,即使用 [] 語法。

操作步驟

為了介紹基礎的選擇概念,我們從一個非常簡單的 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 中,我們已經發現了 [] 操作員可以用來從容器中選取元素;例如, some_dictionary[0] 會給您與鍵 0 息配的值。對於 pd.Series ,基本選取行為類別似:

ser[3]
'a'

透過表示式 ser[3] ,Pandas 嘗試在 pd.Series 的索引中查詢標籤 3 ,如果只有一個比對項則傳回與該標籤相關聯的值。

除了從 pd.Series 中選取相關聯的值之外,您也可能希望傳回一個 pd.Series ,因為這樣可以讓標籤 3 與資料元素 “a” 延續關聯。透過提供一個包含單個元素的列表引數來實作:

ser[[3]]
3    a
dtype: object

延伸使用列表引數:如果您的列表包含多個元素,則可以從 pd.Series 中選取多個值:

ser[[0, 2]]
0    a
2    c
dtype: object

假設您使用預設索引:您可以使用類別似於切片 Python 列表的切片引數。例如:要取得不包括位置為3元素之前(但不包括)的一個 pd.Series 的部分元素:

ser[:3]
0    a
1    b
2    c
dtype: object

負切片索引對 Pandas 沒有問題。以下程式碼將選取 pd.Series 的最後四個元素:

ser[-4:]
5     c
6     a
7     b
8     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

#### 若以人話來說:
當我們說我們要切割一塊餅乾成三塊時(從索引1開始),分別拿起每三塊中第一塊餅乾放入盒子裡面(記得要跳過中間兩塊),直到拿到第八塊餅乾時停止。
這樣每三塊餅乾就會有一塊被放入盒子裡面。

接著我們來看看如何對自定義 pd.Index 值進行選取:
讓我們建立一個帶有字串索引標籤的小型 pd.Series ,以供說明:
```python

ser = pd.Series(range(3), index=["Jack", "Jill", "Jayne"])

這樣顯示結果是:


Jack      0

Jill      1

Jayne     2

dtype: int64

若以人話來說:

當你想要拿起某特定人的餅乾時(假設叫Jill),就直接去拿那張餅乾卡上面寫Jill名字的人。 如果你想要再仔細看清楚Jill有沒有被拿錯人時, 就直接把那張寫Jill名字的人拿出來仔細檢查一下。 接著我們再來看看第二個例子:


ser["Jill"]

顯示結果是:


1

若以人話來說:

你去找了寫著Jill名字的人, 然後發現他手上有一張餅乾卡, 因為你只想知道他手上的那張餅乾, 所以你就直接把他手上的那張餅乾卡拿走, 然後其他人都不知道你這樣做。 接著我們再來看看第三個例子:


ser[["Jill"]]

顯示結果是:


 Jill     1

 dtype: int64

若以人話來說:

你去找了寫著Jill名字的人, 然後發現他手上有一張餅乾卡, 因為你想知道他有沒有偷吃那張餅乾, 所以你就把他手上的那張餅乾卡給拿走, 然後其他人都知道你這樣做。 所以其實兩者差別就在於, 是否要讓其他人知道你已經拿走了某人的餅乾卡。

若以程式碼比喻:

當你想要透過Python程式碼來操作某資料時, 它有一種叫做「序列」(Series)跟「資料框」(Dataframe)資料結構。 當我想知道序列中的第幾筆資料時, 用方法「Series[index]」就是可以直接把該筆資料給抽出來。 但若我在抽出該筆資料後, 想要其他人也知道我已經抽出該筆資料時, 就要用「Series[[index]]」這個方法。 總之就是差別在於我是否想讓其他人知道我在抽資料。

其他需要注意事項:

一個常見陷阱是假設使用整數引數進行 [] 操作與從 Python 列表中進行選擇時具有相同行為。這只有在使用預設 pd.Index(技術上稱為 pd.RangeIndex)且該預設 Index 自動編號從0開始時才成立。

當不使用 pd.RangeIndex時需特別注意其行為。 例如:讓我們從一個小型 pd.Series 開始: 它仍然在其 pd.Index 中使用整數但不是自增序列開始於0:


ser = pd.Series(list("abc"), index=[2, 42, 21])

顯示結果是:


2      a

42     b

21     c

 dtype: object

這裡很重要的一點是整數引數根據標籤而不是位置進行選取;即以下程式碼會傳回與標籤為2相關聯的值而不是位置2:


ser[2]

'a'

雖然整數引數按標籤比對而不是按位置, 但是切片仍按位置工作。 以下範例不會在遇到數字2時停止而是傳回前兩個元素:


ser[:2]

  2      a

  42     b

dtype: object

使用者還應熟悉在處理非唯一 pd.Index 的情況下如何進行選取: 例如:讓我們建立一個小型 pd.Series ,其中行索引中的號碼1出現兩次:


ser = pd.Series(["apple", "banana", "orange"], index=[0,1,1])

0        apple

1        banana

1        orange

dtype: object

對於此種 pd.Series ,嘗試按號碼1進行選取不會傳回單一值而是傳回另一個 pd.Series:


ser[1]

 	1        banana

   	1        orange

   dtype: object

然而需要注意的是, 當使用預設 RangeIndex(即自增序列起始於0)進行 [] 操作時,

可能會混淆標籤或位置進行表示。 而實際上不同 Index型別下操作方式不同, 因此建議使用 .loc 和 .iloc方法 。