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.count、pd.Series.mean、pd.Series.std、pd.Series.min、pd.Series.max 和 pd.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_42和mean_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.DataFrame,transform 方法預設會對每一列進行轉換。讓我們建立一個簡單的 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)
內容解密:
- 定義了一個函式
debug_apply,該函式列印預出傳入的值但不傳回值(即傳回None)。 - 建立了一個包含數值 0、1 和 2 的 Series
ser。 - 使用
.map()方法將debug_apply應用於ser中的每個元素。 - 由於
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))
內容解密:
- 建立了一個 3x2 的 DataFrame
df,包含數值 0 到 5。 - 使用
.apply()方法將debug_apply應用於df的每一列。 debug_apply被呼叫了兩次,分別對應 DataFrame 的兩列。- 結果是一個 Series,其中每個元素都是
None,因為debug_apply不傳回值。
使用 .value_counts()
ser = pd.Series(["a", "b", "c", "a", "c", "a"], dtype=pd.StringDtype())
print(ser.value_counts())
內容解密:
- 建立了一個包含字串 “a”、“b” 和 “c” 的 Series
ser。 - 使用
.value_counts()方法計算每個唯一值的頻率。 - 結果是一個 Series,其中索引是唯一值,值是對應的頻率。
使用 .describe()
ser = pd.Series([0, 42, 84], dtype=pd.Int64Dtype())
print(ser.describe())
內容解密:
- 建立了一個包含數值 0、42 和 84 的 Series
ser。 - 使用
.describe()方法計算一系列統計量,包括計數、平均值、標準差、最小值和最大值等。 - 結果是一個 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))
內容解密:
- 建立了一個包含姓名和年齡的 DataFrame
df。 - 使用
pd.cut()方法將 “age” 列分成 4 個區間。 - 設定
precision=0以避免小數位數。 - 結果是一個 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 的行。