一、什么是Numpy
简单的说,Numpy 是 Python 的一个科学计算包,包含了多维数组以及多维数组的操作。
Numpy 的核心是 ndarray 对象,这个对象封装了同质数据类型的n维数组。起名 ndarray 的原因就是因为是 n-dimension-array 的简写。
1.1 ndarray
Numpy 中最重要的一个对象就是 ndarray。
ndarray中的每个元素在内存中使用相同大小的块。 ndarray中的每个元素是数据类型对象的对象(称为 dtype)。
构建ndarray
#导入numpy包
import numpy as np
np.array() 就相当于我们学习过的list,int,str等等,将其他的数据类型转换为ndarray
a= [[1,2,3,4],[4,5,6,7]]
a1 = np.array(a)
a1
>>array([[1, 2, 3, 4],
[4, 5, 6, 7]])
1.2 数据类型
Numpy 中的数组比 Python 原生中的数组(只支持整数类型与浮点类型)强大的一点就是它支持更多的数据类型。
Numpy 常见的基本数据类型如下:
数据类型 | 描述 |
---|---|
bool_ | 布尔(True或False),存储为一个字节 |
int_ | 默认整数类型(与C long相同;通常为int64或int32) |
intc | 与C int(通常为int32或int64)相同 |
intp | 用于索引的整数(与C ssize_t相同;通常为int32或int64) |
int8 | 字节(-128到127) |
int16 | 整数(-32768到32767) |
int32 | 整数(-2147483648至2147483647) |
int64 | 整数(-9223372036854775808至9223372036854775807) |
uint8 | 无符号整数(0到255) |
uint16 | 无符号整数(0到65535) |
uint32 | 无符号整数(0至4294967295) |
uint64 | 无符号整数(0至18446744073709551615) |
float_ | float64的简写。 |
float16 | 半精度浮点:符号位,5位指数,10位尾数 |
float32 | 单精度浮点:符号位,8位指数,23位尾数 |
float64 | 双精度浮点:符号位,11位指数,52位尾数 |
object | Python对象,放什么都可以 |
重点:ndarry里面只能装一种数据类型
需要注意的是,不同于 Python 列表,NumPy 要求数组必须包含同一类型的数据。如果类型不匹配,NumPy 将会向上转换(如果可行)。
那么什么叫做向上转换呢?
b = np.array([3.14, 4, 2, 3])
b
>>array([3.14, 4. , 2. , 3. ])
1、数据中存在整数型和浮点型数据,最终转换结果为都变成了浮点型数据
np.array(['3',5,4.2])
>>array(['3', '5', '4.2'], dtype='<U3')
2、数据中有整数型、浮点型和字符串,最终都转换成了字符串类型
指定数据类型,采用dtype参数:
a = np.array([3.14, 4, 2, 4], dtype='int64')
a
>>array([3, 4, 2, 4], dtype=int64)
上面例子就将浮点类型数据强制转换成了整数类型
查看ndarray数据类型的方法: 用dtype:
a.dtype
>>dtype('int64')
转换数据类型的方法: 用astype:
a.astype(np.float32)
>>array([3., 4., 2., 4.], dtype=float32)
Numpy 的矢量化(向量化)功能
如果想要将一个数组 a 的每个元素与长度相同的另外一个数组 b 中相应位置的元素相乘,在numpy中如何利用矢量化实现上述功能
a = [[1, 2, 3], [5, 7, 8], [4, 5, 6]]
b = [[6, 2, 1], [2, 3, 1], [4, 5, 6]]
a1 = np.array(a)
b1 = np.array(b)
a1*b1
>>array([[ 6, 4, 3],
[10, 21, 8],
[16, 25, 36]])
矢量化代码有很多优点,其中包括:
- 矢量化代码更简洁易读
- 更少的代码行通常意味着更少的错误
- 该代码更接近地类似于标准数学符号(使得更容易,通常,以正确地编码数学构造)
- 矢量化导致更多的“Pythonic”代码。如果没有向量化,我们的代码将会效率很低,难以读取for循环。
二、创建常用的数组
单位矩阵,全零矩阵,三角矩阵等常用的矩阵数组,在线性代数的辅助计算中有很多特殊的作用
下面我们来看一下如何创建这些矩阵数组。
#全部行都能输出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
2.1 创建一个全0数组
参数说明:
shape 形状,要创建数组的形状
dtype 数组的类型
np.zeros(10) # float型
np.zeros(10, dtype="int32")
np.zeros((3, 3), dtype="int32") #二维数组
>>array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
#三维数组
np.zeros((4,3,2),dtype='int')
#这里的4代表有4个二维平面构成的三维体
#3代表每一个二维元素里面都是由3个一维元素构成的
#2代表每一个一维元素里面都是由2个0维元素构成的
2.2 全1数组
数组中所有元素均为1
np.ones([4,3])
>>array([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
2.3 单位矩阵
矩阵对角线元素为1,剩余元素均为0
np.eye(3, 3)
>>array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
#向上偏移一行
np.eye(5,k=1)
>>array([[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.],
[0., 0., 0., 0., 0.]])
#向下偏移一行
np.eye(5,k=-1)
>>array([[0., 0., 0., 0., 0.],
[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.]])
2.4 对角矩阵
d = np.diag(np.array([1, 2, 3, 4]))
d
>>array([[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])
2.5 设定具体的值
np.full((3, 5),3.14 )#三行五列
>>array([[ 3.14, 3.14, 3.14, 3.14, 3.14],
[ 3.14, 3.14, 3.14, 3.14, 3.14],
[ 3.14, 3.14, 3.14, 3.14, 3.14]])
设置空值和无穷大
有一点要尤其注意的是: np.nan, np.inf 都是浮点类型
结合ndarray中的数据类型必须是相同的, 也就是说数组中如何有一个数据是空值,那么所有的数据都只能是浮点型.
np.nan#空(啥也没有)
np.inf#无穷大
type(np.inf)
>>float
2.6 随机数组
上面提到的都是有固定值的数组及矩阵,下面的是自动生生的随机数数组,也就是说数组的值不会固定不变,意味着每次运行结果都会不同
#生成1-10的三行六列的数组
np.random.randint(1, 10, (3, 6))
>>array([[5, 1, 8, 8, 9, 9],
[7, 7, 9, 5, 6, 5],
[6, 1, 5, 5, 5, 6]])
#生成0-1的随机数
a = np.random.rand(2, 3) # uniform in [0, 1]
a
>>array([[0.05254766, 0.59756243, 0.99502083],
[0.4723183 , 0.72458214, 0.85551448]])
#四舍五入到小数点后三位(利用round函数)
np.round(np.random.random((3,4)),3)
>>array([[0.147, 0.727, 0.522, 0.178],
[0.53 , 0.207, 0.797, 0.162],
[0.82 , 0.288, 0.032, 0.134]])
#生成5-12的随机序列
#先放大7倍,再向右偏移5个单位
7*np.random.random((3,4))+5
>>array([[11.46104262, 7.41313746, 8.1893867 , 7.83397531],
[ 7.89122632, 8.69092695, 11.22702728, 5.85397128],
[ 9.9093688 , 10.82050394, 9.74349257, 10.05593118]])
2.6.1 正态分布随机数
正态分布是统计学中常用的一种概率分布
#标准正态分布(均值为0,方差为1)
np.random.rand(3,2)
>>array([[0.36129005, 0.82424353],
[0.61575358, 0.52226236],
[0.74051091, 0.11037443]])
#均值为4,标准差为2的普通正态分布
array = np.random.normal(4, 2, (10, 3)) # 均值, 标准差, 形状
array
>>array([[ 5.95973621, 7.19262672, 2.98362756],
[ 4.78153143, 1.78440733, 5.8766825 ],
[ 5.43184024, 3.23992211, 3.71794129],
[ 5.20221566, 1.84128205, 1.94329058],
[ 2.75472565, 2.58743767, -3.26293124],
[ 4.50987806, 5.15885142, 1.11565559],
[ 2.19926954, 3.24967032, 0.22541738],
[ 3.37805791, 4.37907265, 2.40445887],
[ 6.53946577, 5.14046091, 5.23462548],
[ 1.0062933 , 2.51757328, 4.7654928 ]])
2.6.2 其他分布
#卡方分布,自由度是5
np.random.chisquare(5,(3,4))
>>array([[ 5.01762679, 1.8848837 , 3.11996202, 5.74481173],
[ 6.63079251, 4.03316576, 3.16723971, 1.80446791],
[11.0604356 , 10.64043017, 4.8995559 , 7.71885382]])
#t分布,自由度3
np.random.standard_t(3,(3,4))
>>array([[-0.360586 , 1.55431396, 1.06826751, -0.24576188],
[-3.15302156, -2.55289713, -0.95298275, 0.11738487],
[-1.78035877, -1.02519345, 0.09922377, -0.87289364]])
#f分布,分子自由度2,分母自由度3
np.random.f(2,3,(3,4))
>>array([[8.65117009e-01, 1.66171994e+00, 4.94743004e+00, 4.17935167e-01],
[2.33295805e-01, 5.39910799e+00, 1.14388224e-01, 6.95415742e-01],
[1.13709537e+00, 2.17429504e+00, 1.63103939e-02, 1.73043383e+01]])
#泊松分布
np.random.poisson(0.5,(3,4))
>>array([[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1]])
#二项分布
np.random.binomial(10,0.5,(3,4))
>>array([[2, 4, 5, 9],
[9, 9, 3, 6],
[5, 4, 7, 4]])
2.6.3 设置随机数种子
上面知道生成的随机数组具有很强的不确定性,每次结果都会不同,所谓随机数种子就是设置一个固定种子值,那么每次生成的随机数组都会是完全相同的,这个数值可任意设定。
# 设置随机数种子
np.random.seed(200)
c = np.random.randn(2, 3)
c
>>array([[-1.45094825, 1.91095313, 0.71187915],
[-0.24773829, 0.36146623, -0.03294967]])
2.7 创建一个线性序列的数组
有以下两种方法:
arange([start,] stop[, step,])
如果只填一个stop参数,默认从0开始,步长为1
特点: 可以设置开始位置,终止位置和步长,但产生数字的数量不方便控制
#如果只填一个stop参数,默认从0开始,步长为1
np.arange(0, 20, 4)
>>array([ 0, 4, 8, 12, 16])
np.linspace(开始位置, 终止位置, 产生数量)
特点: 可以设置开始位置和终止位置以及产生数量,但不方便控制步长
np.linspace(1, 101, 49, dtype=int)
>>array([ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 26,
28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 51, 53,
55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 76, 78, 80,
82, 84, 86, 88, 90, 92, 94, 96, 98, 101])
三、 ndaray常用属性
- 查看数据形状
x.shape - 查看维度
x.ndim - 查看数组元素个数
x.size
3.1 数组的索引和切片
3.1.1 单个元素索引
数组的单元素索引是人们期望的。它的工作原理与其他标准Python序列一样。它是从0开始的,并且接受负索引来从数组的结尾进行索引。
x = np.arange(10)
x
>>array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x[2]
x[0]
>>2
0
3.1.2 高维数组索引
与Python原生的列表、元组不同的是,Numpy数组支持多维数组的多维索引。
每一个逗号, 代表索引的一个维度
x2
>>array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2[第一个维度,第二个维度]
#从外往里看,第一个维度是3,7,1,第二个维度是3,5,2,4
x2[行,列]
x2[0]
x2[0, 0]
x2[1, 1]#第一行第一列
>>array([3, 5, 2, 4])
3
6
3.1.3 修改某一个元素值
利用索引来实现,直接赋值
x2[:2,:2] = 100
x2
>>array([[100, 100, 2, 4],
[100, 100, 8, 8],
[ 1, 6, 7, 7]])
3.2 切片
可以使用切片和步长来截取不同长度的数组,使用方式与Python原生的对列表和元组的方式相同。
语法和之前学过的列表的切片是一样的
x[start:stop:step]
x2
>>array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
#x2[第一维度,第二维度 ]
x2[:2:1, 0:2:1]
>>array([[3, 5],
[7, 6]])
#获取第1列
x2[:, 1]
>>array([5, 6, 6])
3.3 关于视图
关于数组切片有一点很重要也非常有用,那就是数组切片返回的是
数组数据的视图,而不是数值数据的副本。这一点也是 NumPy 数
组切片和 Python 列表切片的不同之处:在 Python 列表中,切片是
值的副本。例如此前示例中的那个二维数组:
x2
>>array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2_0 = x2[:2, :2]
x2_0
>>array([[3, 5],
[7, 6]])
x2_0[0, 0] = 8888
x2_0
>>array([[8888, 5],
[ 7, 6]])
x2
>>array([[8888, 5],
[ 7, 6]])
也就是说修改某个元素值不仅会将切片内的元素修改,原来的数组也会改变。
若想不改变原数组就要用到创建副本。
改为创建副本
也可以通过.copy()方法创建一个副本,这样就不会对原数组进行改动。
x2_1 = x2[:2, :2].copy()#副本(深复制)
x2_1
>>array([[4, 3],
[8, 4]])
x2_1[0, 0] = 8888
x2_1
>>array([[8888, 3],
[ 8, 4]])
x2
>>array([[4, 3, 4, 4],
[8, 4, 3, 7],
[5, 5, 0, 1]])
3.4 数组的变形
数组的变形也是一类非常有用的操作。
数组变形最灵活的实现方式是通过 reshape() 函数来实现。例如,如果你希望将数字 1~9 放入一个3×3 的矩阵中,可以采用如下方法:
3.4.1 reshape改变数组的形状
x2
>>array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2.reshape((2, 6))
>>array([[3, 5, 2, 4, 7, 6],
[8, 8, 1, 6, 7, 7]])
3.4.2 拉伸成一维数组(数组的平铺)
x2
>>array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2.ravel()
>>array([3, 5, 2, 4, 7, 6, 8, 8, 1, 6, 7, 7])
3.4.3 转置
x2
>>array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2.T
>>array([[3, 7, 1],
[5, 6, 6],
[2, 8, 7],
[4, 8, 7]])
无论是ravel、reshape、T,它们都不会更改原有的数组形状,都是返回一个新的数组。
3.5 数组的拼接
拼接或连接 NumPy 中的两个数组主要由np.concatenate实现
x = np.array([34, 43, 12])
y = np.array([343, 34, 676])
#拼接(一维的拼接)
np.concatenate((x, y))
>>array([ 34, 43, 12, 343, 34, 676])
x2
>>array([[1, 2, 3],
[3, 2, 1],
[0, 0, 0]])
x3
>>array([[1, 2, 3],
[3, 2, 1],
[0, 0, 0]])
#二维数组的横向拼接
np.concatenate([x2, x3], axis=1)
>>array([[1, 2, 3, 1, 2, 3],
[3, 2, 1, 3, 2, 1],
[0, 0, 0, 0, 0, 0]])
axis是轴的意思,axis=0,-2沿着竖方向拼接,axis=1,-1沿着横方向拼接
3.6 数据的分裂
将一个数组分成几个较小的数组
既然可以将多个数组进行对堆叠,自然也可以将一个数组拆分成多个小数组。
使用split,可以指定均匀切割成几份,也可以指定沿着哪个位置进行切割, 还可以指定沿着哪个轴进行切割.
np.split(ary, indices_or_sections, axis=0)
#sectins:均匀切分成几份
#indices:手动指定切分点
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]])
np.split(a,2,axis=0)#在索引值为2前面切割
>>[array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]]), array([[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23]])]
#不均匀切分(在指定列前面索引)
np.split(a, [1, 3])
>>[array([[0, 1, 2, 3, 4, 5]]), array([[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]]), array([[18, 19, 20, 21, 22, 23]])]
3.7 广播机制
3.7.1 什么是广播
我们都知道,Numpy中的基本运算(加、减、乘、除、求余等等)都是元素级别的,但是这仅仅局限于两个数组的形状相同的情况下。
可是大家又会发现,如果让一个数组加1的话,结果是整个数组的结果都会加1,这是什么情况呢?
广播broadcast:当把一个低维和一个高维数据进行计算时,会自动将低维数据拉伸到和高维相同的形状,然后进行对应元素位置操作
其实这就是广播机制:Numpy 可以转换这些形状不同的数组,使它们都具有相同的大小,然后再对它们进行运算。给出广播示意图:
x
>>array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
x + 1
>>array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
3.7.2 广播示例
a
>>array([ 0, 10, 20, 30])
b
>>array([[0],
[1],
[2]])
a + b
>>array([[ 0, 10, 20, 30],
[ 1, 11, 21, 31],
[ 2, 12, 22, 32]])
四、Numpy运算
Numpy 中数组上的算术运算符使用元素级别。最后的结果使用新的一个数组来返回。
4.1 基本运算操作
a
b
>>array([20, 30, 40, 50])
array([0, 1, 2, 3])
c = a-b
c
>>array([20, 29, 38, 47])
b**2
>>array([0, 1, 4, 9])
需要注意的是,乘法运算符*的运算在NumPy数组中也是元素级别的(这与许多矩阵语言不同)。如果想要执行矩阵乘积,可以使用dot函数:
A
B
>>array([[1, 1],
[0, 1]])
array([[2, 0],
[3, 4]])
A.dot(B)
>>array([[5, 4],
[3, 4]])
B.dot(A)
>>array([[2, 2],
[3, 7]])
np.dot(A, B)
>>array([[5, 4],
[3, 4]])
np.dot(B,A)
>>array([[2, 2],
[3, 7]])
A*B 元素乘积(elementwise product)
A.dot(B) 矩阵相乘(matrix product)(A左乘B)
np.dot(A, B) 矩阵相乘的另一种方式(another matrix product)(函数形式)
4.2 常用函数
数学运算函数 | |
---|---|
add(x1,x2 [,out]) | 按元素添加参数,等效于 x1 + x2 |
subtract(x1,x2 [,out]) | 按元素方式减去参数,等效于x1 - x2 |
multiply(x1,x2 [,out]) | 逐元素乘法参数,等效于x1 * x2 |
divide(x1,x2 [,out]) | 逐元素除以参数,等效于x1 / x2 |
exp(x [,out]) | 计算输入数组中所有元素的指数。 |
exp2(x [,out]) | 对于输入数组中的所有p,计算2 * p*。 |
log(x [,out]) | 自然对数,逐元素。 |
log2(x [,out]) | x的基础2对数。 |
log10(x [,out]) | 以元素为单位返回输入数组的基数10的对数。 |
expm1(x [,out]) | 对数组中的所有元素计算exp(x) - 1 |
log1p(x [,out]) | 返回一个加自然对数的输入数组,元素。 |
sqrt(x [,out]) | 按元素方式返回数组的正平方根。 |
square(x [,out]) | 返回输入的元素平方。 |
sin(x [,out]) | 三角正弦。 |
cos(x [,out]) | 元素余弦。 |
tan(x [,out]) | 逐元素计算切线。 |
4.3 规约函数(用于统计数据的函数)
下面所有的函数都支持axis来指定不同的轴,用法都是类似的。
函数名称 | 函数功能 |
---|---|
ndarray.sum([axis,dtype,out,keepdims]) | 返回给定轴上的数组元素的总和。 |
ndarray.cumsum([axis,dtype,out]) | 返回沿给定轴的元素的累积和。 |
ndarray.mean([axis,dtype,out,keepdims]) | 返回沿给定轴的数组元素的平均值。 |
ndarray.var([axis,dtype,out,ddof,keepdims]) | 沿给定轴返回数组元素的方差。 |
ndarray.std([axis,dtype,out,ddof,keepdims]) | 返回给定轴上的数组元素的标准偏差。 |
ndarray.argmax([axis,out]) | 沿着给定轴的最大值的返回索引。 |
ndarray.min([axis,out,keepdims]) | 沿给定轴返回最小值。 |
ndarray.argmin([axis,out]) | 沿着给定轴的最小值的返回索引。 |