python-資料分析-Numpy-3、陣列的運算

little小新發表於2024-06-09

陣列的運算

使用 NumPy 最為方便的是當需要對陣列元素進行運算時,不用編寫迴圈程式碼遍歷每個元素,所有的運算都會自動的向量化。簡單的說就是,NumPy 中的數學運算和數學函式會自動作用於陣列中的每個成員。

# -*- coding: utf-8 -*-
#陣列的運算
#使用 NumPy 最為方便的是當需要對陣列元素進行運算時,不用編寫迴圈程式碼遍歷每個元素,所有的運算都會自動的向量化。簡單的說就是,NumPy 中的數學運算和數學函式會自動作用於陣列中的每個成員。

import numpy


# 1、陣列跟標量的運算
#NumPy 的陣列可以跟一個數值進行加、減、乘、除、求模、求冪等運算,對應的運算會作用到陣列的每一個元素上
array1 = numpy.arange(1, 10)
print(array1)   #[1 2 3 4 5 6 7 8 9]

print(array1 + 10)  #[11 12 13 14 15 16 17 18 19]
print(array1 * 10)  #[10 20 30 40 50 60 70 80 90]

#除了上述的運算,關係運算也是沒有問題的,之前講布林索引的時候已經遇到過了
print(array1 > 5)   #[False False False False False  True  True  True  True]
print(array1 % 2 == 0)  #[False  True False  True False  True False  True False]

print('-----------------------------------------------------')

# 2、陣列跟陣列的運算
#NumPy 的陣列跟陣列也可以執行算術運算和關係運算,運算會作用於兩個陣列對應的元素上,這就要求兩個陣列的形狀(shape屬性)要相同
array2 = numpy.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
print(array1 + array2)  #[ 2  3  4  6  7  8 10 11 12]
print(array1 * array2)  #[ 1  2  3  8 10 12 21 24 27]
print(array1 ** array2) #[  1   2   3  16  25  36 343 512 729]

print(array1 > array2)  #[False  True  True  True  True  True  True  True  True]
print(array1 % array2 == 0) #[ True  True  True  True False  True False False  True]

print('-----------------------------------------------------')

# 3、通用一元函式
#NumPy 中通用一元函式的引數是一個陣列物件,函式會對陣列進行元素級的處理
# 例如:sqrt函式會對陣列中的每個元素計算平方根,而log2函式會對陣列中的每個元素計算以2為底的對數
print(numpy.sqrt(array1))
print(numpy.log2(array1))
'''
[1.         1.41421356 1.73205081 2.         2.23606798 2.44948974
 2.64575131 2.82842712 3.        ]
 
[0.         1.         1.5849625  2.         2.32192809 2.5849625
 2.80735492 3.         3.169925  ]
'''

通用一元函式表:

函式 說明
abs / fabs 求絕對值的函式
sqrt 求平方根的函式,相當於array ** 0.5
square 求平方的函式,相當於array ** 2
exp 計算$e^x$的函式
log / log10 / log2 對數函式(e為底 / 10為底 / 2為底)
sign 符號函式(1 - 正數;0 - 零;-1 - 負數)
ceil / floor 上取整 / 下取整
isnan 返回布林陣列,NaN對應True,非NaN對應False
isfinite / isinf 判斷數值是否為無窮大的函式
cos / cosh / sin 三角函式
sinh / tan / tanh 三角函式
arccos / arccosh / arcsin 反三角函式
arcsinh / arctan / arctanh 反三角函式
rint / round 四捨五入函式

# 4、通用二元函式
#NumPy 中通用二元函式的引數是兩個陣列物件,函式會對兩個陣列中的對應元素進行運算
# 例如:maximum函式會對兩個陣列中對應的元素找最大值,而power函式會對兩個陣列中對應的元素進行求冪操作
array3 = numpy.array([[4, 5, 6], [7, 8, 9]])
array4 = numpy.array([[1, 2, 3], [3, 2, 1]])
print(numpy.maximum(array3, array4))
print(numpy.power(array3, array4))
'''
[[4 5 6]
 [7 8 9]]
 
[[  4  25 216]
 [343  64   9]]
'''

通用二元函式:表

