一.用于数组的文件输入输出

1.将数组以二进制格式保存到磁盘

np.save和np.load是读写磁盘数据的两个主要函数。默认情况下,数组是一未压缩的原始二进制格式保存在扩展名为.npy的文件中。

arr=np.arange(10)
np.save('some_arr',arr)  #np.save将数组保存到磁盘,文件名为some_arr.npy
print(np.load('some_arr.npy')) #np.load读取磁盘上的数组
a=np.array([1,3,6])
b=np.array([2,4,8])
np.savez('arr_archive.npz',a=np.array([1,3,6]),b=np.array([2,4,8]))  #np.savez将多个数组保存到一个压缩文件中,将数组以关键字参数的形式传入
arch=np.load('arr_archive.npz')
print(arch)  #输出一串二进制码
print(arch['a'])

2.存取文本文件

存取文本文件主要用到两个函数,一个是np.loadtxt将文本数据加载到普通的Numpy数组中,np.savetxt执行的是相反的操作:将数组写到以某种分隔符隔开的文本文件中

In [1]: !type array_test.txt
-1,-3,-5
0,-1.4,-6
2,4.6,3.7
6,8.4,24
In [2]: arr=np.loadtxt('array_test.txt',delimiter=',')

In [3]: import numpy as np

In [4]: arr=np.loadtxt('array_test.txt',delimiter=',')

In [5]: arr
Out[5]:
array([[-1. , -3. , -5. ],
[ 0. , -1.4, -6. ],
[ 2. , 4.6, 3.7],
[ 6. , 8.4, 24. ]])

 

arr=np.arange(10)
np.save('some_arr',arr)  #np.save将数组保存到磁盘,文件名为some_arr.npy
print(np.load('some_arr.npy')) #np.load读取磁盘上的数组
a=np.array([1,3,6])
b=np.array([2,4,8])
np.savez('arr_archive.npz',a=np.array([1,3,6]),b=np.array([2,4,8]))  #np.savez将多个数组保存到一个压缩文件中,将数组以关键字参数的形式传入
arch=np.load('arr_archive.npz')
print(arch)  #输出一串二进制码
print(arch['a'])

 

二.线性代数

线性代数(如矩阵乘法、矩阵分解、行列式以及其他方阵数学等)是任何数组库的重要组成部分。不想某些语言(如MATLAB),通过*对两个二维数组相乘得到的是一个元素级的积,而不是一个矩阵点积。因此,Numpy提供了一个用于矩阵乘法的dot函数(既是一个数组方法也是numpy命名空间中的一个函数):

   import numpy as np

x=np.array([[1,2,3],[4,5,6]])
y=np.array([[-1,-2],[-9,-8],[-7,-6]])
print(x.dot(y)) #相当于np.dot(x,y)
print(np.dot(x,y))
print(np.dot(x,np.ones(3))) #一个二维数组跟一个大小合适的一维数组的矩阵点积运算之后将会得到一个一维数组
#numpy.linalg中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的东西。

from numpy.linalg import inv,qr
x=np.random.randn(5,5)
mat=x.T.dot(x)
print(inv(mat))
print(mat.dot(inv(mat)))
q,r=qr(mat)
print(q)
print(r)

常用的numpy.linalg函数如下:

函数                                                          说明

diag                          以一维数组的形式返回方阵的对角线(或非对角线)元素,或将一维数组转换为方阵(非对角线元素为0)

dot                                         矩阵乘法

trace                                  计算对角线元素的和

det                                     计算矩阵行列式

eig                                     计算方阵的本征值和本征向量

inv                                     计算方阵的逆

pinv                                  计算矩阵的Moore-Penrose伪逆

qr                                      计算QR分解

svd                                    计算奇异值分解(SVD)

solve                                 解线性方程组Ax=b,其中A为一个方阵

lstsq                                  计算Ax=b的最小二乘解

 

三.随机数生成

numpy.random模块对Python内置的random进行了补充,增加了一些用于高效生成多种概率分布的样本值的函数
例如我们可以用mormal得到一个标准正态分布的4X4样本数组:
samples=np.random.normal(size=(4,4))
print(samples)

而Python内置的random模块则只能一次生成一个样本值。从下面的测试结果中可以看出,如果需要产生大量样本值,numpy.random快了不止一个数量级

 

In [1]: from random import normalvariate

In [2]: import numpy as np

In [4]: %timeit samples=[normalvariate(0,1) for _ in range(1000000)]
802 ms ± 3.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [5]: %timeit np.random.normal(size=1000000)
31.3 ms ± 181 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

