Pandas 提供了多樣化的聚合運算,從計算平均值、標準差到計數非缺失值,都能輕鬆實作。除了內建方法,也能自定義函式處理更複雜的邏輯,例如計算平均值後加減特定數值。在資料轉換方面,.transform 方法允許對資料進行修改而不改變其結構,方便計算群組百分比等指標。.map 方法則適用於對每個元素進行個別操作,例如將列表元素平均或保留非列表元素。針對類別資料,pd.cut 可以將連續數值資料分組,而 pd.get_dummies 則能進行獨熱編碼,將類別資料轉換為數值表示,方便機器學習模型處理。這些方法都能與 GroupBy 結合使用,提升資料處理的靈活性和效率。

資料分析中的聚合運算

在資料分析過程中,聚合運算(Aggregations)扮演著至關重要的角色。聚合運算能夠將一系列的值簡化為單一的值,幫助分析師快速掌握資料的整體特徵和趨勢。常見的聚合運算包括計算記錄數、總和、平均值、最大值和最小值等。

如何進行聚合運算

許多基本的聚合運算已經被實作為 pd.Series 物件的方法,使得計算常見的輸出結果(如計數、總和、最大值等)變得非常簡單。

首先,建立一個包含隨機數的 pd.Series

np.random.seed(42)
ser = pd.Series(np.random.rand(10_000), dtype=pd.Float64Dtype())

內容解密:

  • 使用 np.random.seed(42) 設定隨機種子,以確保結果的可重現性。
  • pd.Series(np.random.rand(10_000), dtype=pd.Float64Dtype()) 生成一個包含 10,000 個隨機浮點數的序列,並指定資料型別為 Float64

pandas 函式庫提供了許多常用的聚合運算方法,如 pd.Series.countpd.Series.meanpd.Series.stdpd.Series.minpd.Series.maxpd.Series.sum

print(f"Count is: {ser.count()}")
print(f"Mean value is: {ser.mean()}")
print(f"Standard deviation is: {ser.std()}")
print(f"Minimum value is: {ser.min()}")
print(f"Maximum value is: {ser.max()}")
print(f"Summation is: {ser.sum()}")

內容解密:

  • ser.count() 計算序列中的非缺失值數量。
  • ser.mean() 計算序列的平均值。
  • ser.std() 計算序列的標準差。
  • ser.min()ser.max() 分別找出序列中的最小值和最大值。
  • ser.sum() 計算序列的總和。

除了直接呼叫這些方法外,還可以使用 pd.Series.agg 方法,並提供要執行的聚合運算名稱作為字串:

print(f"Count is: {ser.agg('count')}")
print(f"Mean value is: {ser.agg('mean')}")
print(f"Standard deviation is: {ser.agg('std')}")
print(f"Minimum value is: {ser.agg('min')}")
print(f"Maximum value is: {ser.agg('max')}")
print(f"Summation is: {ser.agg('sum')}")

內容解密:

  • ser.agg('count') 等同於 ser.count(),用於計算非缺失值的數量。
  • 使用 ser.agg 可以靈活地指定要執行的聚合運算。

使用 pd.Series.agg 的一個優勢是它可以同時執行多個聚合運算。例如,若要一步計算欄位的最小值和最大值,可以傳遞一個列表給 pd.Series.agg

ser.agg(["min", "max"])

內容解密:

  • 將多個聚合運算名稱放入列表中,傳遞給 ser.agg,即可同時獲得多個聚合結果。

對 DataFrame 進行聚合運算

pd.DataFrame 進行聚合運算時,由於有兩個維度(行和列),因此需要考慮沿著哪個維度進行聚合。

建立一個包含隨機數的 pd.DataFrame

np.random.seed(42)
df = pd.DataFrame(
    np.random.randn(10_000, 6),
    columns=list("abcdef"),
).convert_dtypes(dtype_backend="numpy_nullable")

