Python Pandas 索引與分組操作精解

在資料分析領域,Pandas 以其強大的資料處理能力而備受推崇。其中,索引和分組操作更是精華所在,能大幅提升資料處理的效率。本文將結合個人經驗,深入淺出地解析 Pandas 的索引機制、GroupBy 操作,以及實用的切片和篩選技巧。

索引機制:資料存取的根本

不同於 SAS 資料集需要額外建立索引,Pandas DataFrame 在建立時便自動產生行列索引,預設的 RangeIndex 類別似 SAS 的 _N_ 變數。更重要的是,Pandas 的索引機制賦予每一列獨特的標籤,如同資料函式庫的主鍵,方便快速定位和擷取資料。除了預設的整數索引,我們還能以特定欄位建立索引,甚至建立多層次的 MultiIndex

缺失值處理:fillna() 的妙用

真實資料中,缺失值無可避免。Pandas 提供 fillna() 方法填補缺失值,確保資料完整性。以下分享幾種 fillna() 的應用場景:

  1. 以零填補:
import pandas as pd
import numpy as np

data = {'溫度': ['低', '中', '高', '涼', '涼', None, None],
        '速度': ['慢', '中', '快', None, '中', '慢', '慢'],
        '測量值1': [np.nan, 4.2, 9.4, np.nan, 6.1, np.nan, np.nan],
        '測量值2': [2.7, 5.1, 11.0, np.nan, 4.3, 2.9, 2.9],
        '測量值3': [6.6, 7.9, np.nan, 9.1, 12.2, 3.3, 3.3],
        '測量值4': [3.1, 9.1, 6.8, 8.9, 3.7, 1.7, 1.7]}

df = pd.DataFrame(data)

df_filled = df.fillna(0)
print(df_filled)

fillna(0)DataFrame 中所有缺失值(數值型的 NaN 和字串型的 None)替換為 0。在處理數值資料時,這是一種常見的缺失值填補策略。

  1. 針對特定欄位填補:
df_filled_subset = df[['測量值1', '測量值2', '測量值3', '測量值4']].fillna(0)
print(df_filled_subset)

此例僅針對 ‘測量值1’ 到 ‘測量值4’ 這些數值欄位填補缺失值,更精確地控制填補範圍。

  1. 使用字典填補:
fill_values = {'溫度': '低', '速度': '慢', '測量值1': 0, '測量值2': 0, '測量值3': 0, '測量值4': 0}
df_filled_dict = df.fillna(fill_values)
print(df_filled_dict)

使用字典,能為不同欄位指定不同的填補值,例如以 ‘低’ 填補 ‘溫度’,以 0 填補數值欄位,展現高度靈活性。

  1. 以平均值填補:
df_filled_mean = df[["測量值1", "測量值2", "測量值3"]].fillna(df.測量值4.mean())
print(df_filled_mean)

以 ‘測量值4’ 的平均值填補其他測量值欄位。當欄位間數值分佈相對均勻時,此法能有效維持資料的整體特性。

  graph LR
    B[B]
    A[原始 DataFrame] --> B{fillna()}
    B --> C[以 0 填補]
    B --> D[以特定欄位填補]
    B --> E[以字典填補]
    B --> F[以平均值填補]

圖表説明:此流程圖展示 fillna() 的多種應用方式,讓您快速掌握不同填補策略。

切片與篩選:精準擷取資料

Pandas 提供多種切片和篩選方式,如同手術刀般精準擷取所需資料。

  1. [] 運算元: 主要用於選取欄位,或搭配 RangeIndex 選取列。
print(df[['溫度', '速度']])  # 選取 '溫度' 和 '速度' 欄位

[] 運算元搭配欄位名稱列表,即可選取特定欄位。

  1. loc[] 索引器: 使用列標籤和欄位標籤進行選取。
df = df.set_index('溫度')  # 將 '溫度' 設為索引
print(df.loc[['低', '高']])  # 選取索引標籤為 '低' 和 '高' 的列

loc[] 索引器以標籤為依據,精準選取特定列。

  1. iloc[] 索引器: 使用整數位置進行選取。
print(df.iloc[:3])  # 選取前三列資料

