NumPy 是 Python 資料科學領域的核心函式庫,其提供的陣列操作功能對於資料處理和分析至關重要。理解 NumPy 陣列操作的技巧,能大幅提升程式碼效率和簡潔性。本文將深入探討 NumPy 陣列操作的各種技巧,包含比較運算、布林遮罩、高階索引以及 ufunc 的應用。這些技巧涵蓋瞭如何使用布林遮罩進行資料篩選,如何結合多個條件進行篩選,以及如何使用隨機索引選擇陣列中的點等實務操作。藉由程式碼範例和圖表說明,讀者能更清晰地理解這些技巧,並將其應用於實際的資料分析任務中,提升程式碼效率和可讀性。

基本比較運算

以下是一些基本比較運算的範例:

import numpy as np

x = np.array([1, 2, 3, 4, 5])

print(x < 3)  # 輸出:array([ True,  True, False, False, False])
print(x > 3)  # 輸出:array([False, False, False,  True,  True])
print(x <= 3)  # 輸出:array([ True,  True,  True, False, False])
print(x >= 3)  # 輸出:array([False, False,  True,  True,  True])
print(x!= 3)  # 輸出:array([ True,  True, False,  True,  True])
print(x == 3)  # 輸出:array([False, False,  True, False, False])

元素級別比較兩個陣列

你也可以對兩個陣列進行元素級別的比較:

x = np.array([1, 2, 3, 4, 5])
y = np.array([1, 4, 9, 16, 25])

print(x == y)  # 輸出:array([ True, False, False, False, False])

複合表示式

比較運算子也可以用於複合表示式中:

x = np.array([1, 2, 3, 4, 5])

print((2 * x) == (x ** 2))  # 輸出:array([False,  True, False, False, False])

等價的 ufuncs

NumPy 中的比較運算子都有對應的 ufuncs,可以用於實作相同的功能。以下是比較運算子和其等價 ufuncs 的對應關係:

運算子等價 ufunc
==np.equal
!=np.not_equal
<np.less
<=np.less_equal
>np.greater
>=np.greater_equal

例如,當你使用 x < 3 時,NumPy 內部實際上是呼叫了 np.less(x, 3)。這些 ufuncs 可以用於實作更複雜的比較邏輯。

使用NumPy進行布林陣列運算

NumPy提供了多種方法來進行布林陣列運算。以下是使用NumPy進行布林陣列運算的範例。

建立隨機陣列

首先,我們建立一個隨機陣列,使用np.random.default_rng函式生成一個隨機數字產生器,然後使用integers方法生成一個3x4的隨機整數陣列。

import numpy as np

rng = np.random.default_rng(seed=1701)
x = rng.integers(10, size=(3, 4))
print(x)

布林陣列運算

接下來,我們進行布林陣列運算,使用 < 運算元來比較陣列中的值是否小於6。

bool_array = x < 6
print(bool_array)

計算True值的數量

要計算布林陣列中True值的數量,可以使用np.sum函式。因為False被視為0,True被視為1,所以可以直接對布林陣列進行求和。

count = np.sum(x < 6)
print(count)

沿著軸進行求和

如果要沿著特定軸(例如行或列)進行求和,可以使用axis引數指定軸的方向。

row_sum = np.sum(x < 6, axis=1)
print(row_sum)

內容解密:

  • np.random.default_rng(seed=1701):建立一個隨機數字產生器,使用指定的seed值。
  • x = rng.integers(10, size=(3, 4)):生成一個3x4的隨機整數陣列。
  • bool_array = x < 6:進行布林陣列運算,比較陣列中的值是否小於6。
  • count = np.sum(x < 6):計算布林陣列中True值的數量。
  • row_sum = np.sum(x < 6, axis=1):沿著行軸(axis=1)進行求和。

圖表翻譯:

圖表翻譯:

此圖表展示了使用NumPy進行布林陣列運算的步驟。首先,建立一個隨機數字產生器,然後生成一個隨機整數陣列。接下來,進行布林陣列運算,比較陣列中的值是否小於6。然後,計算布林陣列中True值的數量。最後,沿著特定軸(例如行或列)進行求和。

使用 NumPy 進行布林運算

在前面的章節中,我們已經學習瞭如何使用 NumPy 進行基本的數學運算和統計分析。現在,我們將探討如何使用 NumPy 進行布林運算。

布林運算簡介

布林運算是指對布林值(True 或 False)進行的運算。NumPy 提供了多種布林運算函式,包括 np.anynp.all&(位元 AND)、|(位元 OR)、^(位元 XOR)和 ~(位元 NOT)。