內容解密:

  • 使用 np.random.randn(10_000, 6) 生成一個 10,000 行、6 列的隨機數矩陣。
  • 指定欄位名稱為 “a” 至 “f”。

預設情況下,使用內建方法(如 pd.DataFrame.sum)進行聚合運算時,會沿著列進行,也就是說每一列都會被個別聚合:

df.sum()

內容解密:

  • 對每一列進行總和計算,並傳回一個包含每列總和的 pd.Series

若要對每行進行聚合,可以指定 axis=1 引數,但需要注意的是,pandas 對 axis=0 的操作有更好的最佳化,因此沿著行進行聚合可能會較慢:

df.sum(axis=1)

內容解密:

  • 對每一行進行總和計算,並傳回一個包含每行總和的 pd.Series

pd.Series 類別似,pd.DataFrame 也具有 .agg 方法,可以用於同時執行多個聚合運算:

df.agg(["min", "max"])

內容解密:

  • 對 DataFrame 的每一列同時計算最小值和最大值,並傳回結果。

自訂聚合函式

除了使用內建的聚合函式外,還可以傳入可呼叫的自訂函式。每個可呼叫函式應接受一個 pd.Series 引數並簡化為一個純量值:

def mean_and_add_42(ser: pd.Series):
    return ser.mean() + 42

def mean_and_sub_42(ser: pd.Series):
    return ser.mean() - 42

np.random.seed(42)
ser = pd.Series(np.random.rand(10_000), dtype=pd.Float64Dtype())
ser.agg([mean_and_add_42, mean_and_sub_42])

內容解密:

  • 定義了兩個自訂函式:mean_and_add_42mean_and_sub_42,分別計算序列平均值後加 42 和減 42。
  • 將這些自訂函式傳遞給 ser.agg,即可獲得自訂的聚合結果。

資料轉換與應用:深入探索 pandas 中的轉換與對映

在資料分析的過程中,pandas 提供了一系列強大的工具來處理和轉換資料。其中,轉換(transformations)和對映(map)是兩個非常重要的概念,它們能夠幫助我們以更靈活和高效的方式處理資料。

資料轉換(Transformations)

與聚合(aggregations)不同,轉換不會將資料縮減為單一值,而是保持原始資料的形狀。這使得轉換成為計算諸如“群組百分比”等指標的有力工具。

如何實作資料轉換

首先,我們建立一個簡單的 pd.Series

ser = pd.Series([-1, 0, 1], dtype=pd.Int64Dtype())

接下來,我們定義一個函式 adds_one,該函式將輸入的 pd.Series 中的每個元素加一:

def adds_one(ser: pd.Series) -> pd.Series:
    return ser + 1

然後,我們使用 transform 方法將 adds_one 函式和 abs 函式應用於 ser

ser.transform(["abs", adds_one])

輸出結果如下:

   abs  adds_one
0    1         0
1    0         1
2    1         2

對於 pd.DataFrametransform 方法預設會對每一列進行轉換。讓我們建立一個簡單的 pd.DataFrame

df = pd.DataFrame(np.arange(-5, 4, 1).reshape(3, -1)).convert_dtypes(dtype_backend="numpy_nullable")

df 應用 abs 函式:

df.transform("abs")

輸出結果如下:

   0  1  2
0  5  4  3
1  2  1  0
2  1  2  3

如果傳遞多個轉換函式給 pd.DataFrame.transform,將得到一個具有多級索引的結果:

def add_42(ser: pd.Series):
    return ser + 42

df.transform(["abs", add_42])

輸出結果如下:

   0           1           2       
   abs  add_42  abs  add_42  abs  add_42
0    5      37    4      38    3      39
1    2      40    1      41    0      42
2    1      43    2      44    3      45

資料轉換與聚合的結合

轉換和聚合可以與 GroupBy 結合使用,計算諸如“群組百分比”等指標。

資料對映(Map)

.map 方法允許我們對 pandas 物件中的每個元素應用自定義函式。

如何實作資料對映

假設我們有一個混合了數字和數字列表的 pd.Series

