文章目录
本文主要是学习知乎锦恢的专栏Kirigaya的Python实验室中的numpy教程做记录
(一)ndarray对象的基本操作
引用Numpy库
大家一般都会使用别名“np”来调用numpy:
import numpy as np
1. ndarray对象
ndarray
是np处理数据的基本对象,实际上可以理解为一个多维数组(或者看成一个n维张量)。它由两部分构成:
- 实际的数据
- 描述这些数据的元数据(数据维度、数据类型等)
NumPy数组类是ndarray
,它的别名也被叫做数组,但numpy.array
与标准Python库中的数组类array.array
是不相同的,标准Python库中的数组类通常只处理一维数组,并且提供了很少的功能。Ndarray
对象有以下重要的属性:
ndarray对象的属性
import numpy as np
a = np.array([1, 2, 3, 4, 5]) # 创建ndarray后面会讲
b = np.array([[1], [2], [3], [4], [5]])
c = np.array([[1, 2], [3, 4]])
print(a.ndim) # 1
print(b.ndim) # 2
print(c.ndim) # 2
print(a.shape) # (5,)
print(b.shape) # (5, 1)
print(c.shape) # (2, 2)
print(a.size) # 5
print(b.size) # 5
print(c.size) # 4
print(a.dtype) # int32
print(b.dtype) # int32
print(c.dtype) # int32
print(a.itemsize) # 4 以字节的形式返回数组中每一个元素的大小。
print(b.itemsize) # 4 32/87=4
print(c.itemsize) # 4
ndarray元素数据类型
2. 创建ndarray对象
方法一:从组合数据类型中创建
我们只要把列表或者元组这样的python自带的组合数据类型当作np.array()
的参数,那么np.array()
就会返回一个ndarray
对象。即:
a = np.array(list)
b = np.array(tuple)
只要列表和元组同型,那么列表和元组就可以混合使用。
np.array()
还有一个参数用来指定数据类型:
a = np.array(list,dtype=np.int32)
b = np.array(tuple,dtype=np.float32)
当np.array()
的dtype
参数为空时,np会根据数据情况自动关联一个dtype
类型。
>>> a = [1,2,3]
>>> b = np.array(a)
>>> b #ndarray会以array()的形式展现数据
array([1, 2, 3])
>>> a = [[1,2],[3,4]]
>>> b = np.array(a)
>>> b #ndarray在展现数据时会体现数据的维度
array([[1, 2],
[3, 4]])
方法二:使用np函数创建
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.ones((3,6)) #np.ones()的元素默认float64
array([[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]])
>>> np.ones((3,6),dtype=np.int32) #生成函数中也可以配置dtype参数
array([[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1]])
>>> np.zeros((3,6))
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
>>> np.eye(5)
array([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.]])
>>> a = np.ones((2,3,4))
>>> print(a) #用print输出没有了array的前缀
[[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]]
>>> a.shape
(2, 3, 4)
>>> f = np.full((3, 3), 5)
>>> print(f)
[[5 5 5]
[5 5 5]
[5 5 5]]
np还有一些根据已有的ndarray
类型创建新的ndarray
对象的函数:
>>> a = np.array([[1, 2], [3, 4]])
>>> np.ones_like(a)
array([[1, 1],
[1, 1]])
>>> np.full_like(a,2233)
array([[2233, 2233],
[2233, 2233]])
np.linspace()
np.linspace(start, stop, num=50, endpoint=True,
retstep=False, dtype=None, axis=0)
- start:返回样本数据开始点
- stop:返回样本数据结束点
- num:生成的样本数据量,默认为50
- endpoint:布尔型参数。True则包含stop;False则不包含stop
- retstep:布尔型参数。如果为True则结果会给出数据间隔
- dtype:输出数组类型
- axis:0(默认)或-1
>>> np.linspace(2.0, 3.0, num=5)
array([ 2. , 2.25, 2.5 , 2.75, 3. ])
>>> np.linspace(2.0, 3.0, num=5, endpoint=False)
array([ 2. , 2.2, 2.4, 2.6, 2.8])
>>> np.linspace(2.0, 3.0, num=5, retstep=True)
(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25
np.concatenate()
np.concatenate()
还有一个axis
参数,表示合并地方向,默认为0,表示按第一个维度地方向合并。以矩阵为例,列方向为axis=0
,也就是0轴的方向,行方向是1轴的方向。为什么这么规定呢?我们可以认为矩阵是列向量的衍生,而列向量不是只有列吗?所以列向量只有列方向,我们把列向量仅有的方向规定为0轴也就很自然了。当列向量延伸到矩阵时我们保留了0轴的规定,而多出来的行方向我们就用0+1,也就是1轴来规定。
>>> a = np.ones((2,2))
>>> b = np.zeros((2,2))
>>> np.concatenate((a,b)) # axis默认为0
array([[1., 1.],
[1., 1.],
[0., 0.],
[0., 0.]])
>>> np.concatenate((a,b),axis=0) # 按列合并
array([[1., 1.],
[1., 1.],
[0., 0.],
[0., 0.]])
>>> np.concatenate((a,b),axis=1) # 按行合并
array([[1., 1., 0., 0.],
[1., 1., 0., 0.]])
3. 维度变换函数
np提供了一些进行维度变换的函数,不过它们都是以ndarray
对象的方法的形式使用的。使用这些方法可以快速地对我们的数据在维度组织形式上进行塑形。让数据变成我们希望的“形状”。
>>> a = np.array([1,2,3,4,5,6,7,8,9])
>>> a.reshape((3,3))
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> a
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> a.resize((3,3))
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> a.swapaxes(0,1) #交换0轴和1轴方向的维度
array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> a.flatten()
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
4. 数据类型转换函数
前面提到过ndarray对象的数据部分是有数据类型的。np提供了一个用来对已经存在的ndarray
对象进行数据类型转换的函数:new_a = a.astype(new_type)
。它会根据new_type
返回一个新的数组(ndarray
对象)
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> a.dtype
dtype('int32')
>>> b = a.astype(np.float32) #.astype()方法不改变原数组
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> b
array([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]], dtype=float32)
我们可以把列表通过np.array()
转换成ndarray
对象,那反过来怎么把处理好的ndarray
对象重新变回列表呢,直接给出函数:ls = a.tolist()
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> a.tolist()
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
(二)数据的索引和切片
Python对数据的索引和切片是非常重要的特征之一,np也继承了传统Python对组合数据类型的切片与索引操作。方法大同小异。
1. 一维数组索引与切片
>>> a = np.array([9,8,7,6,5])
>>> a[2] # 索引,直接可以通过下标索引
7
>>> a[1 : 4 : 2] # 切片,和列表一样,格式为ls[起始编号 : 终止编号(不含) : 步长]
array([8, 6])
2. 多维数组索引与切片
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> a[2,2] # 多维支持a[ax1,ax2,...,axn]的索引,这种方法比较常用
9
>>> a[2][2] # 也支持传统列表a[ax1][ax2]...[axn]的索引方式,不常用
9
>>> a[:2,:2] # 把每个坐标替换成切片操作符就可以分别对每一个维度进行切片了
array([[1, 2],
[4, 5]])
>>> a[::2,::2] # 切片,和列表一样,格式为ls[起始编号 : 终止编号(不含) : 步长]
array([[1, 3],
[7, 9]])
>>> a[::-1,::-1]
array([[9, 8, 7],
[6, 5, 4],
[3, 2, 1]])
(三)数组运算
1. 标量运算
用标量(实数)去和ndarray
对象做加减乘除就是用标量去和数组中的每一个元素做加减乘除。这实际上是ndarray
广播运算机制的一种体现。
>>> a = np.arange(24).reshape((2,3,4))
>>> a
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]]])
>>> a + 1000
array([[[1000, 1001, 1002, 1003],
[1004, 1005, 1006, 1007],
[1008, 1009, 1010, 1011]],
[[1012, 1013, 1014, 1015],
[1016, 1017, 1018, 1019],
[1020, 1021, 1022, 1023]]])
>>> a * 3.14
array([[[ 0. , 3.14, 6.28, 9.42],
[12.56, 15.7 , 18.84, 21.98],
[25.12, 28.26, 31.4 , 34.54]],
[[37.68, 40.82, 43.96, 47.1 ],
[50.24, 53.38, 56.52, 59.66],
[62.8 , 65.94, 69.08, 72.22]]])
>>> a.mean()
11.5
>>> a / a.mean()
array([[[0. , 0.08695652, 0.17391304, 0.26086957],
[0.34782609, 0.43478261, 0.52173913, 0.60869565],
[0.69565217, 0.7826087 , 0.86956522, 0.95652174]],
[[1.04347826, 1.13043478, 1.2173913 , 1.30434783],
[1.39130435, 1.47826087, 1.56521739, 1.65217391],
[1.73913043, 1.82608696, 1.91304348, 2. ]]])
2. 一元函数
np提供了一些一元函数来对数组中的每一个元素进行计算:(操作对象:单个ndarray
对象)
3. 二元函数
np提供了一些函数与操作符来对两个ndarray
对象进行操作:
>>> a = np.arange(16).reshape((4,4))
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> b = np.sqrt(a)
>>> b
array([[0. , 1. , 1.41421356, 1.73205081],
[2. , 2.23606798, 2.44948974, 2.64575131],
[2.82842712, 3. , 3.16227766, 3.31662479],
[3.46410162, 3.60555128, 3.74165739, 3.87298335]])
>>> np.maximum(a,b)
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.]])
>>> a > b
array([[False, False, True, True],
[ True, True, True, True],
[ True, True, True, True],
[ True, True, True, True]])
4. 线性代数库
np提供了线性代数函数库 linalg,该库包含了线性代数所需的几乎所有功能
(四)随机数
1. 基础np随机数生成函数
numpy
有一个random
子库,用法与python原本的random库差不多,调用时记得加上random:np.random.<方法>
>>> np.random.rand(3,3) #生成一个3*3的随机数矩阵
array([[0.28034402, 0.7632137 , 0.77616993],
[0.67435242, 0.17635725, 0.48415525],
[0.6530347 , 0.33255404, 0.44074308]])
>>> np.random.randn(3,3) #矩阵元素符合正态分布
array([[ 0.19233304, 0.85012517, 1.17034056],
[-0.30448713, -2.2180847 , -0.09353092],
[ 0.5803468 , 0.59346808, -0.60543587]])
>>> np.random.randint(0,10,(3,3)) #每个元素都是范围在[0,10)的随机整数
array([[0, 1, 3],
[7, 5, 5],
[0, 9, 1]])
>>> np.random.seed(10) #通过设定同一个随机数种子,可以使得生成的随机数完全相同
>>> np.random.randint(100,200,(3,4))
array([[109, 115, 164, 128],
[189, 193, 129, 108],
[173, 100, 140, 136]])
>>> np.random.randint(100,200,(3,4))
array([[116, 111, 154, 188],
[162, 133, 172, 178],
[149, 151, 154, 177]])
>>> np.random.seed(10)
>>> np.random.randint(100,200,(3,4))
array([[109, 115, 164, 128],
[189, 193, 129, 108],
[173, 100, 140, 136]])
2. np的高级随机数函数
>>> a = np.random.randint(100,200,(3,4))
>>> a
array([[116, 111, 154, 188],
[162, 133, 172, 178],
[149, 151, 154, 177]])
>>> np.random.shuffle(a) #只对a的第一个轴(0轴,即axis=0)洗牌
>>> a
array([[116, 111, 154, 188],
[149, 151, 154, 177],
[162, 133, 172, 178]])
>>> np.random.shuffle(a) #我们洗了两次牌,可以看到只有列方向的三个元素之间交换了元素
>>> a
array([[162, 133, 172, 178],
[116, 111, 154, 188],
[149, 151, 154, 177]])
3. 概率分布函数
>>> a = np.random.uniform(0,10,(3,4))
>>> a
array([[3.7334076 , 6.74133615, 4.41833174, 4.34013993],
[6.17766978, 5.13138243, 6.50397182, 6.01038953],
[8.05223197, 5.21647152, 9.08648881, 3.19236089]])
>>> a = np.random.normal(10,5,(3,4))
>>> a
array([[ 8.66341406, 7.25345493, 10.66354148, 7.61928993],
[16.5423654 , 10.9750664 , 12.00104994, 8.31183831],
[16.28236132, 6.34015249, 13.30115776, 8.24564054]])
(五)统计函数与梯度函数
1. 统计函数
以下几乎所有涉及的函数都有其对应的方法版,比如:
a = np.array([1,2,3])
print(max(a))
print(a.max())
以矩阵为例,列的方向就是axis=0
(0轴,也就是所谓的第一个轴)的方向。那么以axis=0
的方向对矩阵使用max就是求矩阵每个列向量中元素的最大值:
>>> a = np.arange(1,10,1).reshape((3,3))
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> np.max(a) # 全局最大值
9
>>> np.max(a,axis=0) # 0轴方向,也就是1,4,7方向
array([7, 8, 9])
>>> np.max(a,axis=1) # 1轴方向,也就是1,2,3方向
array([3, 6, 9])
>>> a = np.random.randint(1,100,(3,3))
>>> a
array([[93, 97, 63],
[58, 80, 43],
[58, 98, 51]])
>>> np.sum(a)
641
>>> a.sum() # 结果方法版我还是展示了
641
>>> np.sum(a,axis=0)
array([209, 275, 157])
>>> np.sum(a,axis=1)
array([253, 181, 207])
>>> np.mean(a,axis=0)
array([69.66666667, 91.66666667, 52.33333333])
>>> np.mean(a,axis=1)
array([84.33333333, 60.33333333, 69. ])
>>> np.average(a,axis=0,weights=[10,5,1])
array([79.875, 91.75 , 56. ]) # 79.875 = (10 * 93 + 5 * 58 + 1 * 58) / (10 + 5 + 1)
>>> np.std(a)
19.87615979999813
>>> np.var(a)
395.0617283950617
>>> a
array([[93, 97, 63],
[58, 80, 43],
[58, 98, 51]])
>>> np.max(a)
98
>>> np.argmax(a) # 扁平化的下标
7
>>> np.argmax(a,axis=0)
array([0, 2, 0], dtype=int64)
>>> np.ptp(a)
55
>>> np.ptp(a,axis=0) # 机器学习的样本预处理经常要对axis=0的元素求ptp
array([35, 18, 20])
>>> np.median(a)
63.0
2. 梯度函数
>>> a = np.random.randint(0,20,(5,))
>>> a
array([13, 8, 9, 5, 0])
>>> np.gradient(a) #中间的第i个元素的梯度=(a[i + 1] - a[i - 1]) / 2,最后一个元素a[n]的梯度=(a[n] - a[n - 1]) / 1,第一个元素a[0]的梯度=(a[1] - a[0]) / 1
array([-5. , -2. , -1.5, -4.5, -5. ])
>>> a = np.random.randint(0,20,(5,)) # 再给一组感觉感觉
>>> a
array([18, 3, 16, 18, 16])
>>> np.gradient(a)
array([-15. , -1. , 7.5, 0. , -2. ])
>>> b = np.random.randint(0,50,(3,5))
>>> b
array([[14, 15, 17, 25, 46],
[48, 42, 17, 32, 17],
[41, 16, 41, 26, 12]])
>>> np.gradient(b)
[array([[ 34. , 27. , 0. , 7. , -29. ], # 第一个维度axis=0方向的梯度(列方向)
[ 13.5, 0.5, 12. , 0.5, -17. ],
[ -7. , -26. , 24. , -6. , -5. ]]),
array([[ 1. , 1.5, 5. , 14.5, 21. ], # 第二个维度axis=1方向的梯度(行方向)
[ -6. , -15.5, -5. , 0. , -15. ],
[-25. , 0. , 5. , -14.5, -14. ]])]