函式 說明
add(x, y) / substract(x, y) 加法函式 / 減法函式
multiply(x, y) / divide(x, y) 乘法函式 / 除法函式
floor_divide(x, y) / mod(x, y) 整除函式 / 求模函式
allclose(x, y) 檢查陣列xy元素是否幾乎相等
power(x, y) 陣列$x$的元素$x_i$和陣列$y$的元素$y_i$,計算$x_i^{y_i}$
maximum(x, y) / fmax(x, y) 兩兩比較元素獲取最大值 / 獲取最大值(忽略NaN)
minimum(x, y) / fmin(x, y) 兩兩比較元素獲取最小值 / 獲取最小值(忽略NaN)
dot(x, y) 點積運算(數量積,通常記為$\cdot$,用於歐幾里得空間(Euclidean space))
inner(x, y) 內積運算(內積的含義要高於點積,點積相當於是內積在歐幾里得空間$\mathbb{R}^n$的特例,而內積可以推廣到賦範向量空間,只要它滿足平行四邊形法則即可)
cross(x, y) 叉積運算(向量積,通常記為$\times$,運算結果是一個向量)
outer(x, y) 外積運算(張量積,通常記為$\bigotimes$,運算結果通常是一個矩陣)
intersect1d(x, y) 計算xy的交集,返回這些元素構成的有序陣列
union1d(x, y) 計算xy的並集,返回這些元素構成的有序陣列
in1d(x, y) 返回由判斷x 的元素是否在y中得到的布林值構成的陣列
setdiff1d(x, y) 計算xy的差集,返回這些元素構成的陣列
setxor1d(x, y) 計算xy的對稱差,返回這些元素構成的陣列

廣播機制

上面陣列運算的例子中,兩個陣列的形狀(shape屬性)是完全相同的,我們再來研究一下,兩個形狀不同的陣列是否可以直接做二元運算或使用通用二元函式進行運算

#廣播機制
#廣播機制是 NumPy 中最令人困惑的特性,它允許 NumPy 的陣列運算在陣列形狀不同的情況下也能正常進行。

array5 = numpy.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
array6 = numpy.array([1, 2, 3])
print(array5 + array6)
'''
[[1 2 3]
 [2 3 4]
 [3 4 5]
 [4 5 6]]
'''

array7 = numpy.array([[1], [2], [3], [4]])
print(array5 + array7)
'''
[[1 1 1]
 [3 3 3]
 [5 5 5]
 [7 7 7]]
'''

透過上面的例子,我們發現形狀不同的陣列仍然有機會進行二元運算,但這不代表任意形狀的陣列都可以進行二元運算。

簡單的說,只有兩個陣列後緣維度相同或者後緣維度不同但其中一個陣列後緣維度為1時,廣播機制才會被觸發。

透過廣播機制,NumPy 將兩個原本形狀不相同的陣列變成形狀相同,才能進行二元運算。所謂後緣維度,指的是陣列形狀(shape屬性)從後往前看對應的部分,我們舉例說明。

下圖中,一個陣列的形狀是(4, 3),另一個陣列的形狀是(3, ),從後往前看對應的部分都是3,屬於後緣維度相同,可以應用廣播機制,第二個陣列會沿著缺失元素那個軸的方向去廣播自己,最終讓兩個陣列形狀達成一致。

python-資料分析-Numpy-3、陣列的運算

下圖中,一個陣列的形狀是(3, 4, 2),另一個陣列的形狀是(4, 2),從後往前看對應的部分都是(4, 2),屬於後緣維度相同,可以應用廣播機制,第二個陣列會沿著缺失元素那個軸的方向去廣播自己,最終讓兩個陣列形狀達成一致。

python-資料分析-Numpy-3、陣列的運算

下圖中,一個陣列的形狀是(4, 3),另一個陣列的形狀是(4, 1),這是後緣維度不相同的情況,但是第二個陣列跟第一個陣列不同的地方為1,第二個陣列可以沿著為1 的那個軸廣播自己,最終讓兩個陣列形狀達成一致

python-資料分析-Numpy-3、陣列的運算

其它常用函式

除了上面講到的函式外,NumPy 中還提供了很多用於處理陣列的函式,ndarray物件的很多方法也可以透過呼叫函式來實現,下表給出了一些常用的函式。
表:NumPy其他常用函式

函式 說明
unique 去除陣列重複元素,返回唯一元素構成的有序陣列
copy 返回複製陣列得到的陣列
sort 返回陣列元素排序後的複製
split / hsplit / vsplit 將陣列拆成若干個子陣列
stack / hstack / vstack 將多個陣列堆疊成新陣列
concatenate 沿著指定的軸連線多個陣列構成新陣列
append / insert 向陣列末尾追加元素 / 在陣列指定位置插入元素
argwhere 找出陣列中非0元素的位置
extract / select / where 按照指定的條件從陣列中抽取或處理陣列元素
flip 沿指定的軸翻轉陣列中的元素
fromregex 透過讀取檔案和正規表示式解析獲取資料建立陣列物件
repeat / tile 透過對元素的重複來建立新陣列
roll 沿指定軸對陣列元素進行移位
resize 重新調整陣列的大小
place / put 將陣列中滿足條件的元素/指定的元素替換為指定的值
partition 用選定的元素對陣列進行一次劃分並返回劃分後的陣列
#常用函式
#去重(重複元素值保留一項)
print(numpy.unique(array5)) #[0 1 2 3]