使用 np.anynp.all

np.anynp.all 分別用於檢查陣列中是否存在至少一個 True 值或所有值都為 True。

import numpy as np

x = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# 檢查是否存在任何值大於 8
print(np.any(x > 8))  # Output: True

# 檢查是否所有值都小於 10
print(np.all(x < 10))  # Output: True

使用位元運算元

位元運算元 &|^~ 可以用於進行布林運算。

import numpy as np

rainfall_mm = np.array([10, 15, 20, 25, 30])

# 檢查雨量是否大於 10 mm 且小於 20 mm
print(np.sum((rainfall_mm > 10) & (rainfall_mm < 20)))  # Output: 2

在上面的例子中,我們使用 & 運算元來檢查雨量是否大於 10 mm 且小於 20 mm。結果為 2,表示有 2 天的雨量符合此條件。

結合條件

我們可以使用位元運算元來結合多個條件。

import numpy as np

rainfall_mm = np.array([10, 15, 20, 25, 30])

# 檢查雨量是否大於 10 mm 或小於 20 mm
print(np.sum((rainfall_mm > 10) | (rainfall_mm < 20)))  # Output: 4

在上面的例子中,我們使用 | 運算元來檢查雨量是否大於 10 mm 或小於 20 mm。結果為 4,表示有 4 天的雨量符合此條件。

使用布林陣列進行資料篩選

在前面的章節中,我們已經學習瞭如何使用比較運算元和布林運算元來產生布林陣列。現在,我們將探討如何使用這些布林陣列作為遮罩(masks),以篩選出特定的資料子集。

篩選陣列元素

假設我們有一個二維陣列 x,如下所示:

import numpy as np

x = np.array([[9, 4, 0, 3],
             [8, 6, 3, 1],
             [3, 7, 4, 0]])

如果我們想要篩選出所有小於 5 的元素,可以使用比較運算元產生一個布林陣列:

mask = x < 5
print(mask)

輸出結果:

array([[False,  True,  True,  True],
       [False,  True,  True,  True],
       [ True, False,  True,  True]])

這個布林陣列 mask 現在可以用來篩選出原始陣列 x 中的小於 5 的元素:

filtered_x = x[mask]
print(filtered_x)

輸出結果:

array([4, 0, 3, 4, 3, 1, 3, 4, 0])

如您所見,filtered_x 陣列包含了原始陣列 x 中所有小於 5 的元素。

篩選資料子集

除了篩選單個元素外,我們還可以使用布林陣列來篩選出資料子集。例如,如果我們想要篩選出所有大於 10 的元素,可以使用以下程式碼:

mask = x > 10
filtered_x = x[mask]
print(filtered_x)

輸出結果:

array([9])

在這個例子中,filtered_x 陣列包含了原始陣列 x 中唯一一個大於 10 的元素,即 9。

結合比較運算元和布林運算元

我們也可以結合比較運算元和布林運算元來篩選出更複雜的資料子集。例如,如果我們想要篩選出所有大於 5 且小於 10 的元素,可以使用以下程式碼:

mask = (x > 5) & (x < 10)
filtered_x = x[mask]
print(filtered_x)

輸出結果:

array([6, 7, 8])

在這個例子中,filtered_x 陣列包含了原始陣列 x 中所有大於 5 且小於 10 的元素,即 6、7 和 8。

使用布林遮罩進行資料篩選

在進行資料分析時,經常需要根據特定條件篩選出部分資料。NumPy提供了一種稱為布林遮罩(Boolean Masking)的方法,可以方便地實作這一功能。

布林遮罩的基本概念

布林遮罩是一種使用布林值(True或False)來表示每個資料點是否符合某個條件的方法。透過這種方法,可以輕易地篩選出符合條件的資料點。

篩選資料的範例

假設我們有一個陣列x,其中包含一些整數值。我們可以使用布林遮罩來篩選出小於5的值:

import numpy as np

x = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
mask = x < 5
print(mask)

輸出結果:

array([[ True,  True,  True,  True],
       [False, False, False, False],
       [False, False, False, False]])

這裡,mask是一個布林陣列,其中每個元素表示對應的x陣列元素是否小於5。

使用布林遮罩進行篩選

現在,我們可以使用這個布林遮罩來篩選出小於5的值:

print(x[mask])

輸出結果:

array([1, 2, 3, 4])

這裡,x[mask]傳回了一個新的陣列,其中包含所有小於5的值。

應用於實際資料分析

在實際資料分析中,布林遮罩可以用於篩選出符合特定條件的資料點。例如,假設我們有一個陣列rainfall_mm,其中包含每天的降雨量,我們可以使用布林遮罩來篩選出下雨天:

