Numpy 的核心是连续的多维数组。
Numpy中的数组叫做np.ndarray
,也可以使用别名np.array
。
但这里的np.array与Python标准库中的array.array是不同的。
下列是几个ndarray
中的重要属性:
ndarray.ndim
数组的维数。
ndarray.shape
数组的形状。
ndarray.size
数组的元素个数。
ndarray.dtype
数组的元素类型。
例子:
import numpy as np
data=np.arange(15).reshape(3,5)
print(data)
print(data.shape)
print(data.ndim)
print(data.size)
print(data.dtype,)
[[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] (3, 5) 2 15 int32
创建数组有多种方式。你可以使用np.array
直接用Python的元组和列表来创建。
import numpy as np
a=np.array([1,2,3])
print(a)
b=np.array([11,2,33])
print(b)
c=np.array([(1,2,3),(5.9,5,6)]) #创建二维数组
print(c)
d=np.array([(1,2),(3,4)],dtype=complex) #数组的类型可以在创建时显式声明
print(d)
[1 2 3] [11 2 33] [[1. 2. 3. ] [5.9 5. 6. ]] [[1.+0.j 2.+0.j] [3.+0.j 4.+0.j]]
通常,数组的元素的未知的,但是形状确实已知的。所以NumPy提供了多种创建空数组的方法。
np.zeros
创建全是0的数组。
np.ones
创建全是1的数组。
np.empty
创建初始值是随机数的数组。
需要注意的是上述方法创建的数组元素的类型是 float64
import numpy as np
e=np.zeros((3,4))
print(e.dtype)
f=np.ones((2,3,4),dtype=np.int16)#可以更改数据类型
print(f)
g=np.empty((2,3))
print(g)
float64 [[[1 1 1 1] [1 1 1 1] [1 1 1 1]] [[1 1 1 1] [1 1 1 1] [1 1 1 1]]] [[1. 2. 3. ] [5.9 5. 6. ]]
为了创建列表,NumPy提供了和 range
类似的函数。
np.arange(start,end,step)
a=np.arange(10,30,5)
print(a)
b=np.arange(0,2,0.3)#同样可以接收浮点数
print(b)
[10 15 20 25] [0. 0.3 0.6 0.9 1.2 1.5 1.8]
在生成浮点数列表时,最好不要使用np.arange
,而是使用np.linspace
。
np.linspace(start,stop,num)
np.linspace(0,2,4)
array([0. , 0.66666667, 1.33333333, 2. ])
当你打印一个数组时,NumPy显示数组的方式和嵌套的列表类似,但是会遵循以下布局:
一维数组显示成一行,二维数组显示成矩阵,三维数组显示成矩阵的列表。
a=np.arange(6)
print(a)
b=np.arange(12).reshape(4,3)
print(b)
c=np.arange(24).reshape(2,3,4)
print(c)
[0 1 2 3 4 5] [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]] [[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [[12 13 14 15] [16 17 18 19] [20 21 22 23]]]
当一个数组元素太多,不方便显示时,NumPy会自动数组的中间部分,只显示边角的数据。
print(np.arange(10000))
[ 0 1 2 ... 9997 9998 9999]
数组的算数计算是在元素层级运算的。计算结果会存在一个新创建的数组中。
import numpy as np
a=np.array([20,30,40,50])
b=np.arange(4)
print(b)
c=a-b
print(c)
print(b**2)
print(10*np.sin(a))
print(a<35)
[0 1 2 3] [20 29 38 47] [0 1 4 9] [ 9.12945251 -9.88031624 7.4511316 -2.62374854] [ True True False False]
在NumPy中*
号仍然表示乘法,矩阵乘积用np.dot
来计算。
A=np.array([(1,1),(0,1)])
B=np.array([(2,0),(3,4)])
print(A*B) #对应数字相乘
print(A.dot(B))
print(np.dot(A,B)) #矩阵相乘
[[2 0] [0 4]] [[5 4] [3 4]] [[5 4] [3 4]]
类似于+=
和*=
的运算是直接在现有数组上计算的,没有创建新的数组。Numpy中的计算同样也是向上转型的,可以简单理解成浮点数和整数运算的结果是浮点数。
a = np.ones((2,3), dtype=int)
b = np.random.random((2,3))
a*=3
print(a)
b += a
print(b)
# a += b # 浮点数不会自动转换成整数
[[3 3 3] [3 3 3]] [[3.23001532 3.83959207 3.41767746] [3.36479615 3.22202231 3.18277741]]
np.ndarray
提供了许多一元操作。比如数组求和、求最大最小值等。
a=np.random.random((2,3))
print(a)
print(a.sum())
print(a.mean())
print(a.max())
print(a.min())
print(min(3,4,545,4.4,1.1))
[[0.15872263 0.49702442 0.67841201] [0.37469959 0.31082293 0.65615521]] 2.675836787548347 0.4459727979247245 0.6784120113548243 0.15872263219267346 1.1
默认的,这些一元操作是对整个数组进行计算,没有考虑到数组的形状。你可以设置axis
参数来指定运算方向。axis
表示第n维(从0开始)。
b=np.arange(12).reshape(3,4)
print(b)
print(b.sum(axis=0)) #对第0维的元素求和
print(b.sum(axis=1)) #对第1维的元素求和
print(b.min(axis=1))
print(b.cumsum(axis=1)) #对第1维的元素依次累加求和
[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [12 15 18 21] [ 6 22 38] [0 4 8] [[ 0 1 3 6] [ 4 9 15 22] [ 8 17 27 38]]
NumPy提供了熟知的数学方法,如:sin、cos、exp等。在NumPy中,这些方法被称作广播函数。这些函数会对数组中的每个元素进行计算,返回计算后的数组。
B=np.arange(3)
print(B)
print(np.exp(B))
print(np.sqrt(B))
C=np.array([2,-1,4])
print(np.add(B,C))
print(B+C)
[0 1 2] [1. 2.71828183 7.3890561 ] [0. 1. 1.41421356] [2 0 6] [2 0 6]
一维数组可以被索引、切片和迭代,就和Python中的列表一样。
a=np.arange(10)**3
print(a)
print(a[2])
print(a[2:5])
a[:6:2]=-1000
print(a)
print(a[::-1]) #-1倒序
for i in a:
print(i)
[ 0 1 8 27 64 125 216 343 512 729] 8 [ 8 27 64] [-1000 1 -1000 27 -1000 125 216 343 512 729] [ 729 512 343 216 125 -1000 27 -1000 1 -1000] -1000 1 -1000 27 -1000 125 216 343 512 729
多维数组可以在每一个维度有一个索引,这些索引构成元组来进行访问。
def f(x,y):return 10*x+y
b=np.fromfunction(f,(5,4),dtype=int)
print(b)
print(b[2,3])
print(b[0:5,1])
print(b[:,1])
print(b[1:3,:])
[[ 0 1 2 3] [10 11 12 13] [20 21 22 23] [30 31 32 33] [40 41 42 43]] 23 [ 1 11 21 31 41] [ 1 11 21 31 41] [[10 11 12 13] [20 21 22 23]]
...
表示对索引的省略。如下所示:
c = np.array( [[[ 0, 1, 2], # 三维数组
[ 10, 12, 13]],
[[100,101,102],
[110,112,113]]])
print(c.shape)
print(c[1,...]) # 和 c[1,:,:] 、 c[1]效果相同
print(c[...,2]) # 和c[:,:,2]效果相同
(2, 2, 3) [[100 101 102] [110 112 113]] [[ 2 13] [102 113]]
对多维数组的迭代是在第一维进行迭代的。
for row in b:
print(row)
[0 1 2 3] [10 11 12 13] [20 21 22 23] [30 31 32 33] [40 41 42 43]
如果需要遍历多维数组的所有元素,可以使用flat
这个属性。
for element in b.flat:
print(element)
0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43
a=np.arange(12)**2
i=np.array([1,1,3,8,5])
print(a[i])
j=np.array([[3,4],[8,5]]) #用二维数组来访问数组
print(a[j]) #产生和访问的数组相同形状的结果
[ 1 1 9 64 25] [[ 9 16] [64 25]]
在时间序列的数据上寻找最大值通常会用到数组索引
time=np.linspace(20,145,5)
data=np.sin(np.arange(20)).reshape(5,4)
print(time)
print(data)
ind=data.argmax(axis=0)#返回按照指定轴的方向的最大值的索引
time_max=time[ind]
print(ind)
print(time_max)
data_max=data[ind,range(data.shape[1])]
print(data_max)
[ 20. 51.25 82.5 113.75 145. ] [[ 0. 0.84147098 0.90929743 0.14112001] [-0.7568025 -0.95892427 -0.2794155 0.6569866 ] [ 0.98935825 0.41211849 -0.54402111 -0.99999021] [-0.53657292 0.42016704 0.99060736 0.65028784] [-0.28790332 -0.96139749 -0.75098725 0.14987721]] [2 0 3 1] [ 82.5 20. 113.75 51.25] [0.98935825 0.84147098 0.99060736 0.6569866 ]
你也可以使用数组索引来赋值
a=np.arange(5)
a[[1,3,4]]=0
print(a)
[0 0 2 0 0]
如果赋值时有重复的索引,则赋值会执行多次,留下最后一次执行的结果
a=np.arange(5)
a[[0,0,0]]=[1,2,3] #为0元素索引赋值3次
print(a)
[3 1 2 3 4]
但是赋值时使用+=
时,并不会重复计算
a=np.arange(5)
a[[0,0,0]]+=1
print(a)
[1 1 2 3 4]
这是因为"a+=1"最终是解释成了"a=a+1"
通过使用布尔数组索引,我们可以选择哪些数据是需要的,哪些是不需要的。
在赋值中也非常有用。
a = np.arange(12).reshape(3,4)
b = a > 4
print(b)
print(a[b])
a[b]=10
print(a)
[[False False False False] [False True True True] [ True True True True]] [ 5 6 7 8 9 10 11] [[ 0 1 2 3] [ 4 10 10 10] [10 10 10 10]]
a = np.floor(10*np.random.random((3,4)))
print(a.ravel()) #返回铺平后的数组
print(a.reshape(6,2)) #按照指定的形状更改
print(a.T)#返回转置矩阵
[2. 0. 4. 9. 2. 4. 9. 0. 5. 3. 8. 7.] [[2. 0.] [4. 9.] [2. 4.] [9. 0.] [5. 3.] [8. 7.]] [[2. 2. 5.] [0. 4. 3.] [4. 9. 8.] [9. 0. 7.]]
如果一个维度填的是-1,则该维度的形状会自动进行计算
print(a.reshape(3,-1))
[[2. 0. 4. 9.] [2. 4. 9. 0.] [5. 3. 8. 7.]]
多个数组可以按照不同的轴合在一起
a=np.floor(10*np.random.random((2,2)))
print(a)
b=np.floor(10*np.random.random((2,2)))
print(b)
print(np.vstack((a,b)))#垂直方向堆砌
print(np.hstack((a,b)))#水平方向堆砌
from numpy import newaxis #插入一个维度
print(a[:,newaxis])
[[0. 9.] [9. 7.]] [[5. 2.] [0. 1.]] [[0. 9.] [9. 7.] [5. 2.] [0. 1.]] [[0. 9. 5. 2.] [9. 7. 0. 1.]] [[[0. 9.]] [[9. 7.]]]
使用hsplit
,vsplit
可以对数组按照水平方向和垂直方向进行划分。
a=np.floor(10*np.random.random((2,12)))
print(a)
print(np.hsplit(a,3))
print(np.hsplit(a,(1,2,3)))#在第一列,第二列,第三列进行划分
[[2. 8. 2. 0. 9. 4. 2. 2. 2. 3. 9. 5.] [3. 3. 6. 2. 9. 3. 2. 8. 3. 0. 7. 2.]] [array([[2., 8., 2., 0.], [3., 3., 6., 2.]]), array([[9., 4., 2., 2.], [9., 3., 2., 8.]]), array([[2., 3., 9., 5.], [3., 0., 7., 2.]])] [array([[2.], [3.]]), array([[8.], [3.]]), array([[2.], [6.]]), array([[0., 9., 4., 2., 2., 2., 3., 9., 5.], [2., 9., 3., 2., 8., 3., 0., 7., 2.]])]
当操作数组时,数组的数据有时会复制到新数组中,有时又不会。这通常令初学者感到困难。总的来说有下面三种情况:
简单的赋值不会复制数组的数据。
a=np.arange(12)
b=a
print(b is a)
b.shape=3,4
print(a.shape)
True (3, 4)
不同的数组可以使用同一份数据,view
函数在同一份数据上创建了新的数组对象。
c=a.view()
print(c is a)
print(c.base is a) #c是a的数据的视图
print(c.flags.owndata)
c.shape=6,2
print(a.shape) #a的形状没有改变
c[4,1]=1234 #a的数据改变了
print(a)
False True False (3, 4) [[ 0 1 2 3] [ 4 5 6 7] [ 8 1234 10 11]]
对数组切片会返回数组的视图
s=a[:,1:3]
s[:]=10
print(a)
[[ 0 10 10 3] [ 4 10 10 7] [ 8 10 10 11]]
copy
函数实现了对数据和数组的完全复制。
d=a.copy()
print(d is a)
print(d.base is a)
d[0,0]=9999
print(a)
False False [[ 0 10 10 3] [ 4 10 10 7] [ 8 10 10 11]]
import numpy as np
a = np.array([[1.0, 2.0], [3.0, 4.0]])
print(a)
a.transpose() #转置
np.linalg.inv(a) #矩阵的逆矩阵
u = np.eye(2) # unit 2x2 matrix; "eye" represents "I" 单位矩阵
j = np.array([[0.0, -1.0], [1.0, 0.0]])
np.dot (j, j) # 点积
np.trace(u) # 矩阵的迹
y = np.array([[5.], [7.]])
print(np.linalg.solve(a, y))#解线性方程组
print(np.linalg.eig(j))#计算特征值
[[1. 2.] [3. 4.]] [[-3.] [ 4.]] (array([0.+1.j, 0.-1.j]), array([[0.70710678+0.j , 0.70710678-0.j ], [0. -0.70710678j, 0. +0.70710678j]]))
a = np.arange(30)
a.shape = 2,-1,3 # -1 表示自动计算大小
print(a.shape)
(2, 5, 3)