ser = pd.Series([123.45, [100, 113], 142.0, [110, 113, 119]])

我們定義一個函式 custom_average,該函式對列表中的元素取平均值,對非列表元素保持原值:

def custom_average(value):
    if isinstance(value, list):
        return sum(value) / len(value)
    return value

使用 map 方法將 custom_average 應用於 ser

ser.map(custom_average)

輸出結果如下:

0    123.45
1    106.50
2    142.00
3    114.00
dtype: float64

資料對映與轉換的區別

.map 方法對每個元素進行操作,而 .transform 方法則嘗試對較大的資料序列進行操作。

Apply 方法

.apply 方法是一種常見但容易被濫用的方法。它可以模擬 .map.transform.agg 的行為,但由於其靈活性,可能會導致不可預期的結果。

如何使用 Apply 方法

對於 pd.Series.apply 的行為與 .map 相似:

ser = pd.Series(range(3), dtype=pd.Int64Dtype())

def debug_apply(value):
    print(f"Apply was called with value:\n{value}")

ser.apply(debug_apply)

輸出結果如下:

Apply was called with value:
0
Apply was called with value:
1
Apply was called with value:
2
0    None
1    None
2    None
dtype: object

總之,pandas 中的轉換和對映提供了強大的資料處理能力。瞭解這些方法的區別和適用場景,可以幫助我們更高效地處理和分析資料。

資料分析中的演算法應用

在資料分析中,Pandas 提供了多種方法來處理和轉換資料,包括 .apply().map().agg().transform()。這些方法可以幫助我們對資料進行各種操作,從簡單的數值計算到複雜的資料轉換。

使用 .apply() 方法

.apply() 方法可以用於對 Pandas Series 或 DataFrame 中的資料進行自定義操作。當應用於 Series 時,它會對每個元素呼叫指定的函式;當應用於 DataFrame 時,它預設會對每一列(即每個 Series)呼叫指定的函式。

對 Series 使用 .apply()

import pandas as pd

def debug_apply(value):
    print("Apply was called with value:")
    print(value)
    # 不傳回值,故傳回 None

ser = pd.Series([0, 1, 2])
result = ser.map(debug_apply)
print(result)

內容解密:

  1. 定義了一個函式 debug_apply,該函式列印預出傳入的值但不傳回值(即傳回 None)。
  2. 建立了一個包含數值 0、1 和 2 的 Series ser
  3. 使用 .map() 方法將 debug_apply 應用於 ser 中的每個元素。
  4. 由於 debug_apply 不傳回值,因此結果 Series 中的每個元素都是 None

對 DataFrame 使用 .apply()

import pandas as pd
import numpy as np

df = pd.DataFrame(np.arange(6).reshape(3, -1), columns=list("ab")).convert_dtypes(dtype_backend="numpy_nullable")
print(df.apply(debug_apply))

內容解密:

  1. 建立了一個 3x2 的 DataFrame df,包含數值 0 到 5。
  2. 使用 .apply() 方法將 debug_apply 應用於 df 的每一列。
  3. debug_apply 被呼叫了兩次,分別對應 DataFrame 的兩列。
  4. 結果是一個 Series,其中每個元素都是 None,因為 debug_apply 不傳回值。
使用 .value_counts()
ser = pd.Series(["a", "b", "c", "a", "c", "a"], dtype=pd.StringDtype())
print(ser.value_counts())

內容解密:

  1. 建立了一個包含字串 “a”、“b” 和 “c” 的 Series ser
  2. 使用 .value_counts() 方法計算每個唯一值的頻率。
  3. 結果是一個 Series,其中索引是唯一值,值是對應的頻率。

使用 .describe()

ser = pd.Series([0, 42, 84], dtype=pd.Int64Dtype())
print(ser.describe())

內容解密:

  1. 建立了一個包含數值 0、42 和 84 的 Series ser
  2. 使用 .describe() 方法計算一系列統計量,包括計數、平均值、標準差、最小值和最大值等。
  3. 結果是一個 Series,包含了對資料分佈的總結。