iloc[] 索引器以整數位置為依據,類別似 Python 列表的切片操作。

  1. 布林索引: 根據條件篩選資料。
print(df[df['測量值1'] > 5])  # 選取 '測量值1' 大於 5 的列

布林索引提供更進階的篩選方式,根據條件選取符合的列,極具彈性。

  graph LR
    A[DataFrame] --> B{選取方式?}
    B -- 標籤 --> C[loc]
    B -- 位置 --> D[iloc / ]
    B -- 條件 --> E[布林索引]

圖表説明:此圖表展示 Pandas 提供的多種資料選取方式,方便您根據需求選擇合適方法。

Pandas 的索引、fillna()、切片和篩選功能,讓資料處理更精準高效。熟練運用這些技巧,您將能輕鬆駕馭 Pandas,提升資料分析效率。

在資料分析的過程中,我發現善用 Pandas 的索引和切片功能,能有效簡化程式碼,並提升執行效率。尤其是在處理大型資料集時,這些技巧更是不可或缺。

透過以上案例,我們可以看到 Pandas 的索引和切片功能的靈活性,可以根據不同的需求選擇不同的操作策略。

import pandas as pd
import numpy as np

# 設定顯示格式
pd.options.display.float_format = '{:,.2f}'.format

# 建立 MultiIndex (columns)
cols = pd.MultiIndex.from_tuples([
    (x, y) for x in ['Test1', 'Test2', 'Test3'] for y in ['Pre', 'Post']
])

# 建立 MultiIndex (index)
rows = pd.MultiIndex.from_tuples([
    (x, y) for x in ['A', 'B'] for y in ['One', 'Two']
])

np.random.seed(98765)

df = pd.DataFrame(np.random.randn(4, 6), index=rows, columns=cols)

print(df)

這段程式碼示範瞭如何建立更複雜的 MultiIndex,並同時應用於 DataFrame 的行和列。這裡,我們分別為行和列建立了 MultiIndex,然後使用這些 MultiIndex 建立 DataFrame。

  graph LR
    A["建立 MultiIndex (columns)"] --> B["建立 MultiIndex (index)"]
    B --> C["建立 DataFrame"]

上圖展示了建立帶有 MultiIndex 的 DataFrame 的流程。

# 選擇特定資料
print(df['Test2']['Pre'])

透過多層索引,我們可以像使用字典一樣,逐層選取資料。df['Test2']['Pre'] 先選取 ‘Test2’ 層級下的所有資料,再選取 ‘Pre’ 層級下的資料。

# 使用 xs() 選取特定層級的資料
print(df.xs('One', level=1))  # level=1 指的是第二層索引

xs() 方法可以根據特定層級的標籤值選取資料。df.xs('One', level=1) 選取第二層索引為 ‘One’ 的所有資料。

# 使用 stack() 和 unstack() 進行層級轉換
stacked = df.stack()
print(stacked)
unstacked = stacked.unstack()
print(unstacked)

stack() 方法可以將列索引的最後一層轉換為新的最內層列索引,而 unstack() 方法則可以將列索引的最內層轉換為新的最外層列索引。這些方法可以方便地調整資料的層級結構。

  graph LR
    A[DataFrame] --> B[stack]
    B --> C[unstack]
    C --> D[DataFrame]

上圖展示了 stack()unstack() 方法之間的轉換關係。

透過 MultiIndex,我們可以更有效率地組織和處理高維資料,並利用 Pandas 提供的各種方法進行資料分析和轉換。

Pandas 的 MultiIndex 提供了處理高維資料的有效方法,讓資料的組織和分析更加便捷。透過理解 MultiIndex 的建立、選取和轉換方式,我們可以更好地應對複雜的資料分析任務。

import pandas as pd
import numpy as np

# 建立範例 DataFrame
years = [2015, 2016, 2017, 2018]
months = [1, 2, 3]
areas = ['City', 'Rural', 'Suburbs']
whens = ['Day', 'Night']

index = pd.MultiIndex.from_product([years, months], names=['Year', 'Month'])
columns = pd.MultiIndex.from_product([areas, whens], names=['Area', 'When'])
tickets = pd.DataFrame(np.random.randint(1, 50, size=(12, 6)), index=index, columns=columns)


