计算库numpy基础使用

310 阅读13分钟

NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。NumPy是使用Python进行科学计算的基础库。NumPy以一个强大的N维数组对象为中心,它还包含有用的线性代数、傅立叶变换和随机数函数。

创建数组

zeros()

zeros(shape, dtype=float, order='C')

shape:数据尺寸 例如:zeros(5) ----就是包含5个元素的零矩阵,默认dtype=numpy.float64int8, int16, 可用的元素类型包括int32, int64, uint8|16|32|64, float16|32|64 and complex64|128

zeros()创建包含任意0值的数组,默认是浮点数,所以会是0.这种形式

创建二/多维数组也很方便,传入一个元组参数即可

print(np.zeros(5,dtype=np.int64)) # [0 0 0 0 0]
print(np.zeros((2,3,5),dtype=np.int64)) #三维矩阵
"""
[[[0 0 0 0 0]
  [0 0 0 0 0]
  [0 0 0 0 0]]

 [[0 0 0 0 0]
  [0 0 0 0 0]
  [0 0 0 0 0]]]
"""

数组属性

numpy返回的数组都有一些共同的属性,并且返回的数组类型是ndarray

image.png

shape

表示数组的长度,多维数组返回元组

ndim

数组的维度

size

数组的元素数量

image.png

ones()

与zeros一样,返回被1填充的数组

np.ones((3,4))
//
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])

full()

返回使用指定值填充的数组

np.full((3,4), np.pi)
//
array([[3.14159265, 3.14159265, 3.14159265, 3.14159265],
       [3.14159265, 3.14159265, 3.14159265, 3.14159265],
       [3.14159265, 3.14159265, 3.14159265, 3.14159265]])

array()

返回指定元素的数组,相当于就是把python中的数组转成Numpy自定义的ndarray类型

np.array([[1,2,3,4], [10, 20, 30, 40]])
//
array([[ 1,  2,  3,  4],
       [10, 20, 30, 40]])

arrange()

同python中的range()一样创建范围数组,可以指定步长

np.arange(1, 5)
// array([1, 2, 3, 4])
np.arange(1.0, 5.0)
//array([1., 2., 3., 4.])
np.arange(1, 5, 0.5)
//array([1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])

linspace()

同arrange,但处理浮点数时最好使用linspace(),指定在范围内生成多少个点

print(np.linspace(0, 5/3, 6))//指定在0,5/3之间生成6个点
[0.         0.33333333 0.66666667 1.         1.33333333 1.66666667]

rand()与randn()

给定元组,rand()创建一个[0,1]范围内的随机数填充数组,随机值服从均匀分布

np.random.rand(3,4)
//
array([[0.07951522, 0.82516403, 0.54524215, 0.46662691],
       [0.12016334, 0.74912183, 0.183234  , 0.105027  ],
       [0.22051959, 0.26931151, 0.02739192, 0.4721405 ]])

randn()效果差不多,但是是包含从均值为0、方差为1的单变量正态分布(高斯分布)中采样的随机浮点数

np.random.randn(3,4)
//
array([[ 0.09545957,  0.14828368, -0.91504156, -0.36224068],
       [ 0.55434999,  0.41143633,  0.84385243, -0.3652369 ],
       [ 1.48071803, -1.45297797,  1.24551713,  0.4508626 ]])

数组数据

dtype

返回数组中元素类型

c = np.arange(1, 5)
print(c.dtype, c)
//int64 [1 2 3 4]
//
c = np.arange(1.0, 5.0)
print(c.dtype, c)
//float64 [ 1.  2.  3.  4.]

itemsize

返回每个元素所占据的byte

e = np.arange(1, 5, dtype=np.complex64)
e.itemsize
//8

修改数组

修改数组维度shape()

只需要调用shape()就可以修改数组维度,但是要保证前后数组元素一致

g = np.arange(24)
print(g)
print("Rank:", g.ndim)
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
Rank: 1
//
g.shape = (6, 4)
print(g)
print("Rank:", g.ndim)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]
Rank: 2
//
g.shape = (2, 3, 4)
print(g)
print("Rank:", g.ndim)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
Rank: 3

修改维度reshape()

reshape()也用来修改维度,但是返回的是指向原数组数据的对象,也就是引用原来的对象,它改变时原对象也会改变

g2 = g.reshape(4,6)
print(g2)
print("Rank:", g2.ndim)
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]
Rank: 2
//Set item at row 1, col 2 to 999 (more about indexing below).
g2[1, 2] = 999
g2
array([[  0,   1,   2,   3,   4,   5],
       [  6,   7, 999,   9,  10,  11],
       [ 12,  13,  14,  15,  16,  17],
       [ 18,  19,  20,  21,  22,  23]])
