Python 科学计算-Numpy

Python 科学计算-Numpy

NumPy(Numerical Python)是Python的一种开源的数值计算扩展,可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。

NumPy 通常与 SciPy(Scientific Python)和 Matplotlib(绘图库)一起使用, 这种组合广泛用于替代 MatLab,是一个强大的科学计算环境,有助于我们通过 Python 学习数据科学或者机器学习。

NumPy 是一个运行速度非常快的数学库,主要用于数组计算,包含:

  • 一个强大的N维数组对象 ndarray
  • 广播功能函数
  • 整合 C/C++/Fortran 代码的工具
  • 线性代数、傅里叶变换、随机数生成等功能

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是为了节省时间,同时保持代码的标准化

1.多维数组 ndarray

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]都会变。

2.特殊函数 ufunc

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 吋,沿着此轴运算时都用此轴上的第一组值。

Numpy库函数

随机生成类
函数名 功能
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π σ1e2σ2(xμ)2
则这个随机变量称之为称为正态随机变量,正态随机变量服从的分布称为正态分布记作 X ∼ N ( μ , σ 2 ) X \sim N(\mu,\sigma^2) XN(μ,σ2) ,当 X ∼ N ( 0 , 1 ) X \sim N(0,1) XN(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)={ba10a<x<bxelse
则这个随机变量称服从均匀分布,记作 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)={ex,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基础部分,感谢阅读。

如果觉得还不错,记得关注哦!。
公众号:koding
Python 科学计算-Numpy_第1张图片知乎:
Python 科学计算-Numpy_第2张图片

你可能感兴趣的:(Python,科学计算-Numpy,python,numpy,线性代数,矩阵)