下表列出了numpy.random中的部分函数

函数                                             说明

seed                                     确定随机数生成器的种子

permutation                          返回一个序列的随机排列或返回一个随机排列的范围

shuffle                                  对一个序列就地随机排列

rand                                      产生均匀分布的样本值

randint                                从给定的上下限范围内随机选取整数

randn                                 产生正态分布(平均值为0,标准差为1)的样本值,类似于MATLAB接口

binomial                            产生二项分布的样本值

normal                              产生正态(高斯)分布的样本值

beta                                  产生beta分布的样本值

chisquare                         产生卡方分布的样本值

gamma                            产生Gamma分布的样本值

uniform                           产生在[0,1)中均匀分布的样本值

 

四.范例:随机漫步

 

我们通过模拟随机漫步来说明如何运用数组运算。先来看一个简单的随机漫步的例子:

从0开始,步长1和-1出现的概率相等。我们通过内置的random模块以纯Python的方式实现1000步的随机漫步:

In [17]: import random

In [18]: position=0

In [19]: walk=[position]

In [20]: steps=1000

In [21]: for i in range(steps):
...: step=1 if random.randint(0,1) else -1
...: position+=step
...: walk.append(position)

In [22]: walk

不难看出,这其实就是随机漫步中各步的累计和,可以用一个数组运算来实现,因此我用np.random模块一次性随机产生1000个"掷硬币"结果(即两个数中任选一个),将其分别设置为1或-1,然后计算累计和:

In [22]: nsteps=1000

In [23]: draws=np.random.randint(0,2,size=nsteps)

In [24]: steps=np.where(draws>0,1,-1)

In [25]: walk=steps.cumsum()

有了这些数据之后,我们就可以做一些统计工作了,比如求取最大值和最小值:

In [26]: walk.min()
Out[26]: -29

In [27]: walk.max()
Out[27]: 10

现在来看一个复杂点的统计任务--------首次穿越时间,即随机漫步过程中第一次到达某个特定值的时间。假设我们想要知道本次随机漫步需要多久才能距离初始0点至少10步远(任一方向均可)。np.abs(walk)>=10可以得到一个布尔型数组,它表示的是距离是否达到或超过10,二我们想要知道的是第一个10或-10的索引,可以用argmax来解决这个问题,它返回的是该布尔型数组第一个最大值的索引(True就是最大值):

In [28]: (np.abs(walk)>=10).argmax()
Out[28]: 249

注意,这里使用argmax并不是很高效,因为它无论如何都会对数组进行完全扫描。在本例中,只要发现了一个True,那我们就知道它是最大值了。

 

一次模拟多个随机漫步

如果我们希望模拟多个随机漫步过程(比如5000个),只需对上面的代码做一点点修改即可生成所有的随机漫步过程。只要给numpy.random的函数传入一个二元元祖就可以产生一个二维数组,然后我们就可以一次性计算5000个随机漫步过程(一行一个)的累计和了:

In [1]: nwalks=5000

In [2]: nsteps=1000

In [3]: import numpy as np

In [4]: draws=np.random.randint(0,2,size=(nwalks,nsteps))

In [5]: steps=np.where(draws>0,1,-1)

In [6]: walks=steps.cumsum(1)

In [7]: walks
Out[7]:
array([[ -1, -2, -3, ..., -6, -5, -6],
[ -1, 0, -1, ..., -18, -19, -20],
[ -1, 0, 1, ..., 36, 35, 34],
...,
[ -1, -2, -3, ..., 22, 21, 22],
[ 1, 0, -1, ..., -28, -27, -28],
[ 1, 0, -1, ..., -2, -3, -4]], dtype=int32)

现在,我们来计算所有随机漫步过程的最大值和最小值:

In [8]: walks.max()
Out[8]: 120

In [9]: walks.min()
Out[9]: -132

得到这些数据之后,我们来计算30或-30的最小穿越时间。这里得要稍微动一下脑筋,因为不是5000个过程都到达了30.我们可以用any方法来对此进行检查:

In [10]: hits30=(np.abs(walks)>=30).any(1)

In [11]: hits30
Out[11]: array([False, False, True, ..., False, True, False])

In [12]: hits30.sum()
Out[12]: 3433

然后我们利用这个布尔型数组选出那些穿越了30(绝对值)的随机漫步(行),并调用argmax在轴1上获取穿越时间:

In [13]: crossing_times=(np.abs(walks[hits30])>=30).argmax(1)

In [14]: crossing_times.mean()
Out[14]: 499.69676667637634