The corresponding element in g has been modified.
g
array([[[  0,   1,   2,   3],
        [  4,   5,   6,   7],
        [999,   9,  10,  11]],

       [[ 12,  13,  14,  15],
        [ 16,  17,  18,  19],
        [ 20,  21,  22,  23]]])

返回一维ravel()

ravel函数返回一个新的一维数据数组,该数组也指向相同的数据(类型同样为ndarray

g.ravel()
array([  0,   1,   2,   3,   4,   5,   6,   7, 999,   9,  10,  11,  12,
        13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23])

数组间元素运算

所有常用的算术运算符(+、-、*、/、/、/、***等)都可以与ndarray一起使用。适用于元素方面,但是注意这不是矩阵运算,只是数组间元素一对一运算

a = np.array([14, 23, 32, 41])
b = np.array([5,  4,  3,  2])
print("a + b  =", a + b)
print("a - b  =", a - b)
print("a * b  =", a * b)
print("a / b  =", a / b)
print("a // b  =", a // b)
print("a % b  =", a % b)
print("a ** b =", a ** b)
a + b  = [19 27 35 43]
a - b  = [ 9 19 29 39]
a * b  = [70 92 96 82]
a / b  = [  2.8          5.75        10.66666667  20.5       ]
a // b  = [ 2  5 10 20]
a % b  = [4 3 2 1]
a ** b = [537824 279841  32768   1681]

广播规则

numpy的数组间元素运算通常需要满足相同维度、数组元素数量相同等条件,如果不满足,将遵循以下规则

1. 低维与高维数组运算,会将低维数组维度+1直到两者维度相同

如三维数组和一维数组相加,将一维数组提升至三维

h = np.arange(5).reshape(1, 1, 5)
h
//array([[[0, 1, 2, 3, 4]]])
h + [10, 20, 30, 40, 50]  # same as: h + [[[10, 20, 30, 40, 50]]]
//array([[[10, 21, 32, 43, 54]]])

2.不管多少维,如果该维度只有一个元素,那么运算时会按最大维度重复使用该元素运算

k = np.arange(6).reshape(2, 3)
k
array([[0, 1, 2],
       [3, 4, 5]])
k + [[100], [200]]  # same as: k + [[100, 100, 100], [200, 200, 200]]
//100200分别运算三次
array([[100, 101, 102],
       [203, 204, 205]])

结合第一和第二规则有以下效果:

k + [100, 200, 300]  # after rule 1: [[100, 200, 300]], and after rule 2: [[100, 200, 300], [100, 200, 300]]
array([[100, 201, 302],
       [103, 204, 305]])
k + 1000  # same as: k + [[1000, 1000, 1000], [1000, 1000, 1000]]
array([[1000, 1001, 1002],
       [1003, 1004, 1005]])

3.通过规则1和2,数组运算必须匹配

try:
    k + [33, 44]
except ValueError as e:
    print(e)
//operands could not be broadcast together with shapes (2,3) (2,) 

广播规则用于许多 NumPy 操作,而不仅仅是算术运算,上述规则在出现奇怪问题的时候再具体研究

堆叠数组vstack/hstack

使用vstack水平将多个数组叠加为一个数组

q1 = np.full((3,4), 1.0)
q1
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])

q2 = np.full((4,4), 2.0)
q2
array([[ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.]])
q3 = np.full((3,4), 3.0)
q3
array([[ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.]])
//--------------
q4 = np.vstack((q1, q2, q3))
q4
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.]])
q4.shape
(10, 4)

同理,hstack垂直将多个数组叠加为一个数组

q5 = np.hstack((q1, q3))
q5
array([[ 1.,  1.,  1.,  1.,  3.,  3.,  3.,  3.],
       [ 1.,  1.,  1.,  1.,  3.,  3.,  3.,  3.],
       [ 1.,  1.,  1.,  1.,  3.,  3.,  3.,  3.]])

q5.shape
(3, 8)

但是注意堆叠的前提条件是维度一致,比如上述vstack可以是因为有相同数量的列,hstack有相同数量的行,下述情况就不行

try:
    q5 = np.hstack((q1, q2, q3))
except ValueError as e:
    print(e)
all the input array dimensions except for the concatenation axis must match exactly

concatenate

concatenate可以指定维度堆叠,与vstack和hstack功能相同

q7 = np.concatenate((q1, q2, q3), axis=0)  # Equivalent to vstack
q7
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.]])
q7.shape
(10, 4)

stack

stack沿新轴堆叠数组。所有行列必须具有相同的形状。