rainy = (rainfall_mm > 0)
print(rainfall_mm[rainy])

這裡,rainy是一個布林陣列,其中每個元素表示對應的rainfall_mm陣列元素是否大於0。

結合多個條件

我們也可以結合多個條件來篩選資料點。例如,假設我們想要篩選出夏季下雨天:

summer = (days > 172) & (days < 262)
rainy_summer = rainy & summer
print(rainfall_mm[rainy_summer])

這裡,summer是一個布林陣列,其中每個元素表示對應的days陣列元素是否在夏季。然後,我們可以使用&運算子結合rainysummer兩個條件。

統計分析

最後,我們可以對篩選出的資料點進行統計分析。例如,假設我們想要計算夏季下雨天的中位數降雨量:

print(np.median(rainfall_mm[rainy_summer]))

這裡,np.median函式傳回了夏季下雨天的中位數降雨量。

圖表翻譯:

這裡,圖表展示了從原始資料到結果的整個過程。首先,我們建立一個布林遮罩來篩選出符合條件的資料點。然後,我們使用這個布林遮罩來篩選出資料點。最後,我們對篩選出的資料點進行統計分析,以得到結果。

運算元與邏輯運算的區別

在 Python 中,andor 這兩個邏輯運算元與 &| 這兩個位元運算元之間的區別,往往會讓人感到困惑。當你使用 andor 時,它們會將整個物件視為一個布林值(Boolean),而當你使用 &| 時,它們則會對物件中的每個元素進行位元運算。

邏輯運算元:and 和 or

當你使用 andor 這兩個邏輯運算元時,Python 會將整個物件視為一個布林值。所有非零的整數在 Python 中都會被視為 True,而零則被視為 False。讓我們來看一些例子:

print(bool(42))  # True
print(bool(0))   # False
print(bool(42 and 0))  # False,因為 0 被視為 False
print(bool(42 or 0))   # True,因為 42 被視為 True

位元運算元:& 和 |

另一方面,當你使用 &| 這兩個位元運算元時,Python 會對整數的位元表示進行運算。這意味著它會對組成整數的每個位元進行 andor 運算。

print(bin(42))  # '0b101010'
print(bin(59))  # '0b111011'
print(bin(42 & 59))  # '0b101010',對每個位元進行 and 運算
print(bin(42 | 59))  # '0b111011',對每個位元進行 or 運算

內容解密:

上述程式碼展示瞭如何使用 andor&| 進行不同型別的運算。在第一部分,我們看到 andor 如何將整數視為布林值進行運算。在第二部分,我們看到 &| 如何對整數的位元表示進行運算。這兩種方法在實際應用中非常重要,因為它們可以幫助我們實作不同的邏輯和位元操作。

圖表翻譯:

上述流程圖展示了整數如何被轉換為布林值或位元表示,並如何進行不同型別的運算。這有助於我們理解 andor&| 之間的區別,以及如何在實際應用中使用它們。

位元運算在 NumPy 陣列中的應用

在處理 NumPy 陣列時,瞭解位元運算的工作原理非常重要。當您有一個布林值(Boolean)陣列時,可以將其視為一串位元,其中 1 代表 True,0 代表 False。使用 &| 運算子,可以執行逐位元的邏輯運算。

逐位元邏輯運算

例如,給定兩個布林值陣列 AB,您可以使用 &| 運算子執行逐位元的邏輯與和邏輯或運算:

import numpy as np

A = np.array([1, 0, 1, 0, 1, 0], dtype=bool)
B = np.array([1, 1, 1, 0, 1, 1], dtype=bool)

print(A | B)  # 逐位元邏輯或
print(A & B)  # 逐位元邏輯與

使用 any()all() 方法

在評估布林值陣列時,您可以使用 any()all() 方法來檢查陣列中是否有至少一個元素為 True 或所有元素是否為 True

x = np.arange(10)
print((x > 4).any())  # 檢查是否有至少一個元素大於 4
print((x > 4).all())  # 檢查所有元素是否大於 4

避免使用 andor 運算子

在評估布林值陣列時,應避免使用 andor 運算子,因為它們會嘗試評估整個陣列的真值,這可能不是您想要的結果:

x = np.arange(10)
try:
    print((x > 4) and (x < 8))
except ValueError as e:
    print(e)

相反,您應該使用 &| 運算子來執行逐位元的邏輯運算:

x = np.arange(10)
print((x > 4) & (x < 8))

探索高階索引