分箱演算法

分箱是將連續變數分類別到離散區間的過程,可以用於將無限多的值轉換為有限的“箱子”以便於分析。

使用 pd.cut()

df = pd.DataFrame([
    ["Jane", 34],
    ["John", 18],
    ["Jamie", 22],
    ["Jessica", 36],
    ["Jackie", 33],
    ["Steve", 40],
    ["Sam", 30],
    ["Stephanie", 66],
    ["Sarah", 55],
    ["Aaron", 22],
    ["Erin", 28],
    ["Elsa", 37],
], columns=["name", "age"]).convert_dtypes(dtype_backend="numpy_nullable")

print(pd.cut(df["age"], 4, precision=0))

內容解密:

  1. 建立了一個包含姓名和年齡的 DataFrame df
  2. 使用 pd.cut() 方法將 “age” 列分成 4 個區間。
  3. 設定 precision=0 以避免小數位數。
  4. 結果是一個 CategoricalDtype,包含了年齡所屬的區間。

資料分組與編碼技術在資料分析中的應用

在資料分析和機器學習的應用中,經常需要對資料進行預處理,以便更好地被數值演算法理解和處理。其中,資料分組和獨熱編碼是兩種常見的技術。

使用 pd.cut 進行資料分組

pd.cut 是一種用於將連續數值資料分組的函式。例如,將年齡資料分組:

import pandas as pd

# 建立範例資料
df = pd.DataFrame({
    "name": ["Jane", "John", "Jamie", "Jessica", "Jackie", "Steve", "Sam", "Stephanie", "Sarah", "Aaron", "Erin", "Elsa"],
    "age": [34, 18, 22, 36, 33, 40, 30, 66, 55, 22, 28, 37]
})

# 使用 pd.cut 分組
df["age_bin"] = pd.cut(df["age"], [10, 20, 30, 40, 50, 60, 999], labels=["10-20", "20-30", "30-40", "40-50", "50-60", "60+"])

print(df)

內容解密:

  • pd.cut 用於將 age 列的數值根據指定的區間 [10, 20, 30, 40, 50, 60, 999] 分組。
  • labels 引數用於指定每個區間的標籤,使結果更易讀。
  • 資料分組預設是右包含的,即每個區間包含右邊界的值。如果需要改變這種行為,可以使用 right=False

獨熱編碼與 pd.get_dummies

獨熱編碼是一種將類別型資料轉換為數值型資料的方法,使其更容易被機器學習演算法處理。pd.get_dummies 是實作獨熱編碼的函式。

# 建立範例資料
ser = pd.Series(["green", "brown", "blue", "amber", "hazel", "amber", "green", "blue", "green"], name="eye_colors")

# 獨熱編碼
dummies = pd.get_dummies(ser, prefix="is")

print(dummies)

內容解密:

  • pd.get_dummies 將類別型資料 ser 轉換為多個布林列,每個列對應一個類別值。
  • prefix="is" 用於在生成的列名前加上指定的字首,使列名更具描述性。

使用 .pipe 方法鏈式呼叫

在寫 pandas 程式碼時,有兩種主要的風格:變數指定和管道式呼叫。管道式呼叫可以使程式碼更簡潔,不需要建立多個中間變數。

# 管道式呼叫範例
df = pd.DataFrame({
    "col1": pd.Series([1, 2, 3], dtype=pd.Int64Dtype()),
    "col2": pd.Series(["a", "b", "c"], dtype=pd.StringDtype()),
})

result = (df
          .assign(col3=lambda x: x["col1"] * 2)
          .pipe(lambda x: x[x["col1"] > 1]))

print(result)

內容解密:

  • .pipe 方法允許將 DataFrame 處理流程鏈式表達,使程式碼更易讀。
  • 在這個範例中,先使用 .assign 新增一列 col3,然後透過 .pipe 篩選出 col1 大於 1 的行。