q8 = np.stack((q1, q3))
q8
array([[[ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.],
        [ 1.,  1.,  1.,  1.]],

       [[ 3.,  3.,  3.,  3.],
        [ 3.,  3.,  3.,  3.],
        [ 3.,  3.,  3.,  3.]]])
q8.shape
(2, 3, 4)

切分数组vsplit/hsplit

同样可以水平或垂直切分数组

r = np.arange(24).reshape(6,4)
r
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
r1, r2, r3 = np.vsplit(r, 3)
r1
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
r2
array([[ 8,  9, 10, 11],
       [12, 13, 14, 15]])
r3
array([[16, 17, 18, 19],
       [20, 21, 22, 23]])
r4, r5 = np.hsplit(r, 2)
r4
array([[ 0,  1],
       [ 4,  5],
       [ 8,  9],
       [12, 13],
       [16, 17],
       [20, 21]])
r5
array([[ 2,  3],
       [ 6,  7],
       [10, 11],
       [14, 15],
       [18, 19],
       [22, 23]])

数学与统计运算

统计特征函数mode/min...

创建数组返回的ndarray自带许多统计学特征(函数),如中值、平均值等

image.png

还有min、max、sum等

a = np.array([[-2.5, 3.1, 7], [10, 11, 12]])
print(a)
print("mean =", a.mean())
[[ -2.5   3.1   7. ]
 [ 10.   11.   12. ]]
mean = 6.76666666667

上述函数可以传参数axis指定维度返回统计信息

c=np.arange(24).reshape(2,3,4)
c
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
//
c.sum(axis=0)  # sum across matrices(0+12、1+13...)
array([[12, 14, 16, 18],
       [20, 22, 24, 26],
       [28, 30, 32, 34]])

//
c.sum(axis=1)  # sum across rows
//
array([[12, 15, 18, 21],
       [48, 51, 54, 57]])

传递元组指定多个维度

c.sum(axis=(0,2))  # sum across matrices and columns
//
array([ 60,  92, 124])
//
0+1+2+3 + 12+13+14+15, 4+5+6+7 + 16+17+18+19, 8+9+10+11 + 20+21+22+23
(60, 92, 124)

通用数学函数abs/sqrt...

NumPy包含一系列通用函数,常用的有abs、square等

a = np.array([[-2.5, 3.1, 7], [10, 11, 12]])
np.square(a)
//求平方
array([[   6.25,    9.61,   49.  ],
       [ 100.  ,  121.  ,  144.  ]])

image.png

数组元素访问

一维数组

通常可以向访问普通一维数组那样访问ndarray

image.png

稍有不同的是,切片只能一个元素一个元素切,而不能是数组,也不能使用del切片删除元素

a[2:5] = -1
a
//array([ 1,  5, -1, -1, -1,  7,  3])
try:
    a[2:5] = [1,2,3,4,5,6]  # too long
except ValueError as e:
    print(e)
//cannot copy sequence with size 6 to array axis with dimension 3
try:
    del a[2:5]
except ValueError as e:
    print(e)
//cannot delete array elements

同样需要注意,切片返回的是原数组数据,如果要复制的话需要使用copy函数

a_slice = a[2:6]
a_slice[1] = 1000
a  # the original array was modified!
//array([   1,    5,   -1, 1000,   -1,    7,    3])
a[3] = 2000
a_slice  # similarly, modifying the original array modifies the slice!
array([  -1, 2000,   -1,    7])
/////////////
another_slice = a[2:6].copy()
another_slice[1] = 3000
a  # the original array is untouched
//array([   1,    5,   -1, 2000,   -1,    7,    3])

多维数组

多维数组元素访问方式类似

image.png

注意最后一个切片,返回一个二维数组

如果省略某些轴的坐标,则返回这些轴中的所有元素

省略号...

可以写一个省略号(...)来要求完全包括所有未指定的轴。

image.png

迭代

遍历 ndarray 与遍历常规 python 数组非常相似。注意,对多维数组的迭代是相对于第一个轴完成的,返回的可能还是多维数组

image.png

迭代所有元素flat

使用flat属性可以迭代所有元素

for i in c.flat:
    print("Item:", i)
Item: 0
Item: 1
Item: 2
Item: 3
Item: 4
......
Item: 23

判断数组相同allclose()

判断数组所有元素相同

np.allclose(n1,n2)

线性代数/矩阵运算

最常用的部分之一

矩阵转置T/transpose

T 属性等效于在 rank ≥2 时调用 transpose(),完成矩阵转置

