Pandas 作為 Python 資料科學領域的核心函式庫,提供高效的資料結構和運算功能。理解 Series 與 DataFrame 的運算機制對於資料分析至關重要。本文從 Series 的基本算術運算出發,逐步深入 DataFrame 的向量化運算、索引對齊及缺失值處理。此外,更涵蓋了聚合運算的應用,包含內建方法與自定義函式,並以程式碼範例佐證,讓讀者能快速掌握 Pandas 的運算技巧,提升資料處理效率。透過索引對齊和廣播機制,Pandas 簡化了複雜的資料操作,讓開發者能更專注於資料分析本身。
基本 pd.Series 算術運算
在探索 pandas 演算法時,最簡單的起點是使用 pd.Series,因為它也是 pandas 函式庫提供的最基本結構。基本的算術運算涵蓋了加法、減法、乘法和除法,正如您將在本文中看到的,pandas 提供了兩種執行這些運算的方法。第一種方法是利用 Python 語言內建的 +、-、* 和 / 運算元,這對於新使用者來說是一種直觀的方式。然而,為了涵蓋 Python 語言未涵蓋的資料分析特定功能,並支援本章稍後將介紹的與 .pipe 連結的方法,pandas 也分別提供了 pd.Series.add、pd.Series.sub、pd.Series.mul 和 pd.Series.div 方法。
如何進行基本 pd.Series 算術運算
首先,讓我們從一個簡單的 Python range 表示式建立一個 pd.Series:
ser = pd.Series(range(3), dtype=pd.Int64Dtype())
ser
輸出結果:
0 0
1 1
2 2
dtype: Int64
讓我們簡要考慮一個表示式,如 a + b。在這樣的表示式中,我們使用了一個二元運算元 (+)。二元指的是需要將兩個東西相加才能使這個表示式有意義,也就是說,只有 a + 這樣的表示式是沒有意義的。這兩個「東西」在技術上被視為運算元;因此,在 a + b 中,我們有左邊的運算元 a 和右邊的運算元 b。
當其中一個運算元是 pd.Series 時,pandas 中最基本的演算法表示式將包含另一個運算元是純量(即單一值)。發生這種情況時,純量值會被廣播到 pd.Series 的每個元素以應用演算法。
例如,如果我們想將數字 42 加到 pd.Series 的每個元素上,我們可以簡單地表達為:
ser + 42
輸出結果:
0 42
1 43
2 44
dtype: Int64
內容解密:
這段程式碼展示瞭如何使用 pandas 的 pd.Series 進行基本的算術運算。首先,建立了一個包含三個元素的 pd.Series,然後使用加法運算元 (+) 將數字 42 加到每個元素上。pandas 能夠以向量化的方式應用加法運算,即將數字 42 一次性應用於所有值,而無需使用者使用 Python 的 for 迴圈。
同樣地,可以使用 - 運算元進行減法運算:
ser - 42
輸出結果:
0 -42
1 -41
2 -40
dtype: Int64
使用 * 運算元進行乘法運算:
ser * 2
輸出結果:
0 0
1 2
2 4
dtype: Int64
使用 / 運算元進行除法運算:
ser / 2
輸出結果:
0 0.0
1 0.5
2 1.0
dtype: Float64
兩個運算元也可以都是 pd.Series:
ser2 = pd.Series(range(10, 13), dtype=pd.Int64Dtype())
ser + ser2
輸出結果:
0 10
1 12
2 14
dtype: Int64
內容解密:
這段程式碼展示了當兩個運算元都是 pd.Series 時,如何進行加法運算。pandas 將根據索引標籤對齊兩個 pd.Series 的元素,然後進行加法運算。
雖然內建的 Python 運算元在大多數情況下是常用且可行的,但 pandas 仍然提供了專門的方法,如 pd.Series.add、pd.Series.sub、pd.Series.mul 和 pd.Series.div:
ser1 = pd.Series([1., 2., 3.], dtype=pd.Float64Dtype())
ser2 = pd.Series([4., pd.NA, 6.], dtype=pd.Float64Dtype())
ser1.add(ser2)
輸出結果:
0 5.0
1 <NA>
2 9.0
dtype: Float64
使用 fill_value= 引數可以處理缺失資料:
ser1.add(ser2, fill_value=0.)
輸出結果:
0 5.0
1 2.0
2 9.0
dtype: Float64
內容解密:
這段程式碼展示瞭如何使用 pd.Series.add 方法進行加法運算,並使用 fill_value= 引數處理缺失資料。當第二個 pd.Series 中有缺失值時,預設情況下結果將是缺失值。使用 fill_value=0. 可以將缺失值視為 0,從而避免結果中的缺失值。
更多內容…
當表示式中的兩個運算元都是 pd.Series 物件時,pandas 將根據行標籤對齊。這種對齊行為被視為一種功能,但也可能令新手感到驚訝。
讓我們考慮兩個具有相同行索引的 pd.Series 物件。當我們將它們相加時,會得到一個相當不令人驚訝的結果:
ser1 = pd.Series(range(3), dtype=pd.Int64Dtype())
ser2 = pd.Series(range(3), dtype=pd.Int64Dtype())
ser1 + ser2
輸出結果:
0 0
1 2
2 4
dtype: Int64
但是,當行索引值不相同時會發生什麼?一種簡單的情況可能涉及將兩個 pd.Series 物件相加,其中一個 pd.Series 使用的行索引是另一個的子集。可以透過下面的程式碼看到這一點,其中 ser3 只有 2 個值,並使用預設的 pd.RangeIndex,其值為 [0, 1]。當與 ser1 相加時,我們仍然得到一個包含 3 個元素的 pd.Series,但只有在兩個 pd.Series 物件中都存在行索引標籤時,才會進行加法運算:
ser3 = pd.Series(range(2), dtype=pd.Int64Dtype())
ser1 + ser3
輸出結果取決於具體的 ser1 和 ser3。
圖表翻譯:
此處可加入 Mermaid 圖表來說明 pandas 如何根據索引標籤對齊兩個 Series 的元素,然後進行加法運算。例如:
graph LR;
A[ser1] -->|索引對齊|> B[相加結果];
C[ser3] -->|索引對齊|> B;
圖表翻譯: 上圖展示了 pandas 如何根據索引標籤對齊兩個 Series 的元素,然後進行加法運算。只有具有相同索引標籤的元素才會被相加。
基本的 pandas 序列運算
在進行資料分析時,pandas 提供了一系列強大的資料結構和運算功能。其中,pd.Series 和 pd.DataFrame 是最常用的兩種資料結構。本章節將重點介紹 pd.Series 和 pd.DataFrame 的基本運算。
pd.Series 運算
首先,我們來探討 pd.Series 的基本運算。假設我們有兩個 pd.Series 物件,ser1 和 ser3,其定義如下:
ser1 = pd.Series([0, 1, 2], dtype=pd.Int64Dtype())
ser3 = pd.Series([2, 4], dtype=pd.Int64Dtype())
當我們將 ser1 和 ser3 相加時,pandas 會根據索引標籤對齊資料。如果某個索引標籤在其中一個序列中不存在,則結果中對應的值將為 <NA>。
ser1 + ser3
輸出結果為:
0 2
1 5
2 <NA>
dtype: Int64
內容解密:
ser1和ser3相加時,索引0和1對齊成功,因此結果分別為2和5。- 索引
2只存在於ser1中,因此結果為<NA>。
索引標籤不同的情況
接下來,我們探討當兩個 pd.Series 物件的索引標籤不同時的情況。
ser4 = pd.Series([2, 4, 8], index=[1, 2, 3], dtype=pd.Int64Dtype())
ser1 + ser4
輸出結果為:
0 <NA>
1 3
2 6
3 <NA>
dtype: Int64
內容解密:
- 索引
1和2同時存在於ser1和ser4中,因此結果分別為3和6。 - 索引
0和3只存在於其中一個序列中,因此結果為<NA>。
索引標籤非唯一情況
如果某個 pd.Series 的索引標籤非唯一,則會導致結果中出現重複的索引標籤。
ser5 = pd.Series([2, 4, 8], index=[0, 1, 1], dtype=pd.Int64Dtype())
ser1 + ser5
輸出結果為:
0 2
1 5
1 9
2 <NA>
dtype: Int64
內容解密:
- 索引
0對齊成功,結果為2。 - 索引
1出現兩次,因此結果中有兩個值,分別為5和9。 - 索引
2只存在於ser1中,因此結果為<NA>。
這種行為類別似於 SQL 中的 FULL OUTER JOIN 操作。
SQL 中的 FULL OUTER JOIN
以下是一個在 PostgreSQL 中實作類別似操作的例子:
WITH ser1 AS (
SELECT * FROM (
VALUES
(0, 0),
(1, 1),
(2, 2)
) AS t(index, val1)
),
ser5 AS (
SELECT * FROM (
VALUES
(0, 2),
(1, 4),
(1, 8)
) AS t(index, val2)
)
SELECT index, val1 + val2 AS value
FROM ser1
FULL OUTER JOIN ser5 USING(index);
輸出結果為:
index | value
------+-------
0 | 2
1 | 5
1 | 9
2 |
(4 rows)
圖表翻譯:
此圖示呈現了 FULL OUTER JOIN 的操作過程。兩個表格根據索引標籤進行連線,如果某個索引標籤在其中一個表格中不存在,則結果中對應的值將為空。
graph LR;
A[ser1] -->|FULL OUTER JOIN|> C[結果];
B[ser5] -->|FULL OUTER JOIN|> C;
基本的 pd.DataFrame 運算
接下來,我們探討 pd.DataFrame 的基本運算。假設我們有一個 $3 \times 3$ 的 pd.DataFrame,其定義如下:
np.random.seed(42)
df = pd.DataFrame(
np.random.randn(3, 3),
columns=["col1", "col2", "col3"],
index=["row1", "row2", "row3"],
).convert_dtypes(dtype_backend="numpy_nullable")
當我們對 df 進行基本運算時,例如加法或乘法,pandas 將根據標籤對齊資料。
df + 1
df * 2
輸出結果分別為:
col1 col2 col3
row1 1.496714 0.861736 1.647689
row2 2.523030 0.765847 0.765863
row3 2.579213 1.767435 0.530526
col1 col2 col3
row1 0.993428 -0.276529 1.295377
row2 3.046060 -0.468307 -0.468274
row3 3.158426 1.534869 -0.938949
與 pd.Series 的運算
當我們將 df 與一個 pd.Series 相加時,pandas 將根據列標籤對齊資料。
ser = pd.Series([20, 10, 0], index=["col1", "col2", "col3"], dtype=pd.Int64Dtype())
df + ser
輸出結果為:
col1 col2 col3
row1 20.496714 9.861736 0.647689
row2 21.523030 9.765847 -0.234137
row3 21.579213 10.767435 -0.469474
圖表翻譯:
此圖示呈現了 DataFrame 與 Series 相加的過程。Series 的索引標籤與 DataFrame 的列標籤對齊,然後進行相加操作。
graph LR;
A[DataFrame] -->|與 Series 相加|> C[結果];
B[Series] -->|與 DataFrame 相加|> C;
控制對齊方式
我們可以使用 axis= 引數控制對齊方式。
ser = pd.Series([20, 10, 0, 42], index=["row1", "row2", "row3", "row4"], dtype=pd.Int64Dtype())
df.add(ser, axis=0)
輸出結果為:
col1 col2 col3
row1 20.496714 19.861736 20.647689
row2 11.523030 9.765847 9.765863
row3 1.579213 0.767435 -0.469474
row4 <NA> <NA> <NA>
DataFrame 之間的運算
兩個 DataFrame 之間的運算也遵循相同的對齊規則。
df * df
輸出結果為:
col1 col2 col3
row1 0.246725 0.019117 0.419500
row2 2.319620 0.054828 0.054820
row3 2.493913 0.588956 0.220406
綜上所述,pandas 提供了一系列強大的資料結構和運算功能,能夠滿足各種資料分析需求。瞭解其基本運算規則和對齊方式,有助於更好地利用 pandas 處理資料。
資料加總與聚合運算
在資料分析過程中,聚合運算(Aggregations)扮演著至關重要的角色。它能夠將一連串的數值簡化為單一數值,使得複雜的資料變得更容易理解和運用。本篇將探討pandas函式庫中的聚合運算功能,並展示如何有效地應用這些運算來處理資料。
建立範例資料
首先,我們建立一個包含隨機數值的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提供了一系列內建的聚合方法,如count、mean、std、min、max和sum等,可以直接對pd.Series進行操作:
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():計算序列中所有數值的總和。
使用.agg()進行聚合運算
除了直接呼叫聚合方法外,pandas還提供了.agg()方法,可以用來執行多種聚合運算:
ser.agg(["min", "max"])
內容解密:
.agg()方法接受一個列表,列表中包含要執行的聚合運算名稱,如min和max。
對pd.DataFrame進行聚合運算
對於pd.DataFrame,聚合運算預設是沿著列(axis=0)進行的:
np.random.seed(42)
df = pd.DataFrame(
np.random.randn(10_000, 6),
columns=list("abcdef"),
).convert_dtypes(dtype_backend="numpy_nullable")
df.sum()
內容解密:
df.sum():對DataFrame的每一列進行求和運算,傳回一個包含每列總和的Series。
若要沿著行(axis=1)進行聚合運算,可以指定axis=1引數:
df.sum(axis=1)
內容解密:
df.sum(axis=1):對DataFrame的每一行進行求和運算,傳回一個包含每行總和的Series。
自定義聚合函式
除了使用內建的聚合函式外,還可以自定義函式並傳遞給.agg()方法:
def mean_and_add_42(ser: pd.Series):
return ser.mean() + 42
def mean_and_sub_42(ser: pd.Series):
return ser.mean() - 42
ser.agg([mean_and_add_42, mean_and_sub_42])
內容解密:
- 自定義函式
mean_and_add_42和mean_and_sub_42分別計算序列的平均值並加上或減去42。 - 將這些自定義函式傳遞給
.agg()方法,以執行自定義的聚合運算。