返回文章列表

NumPy陣列操作索引切片與重塑

本文深入探討 NumPy 陣列的核心操作,包含索引、切片、重塑、串接與分割,搭配程式碼範例與圖表,完整展示陣列操作技巧,有效提升資料處理效率。

資料科學 Python

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 是切片的步長。如果未指定 startstopstep,則它們將預設為 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 陣列的索引和切片功能。索引允許您存取特定元素,而切片允許您存取陣列的一部分。透過指定 startstopstep,您可以控制切片的範圍和步長。最終,切片傳回的是原始陣列的一部分。

多維陣列切片與索引

在進行資料分析時,瞭解如何有效地存取和操作多維陣列中的元素至關重要。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

圖表翻譯:

上述流程圖描述瞭如何將兩個陣列 xy 進行串接,得到一個新的結果陣列,並最終輸出結果。這個過程使用 np.concatenate() 函式來實作。

內容解密:

在上面的範例中,我們首先匯入 NumPy 函式庫,然後定義了兩個一維陣列 xy。接著,我們使用 np.concatenate() 函式將這兩個陣列串接起來,得到一個新的結果陣列。對於二維陣列,我們可以指定沿著哪個軸進行串接。另外,np.vstack()np.hstack() 函式提供了垂直和水平堆積疊的功能,方便了不同維度陣列的串接。

從底層實作到高階應用的全面檢視顯示,NumPy 陣列為 Python 的數值運算提供了堅實的基礎。透過多維度效能指標的實測分析,NumPy 陣列在記憶體使用效率和運算速度方面展現顯著優勢,尤其在處理大型資料集時更為突出。然而,陣列檢視的特性也可能導致非預期的資料修改,需要開發者妥善運用 .copy() 方法來管理資料完整性。技術堆疊的各層級協同運作中體現,NumPy 的索引與切片機制極大簡化了資料存取和操作的複雜度,其向量化運算能力更進一步提升了程式碼執行效率。對於追求效能的 Python 開發者而言,深入理解 NumPy 陣列的特性和操作方法至關重要。玄貓認為,熟練掌握 NumPy 陣列的各種技巧,例如重塑、串接和分割,能有效提升資料處理效率,是 Python 資料科學領域不可或缺的根本。未來,隨著資料規模的不斷增長,預計 NumPy 將持續最佳化效能並整合更多進階功能,以滿足日益複雜的資料處理需求。