m1 = np.arange(10).reshape(2,5)
m1
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
m1.T
array([[0, 5],
       [1, 6],
       [2, 7],
       [3, 8],
       [4, 9]])

矩阵乘法dot()

使用dot()完成矩阵乘

image.png

逆矩阵inv()/伪逆矩阵pinv()

numpy.linalg 模块中提供了许多线性代数函数,特别是用于计算逆矩阵的 inv 函数

import numpy.linalg as linalg
m3 = np.array([[1,2,3],[5,7,11],[21,29,31]])
m3
array([[ 1,  2,  3],
       [ 5,  7, 11],
       [21, 29, 31]])
linalg.inv(m3)
array([[-2.31818182,  0.56818182,  0.02272727],
       [ 1.72727273, -0.72727273,  0.09090909],
       [-0.04545455,  0.29545455, -0.06818182]])

使用pinv()函数计算伪逆矩阵

linalg.pinv(m3)
array([[-2.31818182,  0.56818182,  0.02272727],
       [ 1.72727273, -0.72727273,  0.09090909],
       [-0.04545455,  0.29545455, -0.06818182]])

单位矩阵eye()

可以通过调用 eye 创建一个大小为 NxN 的单位矩阵:

np.eye(3)
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])

求解线性方程组solve()

solve 函数求解线性标量方程组,例如:

  • 2x+6y=62x + 6y = 6
  • 5x+3y=95x + 3y = -9
coeffs  = np.array([[2, 6], [5, 3]])
depvars = np.array([6, -9])
solution = linalg.solve(coeffs, depvars)
solution
array([-3.,  2.])

矢量化运算meshgrid()

矢量化运算即针对整个数组进行运算而非对数组中的单个元素一个个运算,可以优化执行速度

例如,假设我们要根据公式 sin(xy40.5) 生成一个 768x1024 的数组。一个不好的选择是使用嵌套循环在 python 中进行数学运算

import math
data = np.empty((768, 1024))
for y in range(768):
    for x in range(1024):
        data[y, x] = math.sin(x*y/40.5)  # BAD! Very inefficient.

当然,这行得通,但是由于循环是在纯 python 中发生的,因此效率非常低。向量化这个算法。首先,使用 NumPy 的meshgrid函数从坐标向量生成坐标矩阵。

x_coords = np.arange(0, 1024)  # [0, 1, 2, ..., 1023]
y_coords = np.arange(0, 768)   # [0, 1, 2, ..., 767]
X, Y = np.meshgrid(x_coords, y_coords)
X

上述将初始化X、Y两个768x1024的数组,然后针对矩阵直接运算即可

data = np.sin(X*Y/40.5)

保存和加载数据

NumPy 可以轻松地以二进制或文本格式保存和加载 ndarray

save/load

save默认保存.npy格式文件,load按.npy格式加载数据

a = np.random.rand(2,3)
a
array([[ 0.41307972,  0.20933385,  0.32025581],
       [ 0.19853514,  0.408001  ,  0.6038287 ]])
np.save("my_array", a)
a_loaded = np.load("my_array.npy")
a_loaded
array([[ 0.41307972,  0.20933385,  0.32025581],
       [ 0.19853514,  0.408001  ,  0.6038287 ]])

保存为格式化文件savetxt/loadtxt

savetxt可以保存数组为格式化文件,如csv

np.savetxt("my_array.csv", a)
with open("my_array.csv", "rt") as f:
    print(f.read())
4.130797191668116319e-01 2.093338525574361952e-01 3.202558143634371968e-01
1.985351449843368865e-01 4.080009972772735694e-01 6.038286965726977762e-01

可以设定第三个参数为分隔符,同理,loadtxt加载格式化数据

np.savetxt("my_array.csv", a, delimiter=",")
a_loaded = np.loadtxt("my_array.csv", delimiter=",")
a_loaded
array([[ 0.41307972,  0.20933385,  0.32025581],
       [ 0.19853514,  0.408001  ,  0.6038287 ]])

多个数据npz文件savez()

使用savez()也可以将多个数组保存在一个压缩文件中:

b = np.arange(24, dtype=np.uint8).reshape(2, 3, 4)
b
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]], dtype=uint8)

np.savez("my_arrays", my_a=a, my_b=b)

上述保存数组a和数组b两个数据,使用load直接加载会返回字典类型

my_arrays = np.load("my_arrays.npz")
my_arrays
<numpy.lib.npyio.NpzFile at 0x10fa4d4d0>
my_arrays.keys()
['my_b', 'my_a']

my_arrays["my_a"]
array([[ 0.41307972,  0.20933385,  0.32025581],
       [ 0.19853514,  0.408001  ,  0.6038287 ]])