高階索引是一種概念上簡單的技術:它涉及傳遞索引陣列以一次存取多個陣列元素。例如,考慮以下陣列:

import numpy as np

rng = np.random.default_rng(seed=1701)
x = rng.integers(100, size=10)
print(x)

假設我們想存取三個不同的元素。可以按以下方式進行:

print([x[3], x[7], x[2]])

或者,我們可以傳遞一個單一的索引列表或陣列來獲得相同的結果:

ind = [3, 7, 4]
print(x[ind])

當使用索引陣列時,結果的形狀反映了索引陣列的形狀,而不是被索引陣列的形狀:

ind = np.array([[3, 7], [4, 5]])
print(x[ind])

高階索引也可以在多維陣列中使用。考慮以下陣列:

內容解密:

在上述程式碼中,我們首先匯入了 NumPy 函式庫並建立了一個隨機數生成器。然後,我們生成了一個 10 個元素的隨機整數陣列 x。接下來,我們展示瞭如何使用索引列表或陣列來存取 x 中的多個元素。最後,我們演示了高階索引在多維陣列中的應用。

圖表翻譯:

此圖示為高階索引的工作原理。當我們傳遞一個索引陣列給 x 時,NumPy 會根據索引陣列的形狀傳回一個新的陣列,其中包含了 x 中對應位置的元素。

圖表翻譯:

此圖示展示了高階索引在多維陣列中的應用。當我們傳遞一個二維索引陣列給 x 時,NumPy 會根據索引陣列的形狀傳回一個新的二維陣列,其中包含了 x 中對應位置的元素。

結合索引的強大操作

在進行陣列操作時,瞭解不同索引方法的結合是非常重要的。讓我們先從一個基本的陣列開始:

import numpy as np

X = np.arange(12).reshape((3, 4))
print(X)

輸出結果:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

精細索引(Fancy Indexing)

精細索引允許你使用陣列作為索引。例如,你可以使用兩個陣列,一個代表行索引,另一個代表列索引:

row = np.array([0, 1, 2])
col = np.array([2, 1, 3])

result = X[row, col]
print(result)

輸出結果:

[ 2  5 11]

這裡,第一個值是 X[0, 2],第二個值是 X[1, 1],第三個值是 X[2, 3]

廣播規則(Broadcasting)

當你結合不同形狀的陣列作為索引時,會按照廣播規則來匹配索引。例如,如果你將一個列向量和一個行向量結合起來,你會得到一個二維結果:

result = X[row[:, np.newaxis], col]
print(result)

輸出結果:

[[ 2  1  3]
 [ 6  5  7]
 [10  9 11]]

這裡,每一個行值都會與每一個列值匹配,正如你在廣播算術運算中所看到的一樣。

結合索引

你可以將精細索引與其他索引方案結合起來,進行更加強大的操作。例如,給定陣列 X

print(X)

輸出結果:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

你可以結合精細索引和簡單索引:

內容解密:

上述程式碼示範瞭如何使用 NumPy 進行陣列操作,包括精細索引和廣播規則。透過使用不同的索引方法,你可以對陣列進行複雜的操作,並獲得想要的結果。

圖表翻譯:

下面的 Plantuml 圖表展示瞭如何使用精細索引和廣播規則來進行陣列操作:

@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

這個圖表展示了從定義陣列到進行精細索引和使用廣播規則,最後獲得結果的過程。

結合索引和掩碼進行陣列操作

在進行陣列操作時,我們可以結合不同的索引方法來實作複雜的操作。例如,我們可以結合「花式索引」(fancy indexing)和切片(slicing)來選擇陣列中的特定元素。

import numpy as np

# 建立一個2D陣列
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])

# 使用花式索引選擇特定元素
print(X[2, [2, 0, 1]])  # Output: [9 7 8]

# 結合花式索引和切片
print(X[1:, [2, 0, 1]])  # Output: [[ 6  4  5]
                         #          [10  8  9]]

# 建立一個掩碼
mask = np.array([True, False, True, False])

# 使用掩碼和花式索引選擇特定元素
print(X[np.arange(X.shape[0])[:, np.newaxis], mask])  # Output: [[ 1  3]
                                                     #          [ 4  6]
                                                     #          [ 7  9]
                                                     #          [10 12]]

隨機選擇陣列中的點

在資料分析中,常常需要隨機選擇陣列中的點。例如,我們可以使用 NumPy 的 random.choice 函式來選擇陣列中的隨機索引。

import numpy as np
import matplotlib.pyplot as plt

# 建立一個2D陣列,代表100個點在2D空間中的位置
mean = [0, 0]
cov = [[1, 2], [2, 5]]
X = np.random.multivariate_normal(mean, cov, 100)

