2.5 数组运算
NumPy数组不需要循环遍历即可对每个元素执行批量的算术运算操作,这个过程叫矢量化运算。如果两个数组大小ndarray.shape
不同,则进行算术运算时会出现广播机制。同时,数组还支持算术运算符和标量进行运算。
2.5.1 矢量化运算
在NumPy中任何大小相等的数组之间的算术运算都会应用到元素级,即只用于位置相同的元素之间,所得的运算结果组成一个新的数组。
如图所示,两数组对齐以后会让相同位置的元素操作得到结果,并且结果的位置和操作数的位置相同,例如:
import numpy as np
a = np.array([4, 5, 3, 1])
b = np.array([1, 3, 2, 8])
print("a+b=", a + b)
print("a-b=", a - b)
print("a*b=", a * b)
print("a%b=", a % b)
输出为:
a+b= [5 8 5 9]
a-b= [ 3 2 1 -7]
a*b= [ 4 15 6 8]
a%b= [0 2 1 1]
2.5.2 数组广播
数组进行矢量化运算时,要求数组的形状是相等的。当形状不相等的数组执行算术运算时会出现广播机制,该机制会对数组进行扩展,使数组的shape
属性值一样,然后就可以进行矢量化运算。
如图所示,输入一个4行1列和一个1行3列的数组,在二者进行计算的时候会按照广播机制进行扩展使两者都成为4行3列的数组再进行矢量运算,代码为:
import numpy as np
a = np.array([[1], [4], [6], [8]])
b = np.array([3, 2, 7])
print("a+b=")
print(a + b)
输出为:
a+b=
[[ 4 3 8]
[ 7 6 11]
[ 9 8 13]
[11 10 15]]
广播的规则:
- 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。
- 输出数组的形状是输入数组形状的各个维度上的最大值。
- 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。
- 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。
2.5.3 数组与标量间的运算
数组与标量之间的运算类似于广播机制,标量会扩展为与输入相同大小的数组,所产生的数组中的所有元素均为该标量,例如:
import numpy as np
a = np.arange(10)
a.shape = (2, 5)
b = 1
print("a=", a)
print("a+b=", a + b)
输出为:
a= [[0 1 2 3 4]
[5 6 7 8 9]]
a+b= [[ 1 2 3 4 5]
[ 6 7 8 9 10]]
2.5.4 数组的操作
NumPy中包含了一些函数用于处理数组,如图所示:
修改数组形状
函数 | 描述 |
---|---|
reshape |
不改变数据的条件下修改形状 |
flat |
数组元素迭代器 |
flatten |
返回一份数组拷贝,对拷贝所做的修改不会影响原始数组 |
ravel |
返回展开数组 |
numpy.reshape
格式:
numpy.reshape(arr, newshape, order='C')
arr
:要修改形状的数组newshape
:整数或者整数数组,新的形状应当兼容原有形状- order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'k' -- 元素在内存中的出现顺序。
代码:
import numpy as np
a = np.arange(6)
print("原始数组为:")
print(a, '\n')
b = a.reshape(2, 3)
print("修改后的数组为:")
print(b)
输出:
原始数组为:
[0 1 2 3 4 5]
修改后的数组为:
[[0 1 2]
[3 4 5]]
numpy.ndarray.flat
数组元素迭代器,对数组中每个元素都进行处理,可以使用flat属性。
代码:
import numpy as np
a = np.arange(6).reshape(2,3)
print("原始数组:")
for r in a:
print(r)
print("迭代后的数组:")
for e in a.flat:
print(e)
输出:
原始数组:
[0 1 2]
[3 4 5]
迭代后的数组:
0
1
2
3
4
5
numpy.ndarray.flatten
返回一份数组拷贝,对拷贝数组所做的修改不会影响原始数组。
格式:
ndarray.flatten(order='C')
参数说明:
- order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'K' -- 元素在内存中的出现顺序。
代码:
import numpy as np
a = np.arange(8).reshape(2, 4)
print("原数组:")
print(a, '\n')
print("展开的数组:")
print(a.flatten(), '\n')
print("以F风格顺序展开的数组:")
print(a.flatten(order='F'), '\n')
输出:
原数组:
[[0 1 2 3]
[4 5 6 7]]
展开的数组:
[0 1 2 3 4 5 6 7]
以F风格顺序展开的数组:
[0 4 1 5 2 6 3 7]
numpy.ravel
numpy.ravel() 展平的数组元素,顺序通常是"C风格",返回的是数组视图,修改会影响原始数组。
语法:
numpy.ravel(a, order='C')
参数说明:
- order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'K' -- 元素在内存中的出现顺序。
代码:
import numpy as np
a = np.arange(8).reshape(2, 4)
print("原数组:")
print(a, '\n')
print("使用ravel后:")
print(a.ravel(), '\n')
print("以F风格调用ravel后:")
print(a.ravel(order='F'), '\n')
输出:
原数组:
[[0 1 2 3]
[4 5 6 7]]
使用ravel后:
[0 1 2 3 4 5 6 7]
以F风格调用ravel后:
[0 4 1 5 2 6 3 7]
flatten
与ravel
import numpy as np
a = np.arange(8).reshape(2, 4)
a_2 = a
b = a.flatten()
c = a_2.ravel()
b[1] = 10
c[1] = 10
print("b=", b)
print("a=", a)
print("c=", c)
print("a_2=", a_2)
输出:
b= [ 0 10 2 3 4 5 6 7]
a= [[ 0 10 2 3]
[ 4 5 6 7]]
c= [ 0 10 2 3 4 5 6 7]
a_2= [[ 0 10 2 3]
[ 4 5 6 7]]
容易看出修改ravel
中的值对原数组元素是有影响的,而修改flatten
中的值对原数组无影响。
翻转数组
函数 | 描述 |
---|---|
transpose |
对换数组的维度 |
ndarray.T |
和 self.transpose() 相同 |
rollaxis |
向后滚动指定的轴 |
moveaxis |
移动轴的位置 |
swapaxes |
对换数组的两个轴 |
numpy.transpose
numpy.transpose() 函数用于对换数组的维度
语法:
numpy.transpose(arr, axes)
参数说明:
arr
:要操作的数组axes
:整数列表,对应维度,通常所有维度都会对换。
代码:
import numpy as np
a = np.arange(12).reshape(3, 2, 2)
print('原数组:')
print(a, '\n')
print('置换数组:')
print(np.transpose(a))
print(np.transpose(a, (2, 1, 0)))
输出:
原数组:
[[[ 0 1]
[ 2 3]]
[[ 4 5]
[ 6 7]]
[[ 8 9]
[10 11]]]
置换数组:
[[[ 0 4 8]
[ 2 6 10]]
[[ 1 5 9]
[ 3 7 11]]]
[[[ 0 4 8]
[ 2 6 10]]
[[ 1 5 9]
[ 3 7 11]]]
有关参数axes
的解释
以上例为例,a = np.arange(12).reshape(3, 2, 2)
产生一个如图所示的数组:
对于transpose
函数中的axes如果不定义则默认全部转置,例如在三维数组中使用transpose
会将坐标轴由(x,y,z)转置为(z,y,x),通常的表达式为:np.transpose(a,(0,1,2))
其中0代表x轴,1代表y轴,2代表z轴,如果希望在新生成的数组中交换y轴和z轴,则只需输入np.transpose(a,(0,2,1)),如果是四维数组则第四个坐标对应的数字为3,即默认定义的四维数组的axes=(0,1,2,3)
numpy.rollaxis
numpy.rollaxis 函数向后滚动特定的轴到一个特定位置。
语法:
numpy.rollaxis(arr, axis, start)
参数说明:
arr
:数组axis
:要向后滚动的轴,其它轴的相对位置不会改变start
:默认为零,表示完整的滚动。会滚动到特定位置。
代码:
import numpy as np
a = np.arange(24).reshape(4, 3, 2)
print("滚动坐标轴:")
print(np.rollaxis(a, 2).shape)
输出:
滚动坐标轴:
(2, 4, 3)
(4, 3, 2)
通过一幅图来了解一下这个函数的机制:
假设创建的一个数组arr为5维数组,即arr.ndim=5
,则axis
的取值为[-5,4],start
取值[-5,5],如果在函数中不定义start
值默认取0
,而axis
为要移动的轴,轴从0
开始编号,当然反向同样成立,对于一个n维数组,轴0
也是轴-n
,而轴a
为轴-n+a
,官方文档对start
的定义为The axis is rolled until it lies before this position. The default, 0, results in a “complete” roll.**翻译过来就是轴滚动,直到它位于此位置之前。默认值 0 会导致"完整"滚动。**通过对五维数组的尝试,可以发现,以axis
和-arr.ndim+axis
为分隔,当start
值小于等于axis
或-arr.ndim+axis
时,原来位于轴axis
或轴-arr.ndim+axis
的元素滚动至轴start
处,其余轴的相对位置保持不变,当start
值大于axis
或-arr.ndim+axis
时,原来位于轴axis
或轴-arr.ndim+axis
的元素滚动至轴start-1
处,其余轴的相对位置保持不变。
numpy.moveaxis
(版本 1.11.0 中的新增功能) 将数组的轴移动到新位置,其他轴保持原始顺序类似于numpy.rollaxis
,但是这个函数只需指定始位置与终位置即可,且可通过序列定义。
语法:
numpy.moveaxis(arr, source, destination)
参数说明:
arr
:输入的数组source
:要移动的轴的原始位置。这些必须是唯一的。(整型int或整型数列**[int1,int2,...,int n]**)destination
:要移动的轴的目标位置。这些必须是唯一的。(整型int或整型数列**[int1,int2,...,int n]**)
代码:
import numpy as np
a = np.zeros((1, 2, 3, 4, 5))
print(np.moveaxis(a, [1, 2], [-1, -5]).shape)
输出:
(3, 1, 4, 5, 2)
如果输入print(np.moveaxis(a, [1, 1], [-1, -5]).shape)
会报错,原因是moveaxis
第二个参数[1,1]
冲突。
numpy.swapaxes
numpy.swapaxes 函数用于交换数组的两个轴。
语法:
numpy.swapaxes(arr, axis1, axis2)
参数说明:
arr
:输入的数组axis1
:对应第一个轴的整数axis2
:对应第二个轴的整数
代码:
import numpy as np
a = np.ones((1, 2, 3, 4, 5))
print(np.swapaxes(a,0,-3).shape)
输出:
(3, 2, 1, 4, 5)
更改维度数
方法 | 描述 |
---|---|
atleast_1d(*arys) | 将输入转换为至少一维的数组。 |
atleast_2d(*arys) | 将输入视为至少具有二维的数组。 |
atleast_3d(*arys) | 以至少三个维度的数组形式查看输入。 |
broadcast | 产生模仿广播的对象。 |
broadcast_to(array, shape[, subok]) | 将数组广播为新形状。 |
broadcast_arrays(*args, **kwargs) | 互相广播任意数量的阵列。 |
expand_dims(a, axis) | 扩展数组的形状。 |
squeeze(a[, axis]) | 从数组形状中删除一维条目。 |
改变数组的种类
方法 | 描述 |
---|---|
asarray(a[, dtype, order]) | 将输入转换为数组。 |
asanyarray(a[, dtype, order]) | 将输入转换为ndarray,但通过ndarray子类。 |
asmatrix(data[, dtype]) | 将输入解释为矩阵。 |
asfarray(a[, dtype]) | 返回转换为浮点类型的数组。 |
asfortranarray(a[, dtype]) | 返回以Fortran顺序排列在内存中的数组(ndim> = 1)。 |
ascontiguousarray(a[, dtype]) | 返回内存中的连续数组(ndim> = 1)(C顺序)。 |
asarray_chkfinite(a[, dtype, order]) | 将输入转换为数组,检查NaN或Infs。 |
asscalar(a) | 将大小为1的数组转换为其等效的标量。 |
require(a[, dtype, requirements]) | 返回提供的类型满足要求的ndarray。 |
组合数组
方法 | 描述 |
---|---|
concatenate((a1, a2, …) | 沿现有轴连接一系列数组。 |
stack(arrays[, axis, out]) | 沿新轴连接一系列数组。 |
column_stack(tup) | 将一维数组作为列堆叠到二维数组中。 |
dstack(tup) | 沿深度方向(沿第三轴)按顺序堆叠数组。 |
hstack(tup) | 水平(按列)顺序堆叠数组。 |
vstack(tup) | 垂直(行)按顺序堆叠数组。 |
block(arrays) | 从块的嵌套列表中组装一个nd数组。 |
拆分数组
方法 | 描述 |
---|---|
split(ary, indices_or_sections[, axis]) | 将数组拆分为多个子数组,作为ary的视图。 |
array_split(ary, indices_or_sections[, axis]) | 将一个数组拆分为多个子数组。 |
dsplit(ary, indices_or_sections) | 沿第3轴(深度)将数组拆分为多个子数组。 |
hsplit(ary, indices_or_sections) | 水平(按列)将一个数组拆分为多个子数组。 |
vsplit(ary, indices_or_sections) | 垂直(行)将数组拆分为多个子数组。 |
平铺数组
方法 | 描述 |
---|---|
tile(A, reps) | 通过重复A代表次数来构造一个数组。 |
repeat(a, repeats[, axis]) | 重复数组的元素。 |
添加和删除元素
方法 | 描述 |
---|---|
delete(arr, obj[, axis]) | 返回一个新的数组,该数组具有沿删除的轴的子数组。 |
insert(arr, obj, values[, axis]) | 沿给定轴在给定索引之前插入值。 |
append(arr, values[, axis]) | 将值附加到数组的末尾。 |
resize(a, new_shape) | 返回具有指定形状的新数组。 |
trim_zeros(filt[, trim]) | 修剪一维数组或序列中的前导和/或尾随零。 |
unique(ar[, return_index, return_inverse, …]) | 查找数组的唯一元素。 |
重新排列元素
方法 | 描述 |
---|---|
flip(m[, axis]) | 沿给定轴颠倒数组中元素的顺序。 |
fliplr(m) | 左右翻转数组。 |
flipud(m) | 上下翻转阵列。 |
reshape(a, newshape[, order]) | 在不更改数据的情况下为数组赋予新的形状。 |
roll(a, shift[, axis]) | 沿给定轴滚动数组元素。 |
rot90(m[, k, axes]) | 在轴指定的平面中将阵列旋转90度。 |
2.6 函数
2.6.1 通用函数
在Numpy中提供了诸如sin,cos和exp等常见的数学函数,这些函数称为通用函数(ufunc),通用函数是针对ndarray数据执行元素级运算的函数,函数返回是一个新的数组,通常,将通用函数中接受一个数组参数的函数称为一元通用函数,接受两个数组参数的称为二元通用函数。
常用的一元通用函数
函数 | 描述 |
---|---|
abs、fabs | 计算整数、浮点数或辅助的绝对值 |
sqrt | 计算各元素的算术平方根 |
square | 计算各元素的平方 |
exp | 计算各元素的指数ex |
log、log10、log2、log1p | 分别为自然对数(底数为e),底数为10的对数,底数为2的对数,log(1+x) |
sign | 计算各元素的正负号:1(正数)、0(零)、-1(负数) |
ceil | 计算各元素的ceilling值,即大于或者等于该值的最小整数 |
floor | 计算个元素的floor值,即小于或者等于该值的最大整数 |
rint | 将各元素四舍五入到最接近的整数 |
modf | 将数组的小数和整数部分以两个独立数组的形式返回 |
isnan | 返回一个表示“哪些值是NaN”的布尔型数组 |
isfinite、isinf | 分别返回表示“哪些元素是有穷的”或“哪些元素是无穷的”的布尔型数组 |
sin、sinh、cos、cosh、tan、tanh | 普通型和双曲型三角函数 |
arcos、arccosh、arcsin | 反三角函数 |
常见的二元通用函数
函数 | 描述 |
---|---|
add | 将数组中对应的元素相加 |
subtract | 从第一个数组减去第二个数组中的元素 |
multiply | 两个数组元素相乘 |
divide、floor_divide | 除法或向下整除法(舍去余数) |
maximum、fmax | 元素级的最大值计算 |
minimum、fmin | 元素级的最小值计算 |
mod | 元素级的求模运算 |
copysign | 将第二个数组中的值得符号赋值给第一个数组中的值 |
greater、greater_equal、less、less_equal、equal、not_equal、logical_and、logical_or、logical_xor | 执行元素级的比较运算,最终产生布尔型数组,相当于运算符>、≥、<、≤、==、!=、逻辑和、逻辑或、逻辑异或 |
代码:
# 通用函数
import numpy as np
# 计算各元素的平方根
a = np.array([1, 4, 9])
print("数组平方根为:", np.sqrt(a))
# 返回数组的整数与小数
b = np.array([1.2, 2.5, 3])
c = np.modf(b)[0]
d = np.modf(b)[1]
print("b数组的小数部分为:", c, "\nb数组的整数部分为:", d)
# 符号赋予
e = np.ones(5)
e[::2] = -1
f = np.arange(5)
g = np.copysign(f, e)
print("符号赋予后的数组:", g)
# 元素级比较
print("e和g数组比较:", np.greater(e, g))
输出:
数组平方根为: [1. 2. 3.]
b数组的小数部分为: [0.2 0.5 0. ]
b数组的整数部分为: [1. 2. 3.]
符号赋予后的数组: [-0. 1. -2. 3. -4.]
e和g数组比较: [False False True False True]
2.6.2 统计函数
1. 将条件逻辑转换为数组运算
numpy的where()
函数是三元表达式x if condition else y
的矢量化版本,可以通过逻辑转化为数组运算,例如:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
con = np.array([True, False, True])
print(np.where(con, a, b))
输出为[1 5 3]
,可以看出,where
函数的第一个参数作为判定条件,如果元素为True
则选取第二个数组中对应位置的元素,如果为False
则选取第三个数组中对应位置的元素。当然判定数组也可以使用1
代表True
,0
代表False
,即con = np.array([1, 0, 1])
也能得到同样的结果。
2. 数组的统计运算
NumPy数组中与统计运算相关的方法
方法 | 描述 |
---|---|
sum | 对数组中全部或某个轴向的元素求和 |
mean | 算术平均值 |
min | 计算数组中的最小值 |
max | 计算数组中的最大值 |
argmin | 输出最小值索引 |
argmax | 输出最大值索引 |
cumsum | 所有元素的累计和 |
cumprod | 所有元素的累计积 |
代码:
import numpy as np
a = np.arange(10)
print("累计和为:", np.cumsum(a))
print("累计积为:", np.cumprod(a))
对于累计和如下图所示,累计积同理:
输出:
累计和为: [ 0 1 3 6 10 15 21 28 36 45]
累计积为: [0 0 0 0 0 0 0 0 0 0]
3. 数组排序
对数组中的元素进行排序,只需使用sort()
函数,例如:
import numpy as np
arr = np.array([[1, 5, 2, 3], [5, 8, 3, 1]])
arr.sort()
print(arr)
输出:
[[1 2 3 5]
[1 3 5 8]]
排序方法会修改原来的数组,函数中的参数代表着轴,例如:
import numpy as np
arr = np.array([[1, 5, 2, 3], [5, 8, 3, 1]])
arr.sort(0)
print(arr)
则得到的结果为:
[[1 5 2 1]
[5 8 3 3]]
4. 检索数组元素
在NumPy中,all()
函数用于判断整个数组中的元素的值是否全部满足条件,如果全部满足条件则返回True
,否则返回False
。any()
函数用于判断整个数组中的元素至少有一个满足条件则返回True
,否则返回False
。
代码:
import numpy as np
arr = np.arange(12).reshape(3, 4)
print(np.any(arr > 10))
print(np.all(arr > 10))
输出:
True
False
5. 唯一化及其他集合逻辑
unique()
去除数组中的重复元素,保留唯一元素,即如果数组为[1,1,2,2]
则输出为[1,2]
,且排序输出数组
语法:
unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None)
参数说明:
ar
: 待操作数组return_index
: 输出数组元素对应的索引(可选,默认False)return_inverse
: 输出重建数组元素索引(可选,默认False)return_count
: 输出每一元素出现的次数(可选,默认False)axis
:函数的操作维度(可选)
图解参数:
对于操作维度,如果不定义默认将数组降为一维,对一维数组进行unique
操作。
代码:
import numpy as np
arr = np.array([7, 2, 1, 1, 8, 2])
print(np.unique(arr, return_index=True))
print(np.unique(arr, return_inverse=True))
print(np.unique(arr, return_counts=True))
arr_2 = np.array([[0, 1, 0], [0, 1, 0], [2, 3, 4]])
print(np.unique(arr_2))
print(np.unique(arr_2, axis=0))
print(np.unique(arr_2, axis=1))
输出:
(array([1, 2, 7, 8]), array([2, 1, 0, 4],
dtype=int64))
(array([1, 2, 7, 8]), array([2, 1, 0, 0, 3, 1], dtype=int64))
(array([1, 2, 7, 8]), array([2, 2, 1, 1],
dtype=int64))
[0 1 2 3 4]
[[0 1 0]
[2 3 4]]
[[0 0 1]
[0 0 1]
[2 4 3]]
在二维数组中对1
轴使用unique
函数时,排序以输出的第一行为准,例如:
[[4 3 2 1]
[5 6 7 8]]
输出将为:
[[1 2 3 4]
[8 7 6 5]]
数组集合运算的常见函数
函数 | 描述 |
---|---|
unique(x) | 计算x中的唯一元素,并返回有序结果 |
intersect1d(x,y) | 计算x和y中的公共元素,并返回有序结果 |
union1d(x,y) | 计算x和y的并集,并返回有序结果 |
in1d(x,y) | 得到一个表示"x的元素是否包含y"的布尔型数组 |
setdiff1d(x,y) | 集合的差,即元素在x中且不再y中 |
setxor1d(x,y) | 集合的对称差,即存在于一个数组中但不同时存在于两个元素中的元素 |
2.6.3 线性代数相关函数
线性代数是数学运算中的一个重要工具,在numpy.linalg模块中有一组标准的矩阵分解运算以及诸如逆和行列式之类的东西,下面的例子代表矩阵的乘法:
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([[1, 2], [3, 4], [5, 6]])
print(a.dot(b)) # 等价于np.dot(a,b)
输出为:
[[22 28]
[49 64]]
linalg模块常用函数
函数 | 描述 |
---|---|
dot | 矩阵乘法 |
diag | 以一维数组的形式返回方阵的对角线或将一维数组转为方阵 |
trace | 计算对角线元素和 |
det | 计算矩阵的行列式 |
eig | 计算方阵的特征值和特征向量 |
inv | 计算方阵的逆 |
qr | 计算qr分解 |
svd | 计算奇异值 |
solve | 解线性方程组Ax=b,其中A是一个方阵 |
lstsq | 计算Ax=b的最小二乘解 |
2.6.4 随机数相关函数
与Python的random模块相比,NumPy的random模块功能更多,它增加了一些可以高效生成多种概率分布的样本值函数。
import numpy as np
# 随机生成一个3行3列的二维数组
print(np.random.rand(3, 3))
# 随机生成一个的三维数组
print(np.random.rand(2, 3, 3))
输出:
[[0.51977788 0.78215477 0.03626778]
[0.53886531 0.84636492 0.20763375]
[0.62779302 0.01742627 0.21344803]]
[[[0.21952024 0.79324513 0.99029048]
[0.25493268 0.64863243 0.61611571]
[0.85816947 0.52925024 0.16129451]]
[[0.02905666 0.85701923 0.81112184]
[0.93242048 0.2237645 0.35160048]
[0.8884219 0.59137365 0.81497935]]]
random模块常见函数
函数 | 描述 |
---|---|
seed | 生成随机数种子 |
rand | 生成均匀分布的样本值 |
randint | 从给定的上下限范围内随机选取整数 |
normal | 产生正态分布的样本值 |
beta | 产生Beta分布的样本值 |
uniform | 产生在[0,1]中均匀分布的样本值 |