NumPy 是 Python 資料科學的根本,其高效的陣列操作是關鍵。理解陣列的索引和切片才能有效率地存取和操作資料。重塑陣列能調整資料結構以符合不同演算法需求,例如將一維資料轉換為二維矩陣。陣列的串接和分割則能合併或拆分資料,方便資料重組與分析。這些技巧對於資料科學工作流程至關重要,能最佳化資料處理效能。
import numpy as np
# 設定隨機種子
rng = np.random.default_rng(seed=1701)
# 建立範例陣列
x1 = rng.integers(10, size=6)
x2 = rng.integers(10, size=(3, 4))
x3 = rng.integers(10, size=(3, 4, 5))
NumPy陣列基礎
NumPy(Numerical Python)是一個強大的Python函式庫,用於高效地處理大型多維陣列和矩陣。NumPy陣列是NumPy中的一個基本資料結構,它能夠以一個單一的資料結構來表示多維陣列,並提供了大量的函式和方法來操作這些陣列。
NumPy陣列的優點包括:
- 高效的記憶體使用:NumPy陣列能夠以緊湊的形式儲存大型資料集,這使得它們非常適合於需要高效記憶體使用的情況。
- 快速的運算:NumPy陣列提供了大量的向量化運算,這使得它們能夠比傳統的Python列表快得多。
- 豐富的函式庫:NumPy提供了大量的函式和方法來操作陣列,包括基本的算術運算、統計函式、線性代數運算等。
瞭解NumPy陣列屬性
在進行資料操作時,瞭解NumPy陣列的屬性是非常重要的。這些屬性可以告訴我們陣列的維度、大小、資料型別等資訊。讓我們先建立一些隨機陣列來示範。
import numpy as np
# 設定隨機種子以確保結果的一致性
rng = np.random.default_rng(seed=1701)
# 建立一維、兩維和三維陣列
x1 = rng.integers(10, size=6)
x2 = rng.integers(10, size=(3, 4))
x3 = rng.integers(10, size=(3, 4, 5))
# 顯示陣列的維度(ndim)
print("x3 的維度(ndim):", x3.ndim)
# 顯示陣列的形狀(shape)
print("x3 的形狀(shape):", x3.shape)
內容解密:
ndim屬性代表陣列的維度數量。shape屬性傳回一個元組,描述陣列每個維度的大小。size屬性傳回陣列中元素的總數。dtype屬性表示陣列中元素的資料型別。
圖表翻譯:
這個流程圖描述了NumPy陣列的屬性以及它們傳回的資訊。瞭解這些屬性對於後續的資料操作和分析是非常重要的。
陣列索引:存取單一元素
如果你熟悉Python的標準列表索引,NumPy的索引會感到非常熟悉。在一維陣列中,索引值(從零開始計數)可以透過陣列名稱加上索引值來存取,就像Python列表一樣。
例如,假設我們有一個名為x1的一維陣列,內容為 [9, 4, 0, 3, 8, 6]。要存取第一個元素,可以使用 x1[0],結果將是 9。同樣,要存取第五個元素,可以使用 x1[4],結果將是 8。
import numpy as np
# 建立一維陣列
x1 = np.array([9, 4, 0, 3, 8, 6])
# 存取第一個元素
print(x1[0]) # 輸出: 9
# 存取第五個元素
print(x1[4]) # 輸出: 8
此外,還可以使用負索引從陣列末尾開始計數。例如, -1 代表最後一個元素, -2 代表倒數第二個元素,以此類別推。
# 存取最後一個元素
print(x1[-1]) # 輸出: 6
# 存取倒數第二個元素
print(x1[-2]) # 輸出: 8
在多維陣列中,元素可以使用以逗號分隔的元組(行,列)來存取。例如,假設我們有一個三維陣列 x3,形狀為 (3, 4, 5)。要存取特定元素,可以使用 (行, 列, 深度) 的索引方式。
# 建立三維陣列
x3 = np.random.randint(0, 10, size=(3, 4, 5))
# 存取特定元素
print(x3[0, 1, 2]) # 輸出: 元素值
圖表翻譯:
在這個圖表中,我們展示了陣列索引的基本概念,包括一維陣列和多維陣列的索引方式。這有助於讀者更好地理解如何存取陣列中的特定元素。
瞭解 NumPy 陣列索引和切片
NumPy 陣列提供了強大的索引和切片功能,讓您可以輕鬆存取和操作陣列中的元素。在本文中,我們將探討如何使用索引和切片來存取 NumPy 陣列中的元素。
索引
索引是存取 NumPy 陣列中特定元素的方法。您可以使用方括號 [] 來指定要存取的元素。例如,假設我們有以下陣列:
x2 = np.array([[3, 1, 3, 7],
[4, 0, 2, 3],
[0, 0, 6, 9]])
我們可以使用索引來存取特定元素,如下所示:
x2[0, 0] # 存取第一行第一列的元素
x2[2, 0] # 存取第三行第一列的元素
x2[2, -1] # 存取第三行最後一列的元素
您也可以使用索引來修改元素的值,如下所示:
x2[0, 0] = 12
這將修改第一行第一列的元素為 12。
切片
切片是存取 NumPy 陣列中的一部分元素的方法。您可以使用方括號 [] 來指定要存取的切片。切片的語法如下:
x[start:stop:step]
其中 start 是切片的起始索引,stop 是切片的結束索引,step 是切片的步長。如果未指定 start、stop 或 step,則它們將預設為 0、陣列大小和 1。
以下是一些切片的例子:
x1 = np.array([3, 4, 0, 3, 8, 6])
x1[:3] # 存取前三個元素
x1[1:4] # 存取第二個到第四個元素
x1[1:4:2] # 存取第二個到第四個元素,步長為 2
注意,切片傳回的是原始陣列的一部分,並不會修改原始陣列。
多維陣列切片
當處理多維陣列時,您可以使用相同的切片語法來存取特定維度的切片。例如:
x2 = np.array([[3, 1, 3, 7],
[4, 0, 2, 3],
[0, 0, 6, 9]])
x2[:, :2] # 存取所有行的前兩列
x2[1:, 1:] # 存取第二行以後的所有行和第二列以後的所有列
這些是基本的 NumPy 陣列索引和切片功能。透過掌握這些功能,您可以更有效地操作和分析您的資料。
圖表翻譯:
在這個圖表中,我們展示了 NumPy 陣列的索引和切片功能。索引允許您存取特定元素,而切片允許您存取陣列的一部分。透過指定 start、stop 和 step,您可以控制切片的範圍和步長。最終,切片傳回的是原始陣列的一部分。
多維陣列切片與索引
在進行資料分析時,瞭解如何有效地存取和操作多維陣列中的元素至關重要。Python 的 NumPy函式庫提供了一種強大的方式來處理多維陣列,包括切片和索引。
單一維度陣列切片
單一維度陣列的切片與列表的切片類別似,但更為強大。以下是一些範例:
x1[3:]:從索引 3 到結尾的所有元素。x1[1:4]:從索引 1 到 3 的所有元素(不含索引 4)。x1[::2]:每個第二個元素,從開始到結尾。x1[1::2]:每個第二個元素,從索引 1 開始。
負步長切片
當步長為負數時,切片會從右到左進行。例如:
x1[::-1]:所有元素,以反向順序。x1[4::-2]:從索引 4 開始,每個第二個元素,以反向順序。
多維陣列切片
對於多維陣列,切片可以在每個維度上進行,使用逗號分隔不同的維度。例如:
x2[:2, :3]:前兩行和前三列的元素。x2[:3, ::2]:前三行和每個第二列的元素。
這些切片方法可以用來提取多維陣列中的特定子集,對於資料分析和科學計算非常有用。
結合切片和索引
在某些情況下,您可能需要結合切片和索引來存取特定的元素或子陣列。例如:
x2[1, :3]:第二行(索引 1)的前三個元素。x2[:2, 1]:前兩行(索引 0 和 1)的第二個元素(索引 1)。
這種結合提供了更大的靈活性和控制力,讓您可以精確地存取和操作多維陣列中的資料。
內容解密:
import numpy as np
# 建立一個單一維度陣列
x1 = np.array([3, 4, 0, 3, 8, 6])
# 切片範例
print(x1[3:]) # [3 8 6]
print(x1[1:4]) # [4 0 3]
print(x1[::2]) # [3 0 8]
print(x1[1::2]) # [4 3 6]
# 負步長切片範例
print(x1[::-1]) # [6 8 3 0 4 3]
print(x1[4::-2]) # [8 0 3]
# 建立一個多維陣列
x2 = np.array([[12, 1, 3, 7],
[4, 0, 2, 3],
[0, 0, 6, 9]])
# 多維陣列切片範例
print(x2[:2, :3]) # [[12 1 3]
# [4 0 2]]
print(x2[:3, ::2]) # [[12 3]
# [4 2]
# [0 6]]
圖表翻譯:
這個流程圖展示瞭如何從建立單一維度陣列開始,進行切片操作、負步長切片、多維陣列切片,然後結合切片和索引,最終應用於資料分析和科學計算。
NumPy陣列操作:索引與切片
NumPy陣列提供了強大的索引和切片功能,讓我們可以輕鬆地存取和操作陣列中的元素。
切片操作
切片操作是指從陣列中提取一部分元素的過程。NumPy陣列的切片操作可以使用以下語法:
array[start:stop:step]
其中,start是切片的起始索引,stop是切片的結束索引,step是切片的步長。
例如,假設我們有以下陣列:
x2 = np.array([[12, 1, 3, 7],
[4, 0, 2, 3],
[0, 0, 6, 9]])
我們可以使用切片操作提取第一行和第一列的元素:
x2[:, 0] # 第一列
x2[0, :] # 第一行
這兩個表示式都會傳回一個新的陣列,分別包含第一列和第一行的元素。
單行和單列存取
如果我們只需要存取單行或單列的元素,可以使用以下語法:
array[row_index] # 存取單行
array[:, col_index] # 存取單列
例如:
x2[0] # 等同於 x2[0, :]
x2[:, 0]
子陣列作為無複製檢視
NumPy陣列的切片操作會傳回一個無複製檢視(view),也就是說,傳回的陣列並不是原始陣列的複製,而是一個指向原始陣列的檢視。
這意味著,如果我們修改了子陣列的元素,原始陣列也會被修改。例如:
x2_sub = x2[:2, :2]
x2_sub[0, 0] = 10
print(x2)
這會輸出:
[[10 1 3 7]
[4 0 2 3]
[0 0 6 9]]
如你所見,原始陣列x2也被修改了。
內容解密:
在上面的例子中,我們使用了切片操作提取了x2陣列的子陣列x2_sub。由於NumPy陣列的切片操作會傳回一個無複製檢視,所以當我們修改了x2_sub的元素時,原始陣列x2也會被修改。
圖表翻譯:
在這個圖表中,我們展示瞭如何使用切片操作提取x2陣列的子陣列x2_sub,以及如何修改x2_sub的元素從而影響原始陣列x2。
陣列檢視與複製
在 NumPy 中,當您從陣列中抽取子陣列時,會建立一個陣列檢視(array view)。這意味著子陣列並不會複製原始陣列的資料,而是指向相同的記憶體位置。因此,當您修改子陣列時,原始陣列也會受到影響。
陣列檢視的範例
import numpy as np
# 建立一個 3x4 的陣列
x2 = np.array([[99, 1, 3, 7], [4, 0, 2, 3], [0, 0, 6, 9]])
# 抽取子陣列
x2_sub = x2[:2, :2]
# 修改子陣列
x2_sub[0, 0] = 99
print(x2_sub)
print(x2)
輸出結果:
[[99 1]
[ 4 0]]
[[99 1 3 7]
[ 4 0 2 3]
[ 0 0 6 9]]
如您所見,當我們修改子陣列 x2_sub 時,原始陣列 x2 也會受到影響。
複製陣列
如果您想要建立一個子陣列的複製品,可以使用 copy() 方法:
x2_sub_copy = x2[:2, :2].copy()
現在,當您修改 x2_sub_copy 時,原始陣列 x2 不會受到影響:
x2_sub_copy[0, 0] = 42
print(x2_sub_copy)
print(x2)
輸出結果:
[[42 1]
[ 4 0]]
[[99 1 3 7]
[ 4 0 2 3]
[ 0 0 6 9]]
內容解密:
x2[:2, :2]用於抽取x2陣列中的前兩行和前兩列,建立一個子陣列檢視。copy()方法用於建立子陣列的複製品。- 當您修改子陣列檢視時,原始陣列會受到影響。
- 當您修改子陣列的複製品時,原始陣列不會受到影響。
圖表翻譯:
這個流程圖展示了陣列檢視和複製的差異。當您抽取子陣列檢視並修改它時,原始陣列會受到影響。然而,當您複製子陣列並修改複製品時,原始陣列保持不變。
陣列重塑
陣列重塑是一種非常有用的操作,可以使用 reshape 方法實作。例如,如果您想將 1 至 9 的數字放入 3 × 3 的網格中,可以執行以下操作:
import numpy as np
grid = np.arange(1, 10).reshape(3, 3)
print(grid)
輸出結果為:
[[1 2 3]
[4 5 6]
[7 8 9]]
注意,陣列重塑的前提是原始陣列的大小必須與重塑後的陣列大小相匹配。在大多數情況下,reshape 方法會傳回原始陣列的無複製檢視。
常見的重塑操作是將一維陣列轉換為二維的行或列矩陣:
x = np.array([1, 2, 3])
print(x.reshape((1, 3))) # 行向量
print(x.reshape((3, 1))) # 列向量
輸出結果為:
[[1 2 3]]
[[1]
[2]
[3]]
一個方便的簡寫方法是使用 np.newaxis 在切片語法中:
print(x[np.newaxis, :]) # 行向量
print(x[:, np.newaxis]) # 列向量
輸出結果為:
[[1 2 3]]
[[1]
[2]
[3]]
這是一種我們將在整本文中經常使用的模式。
內容解密:
在上述程式碼中,我們首先匯入了 NumPy 函式庫,然後使用 np.arange(1, 10) 建立了一個包含 1 至 9 的數字的陣列。接著,我們使用 reshape(3, 3) 方法將其重塑為 3 × 3 的網格。最後,我們列印預出重塑後的陣列。
在第二部分,我們建立了一個一維陣列 x,然後使用 reshape 方法將其轉換為二維的行或列矩陣。我們還展示瞭如何使用 np.newaxis 在切片語法中實作相同的效果。
圖表翻譯:
這個流程圖展示了原始陣列如何透過 reshape 方法重塑為新的陣列,然後列印預出輸出結果,並對結果進行解釋。
陣列串接與分割
NumPy 提供了多種工具,讓您可以將多個陣列合併成一個,也可以將一個陣列分割成多個。以下將介紹陣列串接和分割的方法。
陣列串接
串接是指將兩個或多個陣列合併成一個新的陣列。您可以使用 np.concatenate() 函式來實作串接。以下是基本的串接範例:
import numpy as np
x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
# 串接 x 和 y
result = np.concatenate((x, y))
print(result) # Output: [1 2 3 3 2 1]
您也可以一次串接多個陣列:
z = np.array([99, 99, 99])
result = np.concatenate((x, y, z))
print(result) # Output: [1 2 3 3 2 1 99 99 99]
對於二維陣列,串接可以沿著指定軸進行:
grid = np.array([[1, 2, 3], [4, 5, 6]])
# 沿著第一軸(垂直)串接
result = np.concatenate((grid, grid), axis=0)
print(result)
# Output:
# [[1 2 3]
# [4 5 6]
# [1 2 3]
# [4 5 6]]
# 沿著第二軸(水平)串接
result = np.concatenate((grid, grid), axis=1)
print(result)
# Output:
# [[1 2 3 1 2 3]
# [4 5 6 4 5 6]]
垂直和水平堆積疊
對於不同維度的陣列,使用 np.vstack() 和 np.hstack() 函式可以更清晰地進行串接:
# 垂直堆積疊
result = np.vstack((x, grid))
print(result)
# Output:
# [[1 2 3]
# [1 2 3]
# [4 5 6]]
# 水平堆積疊
y = np.array([[99], [99]])
result = np.hstack((grid, y))
print(result)
# Output:
# [[1 2 3 99]
# [4 5 6 99]]
圖表解釋
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title NumPy陣列操作索引切片與重塑
package "資料視覺化流程" {
package "資料準備" {
component [資料載入] as load
component [資料清洗] as clean
component [資料轉換] as transform
}
package "圖表類型" {
component [折線圖 Line] as line
component [長條圖 Bar] as bar
component [散佈圖 Scatter] as scatter
component [熱力圖 Heatmap] as heatmap
}
package "美化輸出" {
component [樣式設定] as style
component [標籤註解] as label
component [匯出儲存] as export
}
}
load --> clean --> transform
transform --> line
transform --> bar
transform --> scatter
transform --> heatmap
line --> style --> export
bar --> label --> export
note right of scatter
探索變數關係
發現異常值
end note
@enduml
圖表翻譯:
上述流程圖描述瞭如何將兩個陣列 x 和 y 進行串接,得到一個新的結果陣列,並最終輸出結果。這個過程使用 np.concatenate() 函式來實作。
內容解密:
在上面的範例中,我們首先匯入 NumPy 函式庫,然後定義了兩個一維陣列 x 和 y。接著,我們使用 np.concatenate() 函式將這兩個陣列串接起來,得到一個新的結果陣列。對於二維陣列,我們可以指定沿著哪個軸進行串接。另外,np.vstack() 和 np.hstack() 函式提供了垂直和水平堆積疊的功能,方便了不同維度陣列的串接。
從底層實作到高階應用的全面檢視顯示,NumPy 陣列為 Python 的數值運算提供了堅實的基礎。透過多維度效能指標的實測分析,NumPy 陣列在記憶體使用效率和運算速度方面展現顯著優勢,尤其在處理大型資料集時更為突出。然而,陣列檢視的特性也可能導致非預期的資料修改,需要開發者妥善運用 .copy() 方法來管理資料完整性。技術堆疊的各層級協同運作中體現,NumPy 的索引與切片機制極大簡化了資料存取和操作的複雜度,其向量化運算能力更進一步提升了程式碼執行效率。對於追求效能的 Python 開發者而言,深入理解 NumPy 陣列的特性和操作方法至關重要。玄貓認為,熟練掌握 NumPy 陣列的各種技巧,例如重塑、串接和分割,能有效提升資料處理效率,是 Python 資料科學領域不可或缺的根本。未來,隨著資料規模的不斷增長,預計 NumPy 將持續最佳化效能並整合更多進階功能,以滿足日益複雜的資料處理需求。