# 使用隨機索引選擇20個點
indices = np.random.choice(X.shape[0], 20, replace=False)

# 繪製這20個點
plt.scatter(X[indices, 0], X[indices, 1])
plt.show()

這些索引和掩碼的操作可以幫助我們更有效地存取和修改陣列中的元素。同時,使用隨機索引可以幫助我們選擇陣列中的隨機點,對於資料分析和視覺化非常有用。

使用 advance indexing 進行資料選擇和修改

在 NumPy 中,advance indexing 是一種強大的工具,允許您使用陣列索引來選擇和修改資料。以下是 advance indexing 的基本用法:

資料選擇

假設我們有一個 2D 陣列 X,並且我們想要選擇其中某些點。首先,我們需要定義一個索引陣列 indices,其中包含了我們想要選擇的點的索引。

import numpy as np

# 定義 2D 陣列 X
X = np.random.rand(100, 2)

# 定義索引陣列 indices
indices = np.array([80, 87, 90])

# 使用 advance indexing 選擇資料
selection = X[indices]

print(selection.shape)  # (3, 2)

在這個例子中,selection 是一個 2D 陣列,包含了 X 中索引為 808790 的點。

資料修改

advance indexing 也可以用來修改資料。例如,我們可以使用索引陣列 i 來設定 x 陣列中對應的值。

# 定義 1D 陣列 x
x = np.arange(10)

# 定義索引陣列 i
i = np.array([2, 1, 8, 4])

# 使用 advance indexing 修改資料
x[i] = 99

print(x)  # [ 0 99 99 3 99 5 6 7 99 9]

在這個例子中,x 陣列中索引為 2184 的值被設定為 99

注意事項

使用 advance indexing 時,需要注意重複的索引可能會導致意外的結果。例如:

# 定義 1D 陣列 x
x = np.zeros(10)

# 定義索引陣列 i
i = np.array([0, 0])

# 使用 advance indexing 修改資料
x[i] = [4, 6]

print(x)  # [4 0 0 0 0 0 0 0 0 0]

在這個例子中,雖然索引 0 被重複設定為 46,但最終的結果只有 4 被設定到 x[0]

圖表展示

要展示選擇的點,可以使用 Matplotlib 的 scatter 函式。

import matplotlib.pyplot as plt

# 定義 2D 陣列 X
X = np.random.rand(100, 2)

# 定義索引陣列 indices
indices = np.array([80, 87, 90])

# 使用 advance indexing 選擇資料
selection = X[indices]

# 繪製圖表
plt.scatter(X[:, 0], X[:, 1], alpha=0.3)
plt.scatter(selection[:, 0], selection[:, 1],
            facecolor='none', edgecolor='black', s=200);

這個圖表展示了原始資料點和選擇的點。

使用NumPy進行陣列操作

在進行陣列操作時,NumPy提供了多種方法來實作不同的運算。其中,ufunc函式提供了一種高效的方式來進行元素級別的運算。

關於索引和指定

當使用索引和指定時,需要注意的是,指定操作是按順序進行的。例如,當我們執行x[i] += 1時,實際上是先計算x[i] + 1,然後再將結果指定給x[i]。這意味著,如果索引i出現多次,則指定操作也會多次進行。

使用at方法

如果我們想要實作重複運算的效果,可以使用ufuncat方法。例如,np.add.at(x, i, 1)會將值1新增到陣列x中索引為i的位置。

從底層實作到高階應用的全面檢視顯示,NumPy 的布林陣列及進階索引機制為資料處理提供了強大的工具。透過多維比較分析,我們可以看到 NumPy 不僅支援元素級別的比較運算、複合表示式,更允許結合布林遮罩進行高效能的資料篩選和統計分析,展現其在處理大量資料時的優勢。技術限制深析指出,在使用布林陣列時,andor&| 的區別容易造成混淆,需特別注意。同時,進階索引結合廣播規則雖然功能強大,但在處理重複索引時,其指定邏輯需要仔細理解,避免非預期結果。展望未來,NumPy 的向量化運算和廣播機制將持續在資料科學、機器學習等領域扮演關鍵角色。隨著硬體加速技術的發展,預見 NumPy 的效能將進一步提升,更有效地支援更大規模的資料處理需求。玄貓認為,深入理解 NumPy 的核心機制,並結合實際應用場景進行最佳化,將有效提升資料處理效率,並為更複雜的演算法設計奠定堅實基礎。對於追求高效能資料處理的開發者,NumPy 絕對是值得深入學習和應用的利器。