# 選擇 'City' 在 'Day' 時間超過 25 的資料
city_day_over_25 = tickets[tickets[('City', 'Day')] > 25]
print(city_day_over_25)

# 選擇所有 'Day' 時間超過 25 的資料
day_over_25 = tickets[tickets.loc[:, idx[:, 'Day']].gt(25).any(axis=1)]
print(day_over_25)

第一個例子 tickets[tickets[('City', 'Day')] > 25] 選擇 ‘City’ 與 ‘Day’ 的值大於 25 的所有資料列。第二個例子 tickets[tickets.loc[:, idx[:, 'Day']].gt(25).any(axis=1)] 則更為複雜,它首先選取所有 'Day' 時間的資料,然後使用 .gt(25) 判斷是否大於 25,最後使用 .any(axis=1) 判斷每一列是否存在至少一個 True 值,從而選取符合條件的列。

使用 xs() 進行交叉切片

xs() 方法允許您在特定層級上進行切片,並傳回一個新的 DataFrame 或 Series。

# 選擇 2015 年所有月份的資料
tickets_2015 = tickets.xs(2015, level='Year')
print(tickets_2015)

# 選擇所有年份 3 月的資料
tickets_month_3 = tickets.xs(3, level='Month')
print(tickets_month_3)

tickets.xs(2015, level='Year') 選取 Year 層級為 2015 的所有資料,而 tickets.xs(3, level='Month') 選取 Month 層級為 3 的所有資料。xs() 方法提供了一種便捷的方式來提取特定層級的資料。

使用 groupby() 進行分組

groupby() 方法可以根據多層索引的層級對資料進行分組,並執行聚合操作。

# 根據 'Year' 和 'Area' 分組,並計算平均值
grouped = tickets.groupby(level=['Year', 'Area']).mean()
print(grouped)

這段程式碼根據 ‘Year’ 和 ‘Area’ 兩個層級對資料進行分組,並計算每個組的平均值。groupby() 方法結合多層索引可以輕鬆地進行複雜的資料聚合和分析。

  graph LR
    B[B]
    A[原始 DataFrame] --> B{groupby}
    B --> C[分組後的 DataFrame]

總結:本文介紹了多層索引的進階操作技巧,包括使用 IndexSlice、條件切片、xs() 交叉切片以及 groupby() 分組。這些技巧可以幫助您更有效地處理和分析複雜的資料結構。熟練掌握這些技巧將大幅提升您的資料分析效率。

import pandas as pd

# 建立範例 DataFrame
data = {'District': ['I', 'I', 'I', 'I', 'II', 'II', 'II', 'II', 'III', 'III', 'III', 'III', 'III'],
        'Sector': ['North', 'South', 'East', 'West', 'North', 'South', 'East', 'West', 'North', 'South', 'East', 'West', 'West'],
        'Name': ['Patton', 'Joyner', 'Williams', 'Jurat', 'Aden', 'Tanner', 'Jenkins', 'Milner', 'Chang', 'Gupta', 'Haskins', 'LeMay', 'LeMay'],
        'Before': [17, 13, 111, 51, 71, 113, 99, 15, 69, 11, 45, 35, 35],
        'After': [27, 22, 121, 55, 70, 122, 99, 65, 101, 22, 41, 69, 69],
        'Age': [22, 19, 29, 22, 17, 32, 24, 22, 21, 21, 19, 20, 20]}
df = pd.DataFrame(data)

# 使用 pd.cut() 進行分組
bins = [0, 20, 30, 40, 50, 60]
group_names = ['0-20', '21-30', '31-40', '41-50', '51-60']
categories = pd.cut(df['Age'], bins, labels=group_names)

# 將分組結果增加到 DataFrame
df['Age Group'] = categories

# 根據年齡區間進行分組
grouped = df.groupby('Age Group')

# 顯示分組結果
for name, group in grouped:
    print(f"Age Group: {name}")
    print(group)
    print("\n")


# 計算每個年齡區間的統計資訊
print(grouped.agg({'Before': 'mean', 'After': 'mean'}))