#堆疊和拼接
array8 = numpy.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
array9 = numpy.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])
print(numpy.hstack((array8, array9)))   #用於水平(按列)堆疊陣列,相當於沿著軸 1 進行陣列的拼接。
'''
[[1 1 1 4 4 4]
 [2 2 2 5 5 5]
 [3 3 3 6 6 6]]
'''

print(numpy.vstack((array8, array9)))   #用於垂直(按行)堆疊陣列,相當於沿著軸 0 進行陣列的拼接。
'''
[[1 1 1]
 [2 2 2]
 [3 3 3]
 [4 4 4]
 [5 5 5]
 [6 6 6]]
'''
#axis:指定連線的軸。預設值為 0=行,即在第一個軸上進行連線。 1=列
print(numpy.concatenate((array8, array9), axis=1))  #用於沿指定軸連線一系列陣列。它可以在任意維度上進行拼接。
'''
[[1 1 1 4 4 4]
 [2 2 2 5 5 5]
 [3 3 3 6 6 6]]
'''
#numpy.concatenate() 是一個通用的拼接函式,可以指定沿哪個軸拼接陣列。
#numpy.vstack() 是專門用於垂直堆疊的函式,相當於沿軸 0 拼接。
#numpy.hstack() 是專門用於水平堆疊的函式,相當於沿軸 1 拼接。

print('-----------------------------------------------------')

#追加和插入元素。
print(numpy.append(array1, [10, 100]))  #追加元素 追加 10 和 100   [  1   2   3   4   5   6   7   8   9  10 100]

print(numpy.insert(array1, 1, [98, 99, 100]))   #插入元素 插入 98 99 100 到 1 位置  [  1  98  99 100   2   3   4   5   6   7   8   9]

print('-----------------------------------------------------')

#抽取和處理元素。
#說明:extract函式的操作相當於我們之前講的布林索引。
print(numpy.extract(array1 % 2 !=0, array1))    #抽取元素 抽取出 array1 中所有元素值不為偶數的元素  [1 3 5 7 9]

#處理元素 處理 array1 中所有元素值小於等於 3 的元素,將其乘以 10;處理 array1 中所有元素值大於等於 7 的元素,將其平方。
#select函式的第一個引數設定了兩個條件,滿足第一個條件的元素執行了乘以10的操作,滿足第二個條件的元素執行了求平方的操作,兩個條件都不能滿足的陣列元素會被處理為0。
print(numpy.select([array1 <= 3, array1 >= 7], [array1 * 10, array1 ** 2]))     #[10 20 30  0  0  0 49 64 81]

#where函式的第一個引數給出了條件,滿足條件的元素執行了乘以10的操作,不能滿足條件的元素執行了求平方的操作。
print(numpy.where(array1 <= 5, array1 * 10, array1 ** 2))   #[10 20 30 40 50 36 49 64 81]

print('-------------------------------------------------------------')
#重複陣列元素建立新陣列
#repeat函式的第一個引數是陣列,第二個引數是重複次數。
print(numpy.repeat(array1, 3))  #[1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9]

#tile函式的第一個引數是陣列,第二個引數是重複次數。
print(numpy.tile(array1, 2))    #[1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9]

#repeat和tile函式的區別在於,repeat對陣列中的每個元素進行重複。,tile函式對整個陣列進行重複。

print('-------------------------------------------------------------')

#調整陣列的大小
#resize函式的第一個引數是陣列,第二個引數是調整後的大小。
#提示:array1原本是一個有9個元素的一維陣列,透過resize函式調整成為5行3列共15個元素的二維陣列,缺少的元素透過複用原陣列中的元素來補充。
print(numpy.resize(array1, (5, 3))) #(5, 3) 表示5行3列
'''
[[1 2 3]
 [4 5 6]
 [7 8 9]
 [1 2 3]
 [4 5 6]]
'''

print('-------------------------------------------------------------')

#替換陣列元素
#replace函式的第一個引數是陣列,第二個引數是待替換的元素的索引位置,第三個引數是替換的新元素。
print(array1)
#put函的第二個引數給出了要被替換的元素的索引,但是用來作為替換值的元素只有100和200,所以這兩個值會被迴圈使用
# 因此索引為0、1、-1、3、5的元素被依次替換成了100、200、100、200、100
numpy.put(array1, [0, 1, -1, 3, 5], [100, 200])
print(array1)   #[100 200   3 200   5 100   7   8 100]

#place函式的第一個引數是陣列,第二個引數是條件,第三個引數是替換的新元素。
numpy.place(array1, array1 > 5, [1, 2, 3])
print(array1)   #[1 2 3 3 5 1 2 3 1]

#注意:put函式和place函式都沒有返回新的陣列物件,而是在原來的陣列上直接進行替換。

相關文章