NumPy(Numerical Python)是Python的一种开源的数值计算扩展,可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。
NumPy 通常与 SciPy(Scientific Python)和 Matplotlib(绘图库)一起使用, 这种组合广泛用于替代 MatLab,是一个强大的科学计算环境,有助于我们通过 Python 学习数据科学或者机器学习。
NumPy 是一个运行速度非常快的数学库,主要用于数组计算,包含:
Numpy 提供了两类基本对象:多维数组对象 ndarray (n-dimentional array object) 和 特殊函数对象ufunc (universal function object) 。本文以 Python3.7 版本为运行环境,主要探讨 Numpy 基本用法。
安装:直接用 pip 命令即可,下面两行代码分别安装 Numpy 和 Matplotlib
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib
导入:
import numpy as np
我们将numpy缩短为np是为了节省时间,同时保持代码的标准化
Numpy 的 Ndarray 对象为数组的处理提供了强大的便利,当然数组是一维到多维都可以 Numpy 中快速的处理。虽然 Numpy 也有矩阵 (Matrix)类型的数组。但推荐使用 array 对象,因为大部分数组操作以及函数均为 array 作为对象并且返回值也为 array 对象。
可以通过给 array() 函数传递 Python 的数据序列对象来创建数组,如果传递的是多层嵌套的序列,将创建多维数组。这种创建都是先创建一个 Python 的序列对象,然后通过 array 将其转换为数组。之外 Numpy 提供了很多专门创建数组的函数。
numpy.arange() 通过指定初值、终值和步长来创建等差数列的一维数组,所得到的结果中包含初值,不包含终值。
np.arange(0,5,0.5) # 0 到 5,步长为0.5
array([0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5])
numpy.linspace() 通过指定初值、终值和元素个数来创建表示等差数列的一维数组,可以通过endpoint 参数指定是否包含终值,默认值为 True ,即包含终值。需要注意的是 endpoint 的值会改变数组的等差步长。
np.linspace(0,1,10,endpoint=False)
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
numpy.logspace() 通过指定初值、终值和元素个数来创建表示等比数列的一维数组,基数可以通过 base 参数指定,其默认值为10,可以通过endpoint 参数指定是否包含终值,默认值为 True,即包含终值。
np.logspace(0,2,5)
array([1. ,3.16227766 ,10. ,31.6227766 , 100. ])
np.logspace(0,2,5,base=2)
array([1. ,1.41421356 ,2. ,2.82842712 , 4. ])
numpy.fromstring 从字节序列创建数组。Python 的字符串实际上是一个字节序列,每个字符占一个字节。类似的函数有frombuffer(),fromfile()。
st = "python"
np.fromstring(st,dtype=np.int8)
array([112, 121, 116, 104, 111, 110], dtype=int8)
#所得到的数组正好就是字符串中每个字符的ASCII码
numpy.fromfunction 定义一个从下标计算数值的函数,然后用此函数创建数组。fromfunction() 的第一个参数是计算每个数组元素的函数,第二个参数指定数组的形状。
def kod1(x):
return x**2
np.fromfunction(kod1,(6,))
# 第二个参数是长度为6的元组(6,),因此创建了一个有6个元素的一维数组。
array([ 0., 1., 4., 9., 16., 25.])
def kod2(x,y):
return x+y
np.fromfunction(kod2,(5,5))
array([[0., 1., 2., 3., 4.],
[1., 2., 3., 4., 5.],
[2., 3., 4., 5., 6.],
[3., 4., 5., 6., 7.],
[4., 5., 6., 7., 8.]])
特殊数组
随机类
np.random.rand(4) #生成大小为4的随机一维数组,元素为0到1之间的实数
array([0.93624589, 0.17548912, 0.81248716, 0.72682557])
np.random.rand(2,3) #生成大小为(2,3)的随机矩阵,元素为0到1之间的实数
array([[0.1463617 , 0.91962383, 0.06822833],
[0.37520596, 0.58522245, 0.37247154]])
np.random.randint(0,10,6) #产生一个长度为6 ,元素值为0到9的随机数姐,元素为整数
array([5, 7, 6, 7, 2, 8])
np.ones((3,4)) # 1 矩阵,大小为(3,4)
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
np.zeros((2,4)) # 0 矩阵,大小为(2,4)
array([[0., 0., 0., 0.],
[0., 0., 0., 0.]])
np.eye(3) #单位矩阵,大小为(3,3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
np.empty((2,2)) # 空矩阵,大小(2,2)。
#只分配内存,不执行元素初始化,元素是随机的
array([[1., 3.],
[5., 7.]])
np.full((2,3),6) # 指定填充的元素
array([[6, 6, 6],
[6, 6, 6]])
zeros_like(),ones_like(),empty_like(),full_like() 等函数创建与参数数组的形状和类
型相同的数组,即 zeros_like(a) 等同与 zeros(a.shape,a.dtype)。
方括号 [] 来读取元素。
a = np.arange(10)
print("a =",a)
print("a[4] =",a[4]) #整数作为下标获取数组中的元素
print("a[-1] =",a[-1])#整数作为下标获取数组中的元素,负数代表从后开始
print("a[2:5] =",a[2:5])#切片作为下标获取数组的一部分,包含a[2],不包含a[5]
print("a[:6] =",a[:6])#切片中省略开始下标,表示从a[0]开始,一直到第6个元素
print("a[:-2] =",a[:-2])#切片中省略开始下标,表示从a[0]开始,一直到倒数第2个元素
print("----------------")
print("a[1:-1:2] =",a[1:-1:2])#切片中的第三个参数表示步长,2表示隔一个元素取一个元素(往后i)
print("a[8:2:-2] =",a[8:2:-2])#切片中的第三个参数表示步长,2表示隔一个元素取一个元素(往前)
print("a[::-1] =",a[::-1])#省略切片的开始下标和结束下标,步长为-1,整个数组头尾颠倒
结果:
a = [0 1 2 3 4 5 6 7 8 9]
a[4] = 4
a[-1] = 9
a[2:5] = [2 3 4]
a[:6] = [0 1 2 3 4 5]
a[:-2] = [0 1 2 3 4 5 6 7]
----------------
a[1:-1:2] = [1 3 5 7]
a[8:2:-2] = [8 6 4]
a[::-1] = [9 8 7 6 5 4 3 2 1 0]
a = np.arange(20,40,2)
print("a =",a)
print(a[[2,3,3,5]]) #获取a中的下标为2,3,3,5的4个元素,组成一个新的数组
print(a[[2,3,-3,5]]) #获取a中的下标为2,3,-3,5的4个元素(-3表示取倒数第3个元素),组成一个新的数组
print(a[np.array([[3,3,1,8],[3,3,-3,8]])])#下标是多维数组时,得到的也是多维数组
结果:
a = [20 22 24 26 28 30 32 34 36 38]
[24 26 26 30]
[24 26 34 30]
[[26 26 22 36]
[26 26 34 36]]
以上是通过 [] 实现了切片,当然也可以利用slice()创建切片对象(略)。
通过 shape 和 reshape 来调整数组大小。
a = np.array([1,2,3,4,5,6]) # a = np.array((1,2,3,4,5,6))
# a = np.array([1,2,3,4,5,6],dtype=np.float) 指定属性
print("a = ",a)
print(a.shape)
a.shape = 2,3 #更改数组规模
print("a = ",a)
print(a.dtype)
运行结果:
a = [1 2 3 4 5 6]
(6,)
a = [[1 2 3]
[4 5 6]]
int64
可以看出 shape 可以读取数组维数,即每轴长度。 也可以通过 shape 来调整数组每个轴长度。二维数组也类似。
b = np.array([[1,2,3],[4,5,6],[7,8,9]])
print("b = \n",b)
print(b.shape)
b.shape = 1,-1
print("b = \n",b)
运行结果:
b =
[[1 2 3]
[4 5 6]
[7 8 9]]
(3, 3)
b =
[[1 2 3 4 5 6 7 8 9]]
可以看得出shape设置轴长度为 -1时,将自动计算此轴的长度。比如 array.shape = 1,-1 则把数组元素分配到一维数组,array.shape = 3,-1 则把数组元素分配到3轴而且每轴的长度一样。更改数组规模,也有reshape函数,用法如下:
b = np.array([[1,2,3,0],[4,5,6,-1],[7,8,9,-2]])
print("b = \n",b)
print(b.shape)
print("b = \n",b.reshape(4,3)) #b.reshape((4,3))
运行结果:
b =
[[ 1 2 3 0]
[ 4 5 6 -1]
[ 7 8 9 -2]]
(3, 4)
b =
[[ 1 2 3]
[ 0 4 5]
[ 6 -1 7]
[ 8 9 -2]]
通过 dtype 来指定元素属性,也可以获得属性。使用 astype()方法可以对数组的元素类型进行转换。
numpy 常见元素类型有:
numpy.bool_,numpy.bytes_,numpy.datetime64,numpy.object_,numpy.str_,numpy.void,numpy.timedelta64,
numpy.complex128,numpy.complex256,numpy.complex64,
numpy.float128,numpy.float16,numpy.float32,numpy.float64,
numpy.int16,numpy.int32,numpy.int64,numpy.int8,numpy.longlong,
numpy.uint16,numpy.uint32,numpy.uint64,numpy.uint8,numpy.ulonglong,
a = np.array([1,3,5,7],dtype=np.float)
print(a)
print(a.dtype)
a = a.astype(np.int32)
print(a)
print(a.dtype)
运行结果:
[1. 3. 5. 7.]
float64
[1 3 5 7]
int32
之外也可以自定义数据类型–结构数组,主要方合法如下:
#创建一个 dtype 对象 koding
koding = np.dtype({
'names':['name','age','weight'],
'formats':['S30','i','f']},align=True) #自定义数据类型
#它的参数是一个描述结构类型的各个字段的字典。字典有两个键:names,formats
#'S30':长度为30个字节的字符串类型,大小必须固定。
#'i':32位的整数类型,相当于np.int32。
#'f':32位的单精度浮点数类型,相当于np.float32。
a = np.array([("Zhang",32,75.5),("Wang",24,65.2)],dtype = koding)
print(a)
结果:
[(b'Zhang', 32, 75.5) (b'Wang', 24, 65.2)]
通过 view() 方法从同一块数据区创建不同的 dtype 的数组对象,也就是使用不
同的数值类型查看同一段内存中的数据。比如:
a = np.array([[0,1],[2,3]],dtype=np.float32)
#数组 a 的元素类型是单精度浮点数,占用4个字节
print(a)
[[0. 1.]
[2. 3.]]
b = a.view(np.uint32)
#和数组a使用同一段数据内存,但是它将每4个字节的数据当作无符号32位整数处理
print(b)
[[ 0 1065353216]
[1073741824 1077936128]]
c = a.view(np.uint8)
#和数组a使用同一段数据内存,但是它将每个字节的数据当作一个单字节的无符号整数处理
print(c)
[[ 0 0 0 0 0 0 128 63]
[ 0 0 0 64 0 0 64 64]]
# 比如,当a[0,0]的值改变时b[0,0]和c[0,:4]都会变。
ufunc是universal function的缩写,是一种对数组的每个元素进行运算的函数。听说Numpy内置的很多ufunc函数都是用C语言实现的,怪不得它计算快。数组之间的四则运算大部分都是ufunc函数。
# np.add(a,b,out=x) 计算a+b并返回值给x,若没有第三个参数则产生新的数组来保存结果。
a = np.arange(0,4)
b = np.arange(5,9)
print("a =",a,"\nb =",b)
print("a+b =",np.add(a,b)) #相加
a = [0 1 2 3]
b = [5 6 7 8]
a+b = [ 5 7 9 11]
c = np.random.randint(0,5,size=(2,3))
d = np.random.randint(0,10,size=(2,3))
print("c =\n",c)
print("d =\n",d)
print("c+d =\n",np.add(c,d))
c =
[[1 0 4]
[2 1 4]]
d =
[[4 4 4]
[1 4 1]]
c+d =
[[5 4 8]
[3 5 5]]
类似于上面的加法,有以下几个ufunc函数。令a,b,c为等大小的数组。
表达式 | 对应的ufunc函数 |
---|---|
c = a+b | numpy.add(a,b[,c]) |
c = a-b | numpy.subtract(a,b[,c]) |
c = a*b | numpy.multiply(a,b[,c]) |
c = a/b | numpy.divide(a,b[,c]) |
c = a//b | numpy.floor_divide(a,b[,c]) |
c = -a | numpy.negative(a[,c]) |
c = a**b | numpy.power(a,b[,c]) |
c = a%b | numpy.remainder(a,b[,c]) |
c = a==b | numpy.equal(a,b[,c]) |
c = a!=b | numpy.not_equal(a,b[,c]) |
c = a | numpy.less(a,b[,c]) |
c = a<=b | numpy.less_equal(a,b[,c]) |
c = a>b | numpy.greater(a,b[,c]) |
c = a>=b | numpy.greater_equal(a,b[,c]) |
and | numpy.logical_and |
or | numpy.logical_or |
not | numpy.logical_not |
数组运算当然支持四则运算操作符,如+,-,*…,但不支持逻辑运算。
广播:
当使用 uftinc 函数对两个数组进行计算时,ufunc 函数会对这两个数组的对应元素进行计算,因此它要求这两个数组的形状相同。如果形状不同,会进行如下广播( broadcasting )处理,主要规则如下:
1)让所有输入数姐都向其中维数最多的数绀看齐,shape 属性中不足的部分都通过在前面加1补齐。
2) 输出数组的 shape 属性是输入数组的 shape 屈性的各个轴上的最大值。
3)如果输入数组的某个轴的长度为1或与输出数组的对应轴的长度相同,这个数组能够用来计算,否则出错。
4) 当输入数组的某个轴的长度为1 吋,沿着此轴运算时都用此轴上的第一组值。
函数名 | 功能 |
---|---|
rand() | 0到1之间的随机数 |
randn() | 标准正态分布随机数 |
randint() | 指定范围内的随机整数 |
normal() | 正态分布 |
uniform() | 均匀分布 |
poisson() | 泊松分布 |
permutation() | 随机排列 |
shuffle() | 随机打乱顺序 |
choice() | 随机抽取样本 |
seed() | 设置随机数种子 |
科普数学
正态分布:若随机变量 X X X服从一个位置参数 μ \mu μ,尺度参数 σ \sigma σ的概率分布,且其概率密度函数为
f ( x ) = 1 2 π σ e − ( x − μ ) 2 2 σ 2 f(x)=\frac{1}{\sqrt{2\pi}\sigma}e^{-\frac{(x-\mu)^2}{2\sigma^2}} f(x)=2πσ1e−2σ2(x−μ)2
则这个随机变量称之为称为正态随机变量,正态随机变量服从的分布称为正态分布记作 X ∼ N ( μ , σ 2 ) X \sim N(\mu,\sigma^2) X∼N(μ,σ2) ,当 X ∼ N ( 0 , 1 ) X \sim N(0,1) X∼N(0,1)时称为标准正态分布。均匀分布:概率密度函数为
f ( x ) = { 1 b − a a < x < b 0 x ∈ e l s e f(x)= \left\{\begin{matrix} \frac{1}{b-a} & af(x)={b−a10a<x<bx∈else
则这个随机变量称服从均匀分布,记作 U ∼ ( a , b ) U \sim (a,b) U∼(a,b)泊松分布:泊松分布的概率函数为
P ( X = k ) = λ k k ! e − λ , k = 0 , 1 , 2 , . . . P(X=k)=\frac{\lambda ^k}{k!}e^{-\lambda}, k =0,1,2,... P(X=k)=k!λke−λ,k=0,1,2,...
泊松分布的参数 λ \lambda λ是单位时间(或单位面积)内随机事件的平均发生次数,泊松分布的期望和方差均为 λ \lambda λ。
随机类-例子:
np.set_printoptions(precision=2) #只显示小数点后两位数字
np.random.rand(4, 3)
array([[0.05, 0.49, 0.66],
[0.14, 0.9 , 0.27],
[0.15, 0.33, 0.49],
[0.09, 0.48, 0.91]])
np.random.randn(4, 3) #标准正态分布
array([[ 1.25, 0.82, 0.54],
[-0.47, 0.12, 0.57],
[-0.2 , 0.23, -0.11],
[ 1.26, -0.43, 0. ]])
np.random.randint(0, 10,(4, 3))
array([[5, 1, 0],
[0, 6, 7],
[1, 2, 0],
[9, 1, 6]])
分布类-例子:
np.set_printoptions(precision=2) #只显示小数点后两位数字
np.random.normal(100,10,(4,3)) #正态分布
#前两个参数分别为期望值和标准差
array([[105.69, 101.79, 90.17],
[ 92.73, 109.47, 85.28],
[110.26, 76.82, 87.63],
[ 86.72, 90.26, 101.68]])
np.random.uniform(10,20,(4,3))#均匀分布
#前两个参数分别为区间的起始值和终值
array([[14.97, 15.26, 16.45],
[17.01, 12.18, 19.66],
[13.65, 14.26, 13.09],
[19.27, 10.05, 15.8 ]])
np.random.poisson(2.0,(4,3))#泊松分布
#第一个参数指定lamda系数,它表示单位时间(或单位而积)内随机事件的平均发生率。#由于泊松分布是一个离散分布, 因此它输出的数组是一个整数数组
array([[3, 2, 0],
[4, 1, 1],
[4, 1, 2],
[3, 1, 3]])
排列类-例子:
np.random.permutation(10) #产生[0,10)的整数随机排列
array([7, 2, 6, 3, 8, 9, 5, 0, 1, 4])
a = np.array([1,3,5,4,9])
np.random.permutation(a) #产生数组a的随机排列,返回新数组
array([4, 9, 5, 3, 1])
a = np.array([1,3,5,4,9])
np.random.shuffle(a) #产生数组a的随机排列,直接打乱原来的数组
print(a)
[3 1 9 5 4]
函数名 | 功能 | 函数名 | 功能 |
---|---|---|---|
sum | 求和 | mean | 期望 |
average | 加权平均 | std | 标准差 |
var | 方差 | product | 连乘积 |
unique | 去除重复元素 | bincount | 整数元素统计 |
histogram | 一维直方图统计 | digitze | 离散化 |
例子:
a = np.random.randint(0,10,size=(3,4))
print(a)
[[9 9 9 6]
[7 4 9 1]
[5 0 2 0]]
print(np.sum(a)) #所有元素求和
print(np.sum(a,axis=0)) #每列元素求和
print(np.sum(a,axis=1)) #每行元素求和
61
[21 13 20 7]
[33 21 7]
a = np.random.randint(0, 5, 10)
print("a =",a)
a = [2 0 3 1 3 2 3 3 0 1]
print("unique(a) =",np.unique(a))
#返回其参数数组中所有不同的值,并且按照从小到大的顺序排列
unique(a) = [0 1 2 3]
print("bincount(a) =",np.bincount(a)) #各个元素所出现的次数进行统计
#返回数组中第 i 个元素的值表示整数 i 出现的次数
bincount(a) = [2 2 2 4]
函数名 | 功能 |
---|---|
where | 矢量化判断表达式 |
piecewise | 分段函数 |
select | 多分之判断选择 |
启发:怎样定义以下函数?(x不是一个值)
f ( x ) = { e − x , x > = 0 4 x + 1 , x < 0 f(x) = { \begin{cases} e^{-x} ,x >= 0\\ 4x+1 ,x < 0 \end{cases}} f(x)={e−x,x>=04x+1,x<0
def f(x):
return np.where(x>=0 ,np.exp(-x),4*x+1)
例子:
a = np.arange(10)
print("a =",a)
a = [0 1 2 3 4 5 6 7 8 9]
np.where(a<5,9-a,a) #where函数可以看作判断表达式的数组版本
array([9, 8, 7, 6, 5, 5, 6, 7, 8, 9])
'''select(condlist, choicelist, default=0)
分段函数的分段数量的增加,需要嵌套更多层where。这不便于程序的编写和阅读。
可以用select解决这个问题。'''
函数名 | 功能 | 函数名 | 功能 |
---|---|---|---|
concatenate | 连接多个数组 | swapaxes | 交换两轴的顺序 |
vstack | 沿第0轴连接数组 | hstack | 沿第1轴连接数组 |
column_stack | 按列连接多个一维数组 | transpose | 重新设置轴的顺序(转置) |
split | 数组分为多段 | array_split | 数组分为多段 |
例子:
a = np.arange(3) #array([0, 1, 2])
b = np.arange(6,9) #array([6, 7, 8])
np.vstack((a,b))
array([[0, 1, 2],
[6, 7, 8]])
np.hstack((a,b))
array([0, 1, 2, 6, 7, 8])
np.column_stack((a,b))
array([[0, 6],
[1, 7],
[2, 8]])
a = np.random.randint(0,10,size=(3,4))
array([[2, 1, 8, 9],
[1, 2, 5, 9],
[1, 1, 7, 4]])
np.transpose(a) #转置
array([[2, 1, 1],
[1, 2, 1],
[8, 5, 7],
[9, 9, 4]])
函数名 | 功能 | 函数名 | 功能 |
---|---|---|---|
dot | 矩阵乘积 | inner | 内积 |
outer | 外积 | tensordot | 张量乘积 |
vdot | 点积 | linalg.det | 行列式 |
linalg.solve | 矩阵线性方程的解 | linalg.inv | 矩阵的乘法逆矩阵 |
例子:
a = np.random.randint(0,10,size=(2,3))
array([[2, 2, 2],
[0, 6, 2]])
b = np.random.randint(0,5,size=(3,1))
array([[0],
[2],
[4]])
np.dot(a,b) #矩阵乘积(标准乘积)。第一个的列数=第二个的行数
array([[12],
[20]])
np.vdot(a,a) #点积(对应元素相乘,再求和)。行列数一样
52
np.inner(a,a) #内积。返回一维数组的向量内积,对于更高的维度,它返回最后一个轴上的和的乘积
array([[12, 16],
[16, 40]])
np.outer(a,b) #外积
array([[ 0, 4, 8],
[ 0, 4, 8],
[ 0, 4, 8],
[ 0, 0, 0],
[ 0, 12, 24],
[ 0, 4, 8]])
以上是Numpy基础部分,感谢阅读。