這段程式碼首先使用 pd.cut() 方法將 Age 欄位的值分割成不同的區間,並使用 labels 引數指定每個區間的名稱。然後,將分組結果增加到 DataFrame 的 Age Group 欄位。接著,使用 groupby() 方法根據 Age Group 欄位進行分組,並使用迴圈迭代每個組,顯示組名和組內資料。最後,使用 agg() 方法計算每個年齡區間的 ‘Before’ 和 ‘After’ 欄位的平均值。

  graph LR
    A[DataFrame] --> B[pd.cut]
    B --> C[Categorical Data]
    C --> D[groupby]
    D --> E[Grouped DataFrame]
    E --> F[agg]
    F --> G[Statistics]

這個流程圖展示了使用 pd.cut()groupby() 進行分組計算的流程。首先,pd.cut() 將數值資料轉換為分類別資料,然後 groupby() 根據分類別資料進行分組,最後使用 agg() 計算每個組的統計資訊。

透過以上方法,我們可以有效地根據連續數值欄位對資料進行分組,並進行更精細的分析。 Pandas 的 groupby() 方法是資料分析中不可或缺的工具,它可以幫助我們輕鬆地根據不同欄位對資料進行分組,並對每個組進行統計分析。結合 pd.cut() 方法,我們可以更靈活地處理連續數值欄位的分組,從而提取更有價值的資訊。記住善用視覺化圖表,例如 Mermaid 流程圖,可以更清晰地理解資料處理流程,並提升程式碼的可讀性。

連續數值欄位的Pandas分組技巧解析

在資料分析中,我們經常需要根據特定欄位對資料進行分組,然後對每個組進行統計分析。Pandas 的 groupby() 方法提供了一個強大與靈活的工具來實作這個目標。本文將重點探討如何使用 groupby() 處理連續數值欄位,並提供一個清晰的範例,展示如何使用 pd.cut() 方法將連續值分割到不同的區間,然後進行分組和統計分析。

import pandas as pd

# 範例資料
data = {'Age': [22, 38, 26, 35, 45, 60, 28, 32, 55, 70]}
df = pd.DataFrame(data)

# 定義統計函式
def stats(x):
    return {'count': x.count(), 'min': x.min(), 'max': x.max(), 'mean': x.mean()}

# 定義區間和標籤
bins = [0, 25, 50, 75, 200]
gp_labels = ['0 to 25', '26 to 50', '51 to 75', 'Over 75']

# 使用 pd.cut() 方法
df['Age_Group'] = pd.cut(df['Age'], bins, labels=gp_labels, right=False)

# 使用 groupby() 和 apply() 方法
print(df['Age'].groupby(df['Age_Group']).apply(stats).unstack())

首先,我們匯入了 Pandas 函式庫並建立了一個包含年齡資料的 DataFrame。接著,定義了一個名為 stats 的函式,用於計算每個組的 count、min、max 和 mean 統計值。然後,使用 pd.cut() 方法將年齡資料分割到不同的區間,並使用 right=False 引數確保區間左閉右開。groupby() 方法根據新的 Age_Group 欄位進行分組,apply() 方法將 stats 函式應用於每個組,最後 unstack() 方法重新調整結果的形狀,使其更易於閲讀。

  graph LR
    B[B]
    A["定義統計函式 stats"] --> B{"定義區間和標籤"}
    B --> C["使用 pd.cut() 分割資料"]
    C --> D["使用 groupby() 分組"]
    D --> E["使用 apply() 應用統計函式"]
    E --> F["使用 unstack() 重新調整結果"]

這個流程圖清晰地展示了資料處理的步驟:定義統計函式、定義區間和標籤、使用 pd.cut() 分割資料、使用 groupby() 分組、應用統計函式以及重新調整結果。

透過以上方法,我們可以有效地利用 Pandas 的 groupby() 方法對資料進行分組和統計分析,即使是連續數值欄位也能輕鬆處理。 pd.cut() 方法的應用,讓我們能夠更精細地控制分組的區間,從而更好地理解資料的分佈和趨勢。

在資料分析中,Pandas 的 groupby() 方法提供了一個強大與靈活的工具,可以讓我們根據不同的需求對資料進行分組和分析。透過結合不同的聚合函式和過濾條件,我們可以從資料中提取有價值的資訊。這個技巧在實際資料分析中非常有用,可以幫助我們更好地理解資料的分佈和趨勢。