numpy
为什么使用numpy?
1,内置ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。
2,对整组数据进行快速运算的标准数学函数(无需编写循环)。
3,用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
4,线性代数、随机数生成以及傅里叶变换功能
5,用于集成由C、C++、Fortran等语言编写的代码的A C API。
更快的速度,更少的内存
In [7]: import numpy as np
In [8]: my_arr=np.arange(1000000)
In [9]: my_list=list(range(10000000))
In [10]: %time for _ in range(10):my_arr2=my_arr*2
Wall time: 18 ms
In [11]: %time for _ in range(10):my_list2=[x*2 for x in my_list]
Wall time: 9.27 s
原因是NumPy的C语言编写的算法库可以操作内存,而不必进行类型检查或其它前期工作。比起Python的内置序列,NumPy数组使用的内存更少。
4.1 NumPy的ndarray:一种多维数组对象
In [32]: data=np.random.randn(2,3)
#生成一个二维数组
In [33]: data
Out[33]:
array([[-0.16078899, -0.00837986, 1.67358752],
[ 0.28902231, -1.20871575, -0.07659614]])
n [37]: type(data)
Out[37]: numpy.ndarray
创建ndarray
使用array函数
In [43]: li=[1,2,3,4]
In [44]: arr=np.array(li)
In [45]: arr
Out[45]: array([1, 2, 3, 4])
In [48]: arr2
Out[48]:
array([[1, 3, 5, 7],
[2, 4, 6, 8]])
查看ndarray数组元素个数
In [49]: arr2.ndim
Out[49]: 2
查看该ndarray数组的规模
In [50]: arr2.shape
Out[50]: (2, 4)
使用np.zeros创建元素全为零的指定长度数组
In [62]: np.zeros(10)
Out[62]: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In [63]: np.zeros((2,3))
Out[63]:
array([[0., 0., 0.],#二维数组
[0., 0., 0.]])
In [65]: np.zeros((2,3,2))#三维数组
Out[65]:
array([[[0., 0.],
[0., 0.],
[0., 0.]],
[[0., 0.],
[0., 0.],
[0., 0.]]])
#np.empyt()
In [81]: empty_u32=np.empty(8,dtype='u4')
In [83]: empty_u32
Out[83]:
array([ 0, 1073741824, 858993459, 1072902963, 0,
1074790400, 0, 1075052544], dtype=uint32)
使用np.empyt并不总是生成全为0的数组,它实际上生成的是未经初始化的垃圾值
控制python的数据存储
In [69]: arr
Out[69]: array([1, 2, 3, 4])
In [70]: arr.dtype
Out[70]: dtype('int32')
In [71]: float_arr=arr.astype(np.float64)
In [74]: float_arr.dtype
Out[74]: dtype('float64')
image.png
image.png
NumPy很聪明,它会将Python类型映射到等价的dtype上。
NumPy数组的运算
矢量运算&广播
大小相等的数组之间的任何算术运算都会将运算应用到元素级:(矢量运算)
In [52]: arr
Out[52]:
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
In [53]: arr * arr
Out[53]:
array([[ 1., 4., 9.],
[ 16., 25., 36.]]
数组与标量的算术运算会将标量值传播到各个元素:(广播)
In [87]: arr
Out[87]: array([1, 2, 3, 4])
In [88]: 1/arr
Out[88]: array([1. , 0.5 , 0.33333333, 0.25 ])
大小相同的数组之间的比较会生成布尔值数组:
In [89]: arr2=np.array([2,1,4,3])
In [90]: arr>arr2
Out[90]: array([False, True, False, True])
ps:不同大小的数组之间的运算叫做广播(broadcasting)
基本的索引和切片
切片索引
In [94]: arr
Out[94]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [95]: arr[6:8]=12
In [96]: arr2=arr[6:8]
In [97]: arr2
Out[97]: array([12, 12])
改变切边中的值,原来的数组也会改变
In [103]: arr2[1]=1111
In [104]: arr
Out[104]: array([ 0, 1, 2, 3, 4, 5, 12, 1111, 8, 9])
为什么numpy中不是复制数组呢?
因为numpy常用来处理大数据。复制数组会导致很大的内存开销
注意:如果你想要得到的是ndarray切片的一份副本而非视图,就需要明确地进行复制操作,例如arr[5:8].copy()。在numpy中,直接选取数组子集,得到的都是视图。(即对子集的操作都会反映在原数组上)
Out[107]:
array([[1, 2, 3],
[2, 3, 4],
[5, 6, 7]])
In [108]: arr[1]
Out[108]: array([2, 3, 4])
In [109]: arr[1,0]#一种更简便的对于多维数组的索引方式
Out[109]: 2
In [110]: arr[1][0]
Out[110]: 2
布尔型索引
现在有一个name数组和一个随机生成的正太数组。假设每一个名字对应data中的一行
In [143]: data=np.random.randn(7,4)
In [147]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In [115]: names
Out[115]: array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
In [116]: data
Out[116]:
array([[ 0.65641145, -0.68575194, -1.11722843, -0.70869661],
[ 0.79043072, -1.87153313, -1.17771005, -0.24625949],
[-0.97664234, -0.46123195, 1.26749148, -0.50228323],
[ 1.301104 , -2.97752302, -0.12416972, 0.90884544],
[ 0.07767613, -0.73169363, -0.10861381, 1.94294076],
[ 0.13195771, -0.3692649 , -1.10250285, -0.6606611 ],
[ 1.26198313, -0.17574783, -0.07108658, -0.32310181]])
传播等运算:得到一个布尔型数组
In [121]: names=='Bob'
Out[121]: array([ True, False, False, True, False, False, False])
这个布尔型数组也可以用来索引
In [123]: data[names=='Bob']
Out[123]:
array([[ 0.65641145, -0.68575194, -1.11722843, -0.70869661],
[ 1.301104 , -2.97752302, -0.12416972, 0.90884544]])
**也可以配合与(&)或(|)运算符食用:
In [142]: data[(names=='Bob')|(names=='Will')]
Out[142]:
array([[ 0.65641145, -0.68575194, -1.11722843, -0.70869661],
[-0.97664234, -0.46123195, 1.26749148, -0.50228323],
[ 1.301104 , -2.97752302, -0.12416972, 0.90884544],
[ 0.07767613, -0.73169363, -0.10861381, 1.94294076]])
通过布尔型数组设置值是一种经常用到的手段。为了将data中的所有负值都设置为0,我们只需:
In [150]: data[data<0]=0#在numpy运算中,若参与运算的两个数据大小不同,则会开始广播
In [151]: data
Out[151]:
array([[0. , 0.12474159, 0. , 1.28128485],
[0. , 0.48916225, 0. , 0. ],
[0.7997503 , 0. , 0.59180193, 0. ],
[0. , 0. , 1.94923891, 0. ],
[0. , 0. , 1.56896046, 1.76103847],
[0. , 0.53510092, 0. , 0.76386017],
[0.54751396, 0.22536771, 1.03933349, 1.95296145]])
以特定顺序选取子集,只需传入一个特定顺序的列表:
In [161]: arr
Out[161]:
array([[0., 0., 0., 0.],
[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.],
[4., 4., 4., 4.],
[5., 5., 5., 5.],
[6., 6., 6., 6.],
[7., 7., 7., 7.]])
In [162]: arr[[4,3,0,6]]
Out[162]:
array([[4., 4., 4., 4.],
[3., 3., 3., 3.],
[0., 0., 0., 0.],
[6., 6., 6., 6.]])
也可以反向选取
In [164]: arr[[-1]]
Out[164]: array([[7., 7., 7., 7.]])
花式索引
它指的是利用整数数组进行索引
In [184]: data[[1,2,4]]
Out[184]:
array([[ 0.31111664, 0.46352445, 0.2631364 , -0.57191206],
[-0.55652541, 0.23431718, -1.20116721, -3.22754567],
[-1.49596259, 0.60906943, -0.89470998, 1.78687171]])
布尔值数组索引和花式索引都是获取原数组的副本,而切片索引获取的是视图
数组的转置
In [185]: arr=np.arange(15).reshape(3,5)
In [186]: arr
Out[186]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
In [187]: arr.T
Out[187]:
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
计算矩阵
np.dot计算矩阵内积:
In [188]: np.dot(arr,arr.T)
Out[188]:
array([[ 30, 80, 130],
[ 80, 255, 430],
[130, 430, 730]])
4.2 通用函数:快速的元素级数组函数
通用函数(即ufunc)是一种对ndarray中的数据执行元素级运算的函数。你可以将其看做简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。
一元ufunc 接受一个数组 如:sqrt和exp
In [6]: arr
Out[6]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [7]: np.sqrt(arr)
Out[7]:
array([0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
二元ufunc 接受两个数组 如maximum,add
In [23]: np.maximum(x,y)
Out[23]:
array([ 1.50417606, 0.77093677, 0.13076845, -0.47592856, 1.0153084 ,
0.09279153, -0.28780107, 1.02153997, 1.08394614, 1.52778182])
#np.maximum计算了x,y中元素级别最大的元素,并以一个ndarray的数组形式返回
In [24]: np.add(x,y)
Out[24]:
array([ 1.97583134, 0.19053618, 0.21755006, -1.01121673, 1.25397178,
-0.51540847, -1.64656588, 0.45943148, 0.09127444, 1.6387961 ])
返回两个数组的ufnc
In [32]: arr
Out[32]:
array([-3.08506902, 5.21511455, 1.51276318, 9.76449431, 6.42604127,
4.25610235, -6.95373912, -7.84043916, -4.55511629, 3.92394445])
In [33]: np.modf(arr)
Out[33]:
(array([-0.08506902, 0.21511455, 0.51276318, 0.76449431, 0.42604127,
0.25610235, -0.95373912, -0.84043916, -0.55511629, 0.92394445]),
array([-3., 5., 1., 9., 6., 4., -6., -7., -4., 3.]))
image.png
image.png
image.png
4.3 利用数组进行数据处理
NumPy数组使你可以将许多种数据处理任务表述为简洁的数组表达式(否则需要编写循环)。用数组表达式代替循环的做法,
通常被称为矢量化。一般来说,矢量化数组运算要比等价的纯Python方式快上一两个数量级(甚至更多),尤其是各种数值计算。
作为简单的例子,假设我们想要在一组值(网格型)上计算函数sqrt(x2+y2)。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x,y)对):
In [57]: points=np.arange(-5,5,0.01)
In [58]: xs,ys=np.meshgrid(points,points)
In [59]: xs,ys
Out[59]:
(array([[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
...,
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99]]),
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
[-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
[-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
...,
[ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
[ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
[ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]]))
根据网格对函数求值的结果
In [165]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
...:
...: In [166]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
...:
...: In [167]: cond = np.array([True, False, True, True, False])
将条件逻辑表述为数组运算
numpy.where函数是三元表达式x if condition else y的矢量化版本。假设我们有一个布尔数组和两个值数组:
In [165]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
...:
...: In [166]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
...:
...: In [167]: cond = np.array([True, False, True, True, False])
假设我们想要根据cond中的值选取xarr和yarr的值:当cond中的值为True时,选取xarr的值,否则从yarr中选取。列表推导式的写法应该如下所示:
result=[x if c else y for x,y,c in zip(xarr,yarr,cond)]
这样会有两个问题:1,对大数组的处理速度不快(纯python完成);2,不支持高维数组
下面使用np.where
In [110]: result=np.where(cond,xarr,yarr)#即如果cond为True则取xarr,否则取yarr
np.where的后两个参数不必是数组
数学和统计方法
可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算
In [130]: arr
Out[130]:
array([[0. , 0.23007915, 0. , 0. ],
[0. , 0.92936307, 0.84937464, 0.56961218],
[1.04159036, 0.53416612, 0.78407108, 1.16551425],
[1.0832948 , 1.46905153, 0.37942245, 0. ]])
In [131]: np.sum(arr)
Out[131]: 9.035539629260919
In [132]: np.mean(arr)
Out[132]: 0.5647212268288074
指定轴向
In [133]: np.sum(arr,axis=1)
Out[133]: array([0.23007915, 2.3483499 , 3.5253418 , 2.93176878])
image.png
image.png
用于布尔型数组的方法
在上面这些方法中,布尔值会被强制转换为1(True)和0(False)。因此,sum经常被用来对布尔型数组中的True值计数。下面的例子用于计算数组中的偶数:
In [135]: arr=np.arange(99)
np.sum(arr%2==0)
Out[138]: 50
any和all:any表示是否数组中至少有一个为True,all表示是否数组全部为True
排序
和python内置的列表一样,numpy也支持sort方法
In [141]: arr
Out[141]:
array([ 0.09497013, 0.30673725, 1.02170321, -0.17585964, -0.10991817,
-0.09855545, 0.27172168, 1.06204876, -1.80845245, 1.23908156])
In [142]: arr.sort()
In [143]: arr
Out[143]:
array([-1.80845245, -0.17585964, -0.10991817, -0.09855545, 0.09497013,
0.27172168, 0.30673725, 1.02170321, 1.06204876, 1.23908156])
唯一化以及其它的集合逻辑
In [149]: names=np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In [150]: np.unique(names)
Out[150]: array(['Bob', 'Joe', 'Will'], dtype='<U4')
4.4 用于数组的文件输入输出
4.5 线性代数
线性代数(如矩阵乘法、矩阵分解、行列式以及其他方阵数学等)是任何数组库的重要组成部分。不像某些语言(如MATLAB),通过*对两个二维数组相乘得到的是一个元素级的积,而不是一个矩阵点积。因此,NumPy提供了一个用于矩阵乘法的dot函数(既是一个数组方法也是numpy命名空间中的一个函数):
矩阵乘法
In [159]: x
Out[159]:
array([[1, 2, 3],
[1, 4, 5],
[6, 3, 1]])
In [160]: y
Out[160]:
array([[4, 3, 2, 4],
[7, 9, 2, 3],
[3, 5, 7, 1]])
In [162]: np.dot(x,y)
Out[162]:
array([[27, 36, 27, 13],
[47, 64, 45, 21],
[48, 50, 25, 34]])
numpy.linalg
计算行列式,逆矩阵等
numpy.linalg中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的东西。它们跟MATLAB和R等语言所使用的是相同的行业标准线性代数库,如BLAS、LAPACK、Intel MKL(Math Kernel Library,可能有,取决于你的NumPy版本)等:
**
image.png
4.6 伪随机数生成
numpy.random模块对Python内置的random进行了补充,增加了一些用于高效生成多种概率分布的样本值的函数。例如,你可以用normal来得到一个标准正态分布的4×4样本数组:
In [169]: samples=np.random.normal(size=(4,4))
In [170]: samples
Out[170]:
array([[-0.71736003, -0.31440865, 0.1260361 , 0.15161766],
[-0.14671479, 1.52192252, 0.9491315 , 0.22841685],
[ 1.49061488, -0.11505862, 0.13655971, 1.30328566],
[-0.60567777, -0.51521654, 3.18577393, -0.21291564]])
image.png
image.png
4.7 示例:随机漫步
我们通过模拟随机漫步来说明如何运用数组运算。先来看一个简单的随机漫步的例子:从0开始,步长1和-1出现的概率相等。
使用random模块
In [178]: import random
In [192]: position=0
In [193]: walk=[position]
In [194]: steps=1000
In [195]: for i in range(steps):
...: step=1 if random.randint(0,1) else -1
...: position+=step
...: walk.append(position)
In [197]: plt.plot(walk)
Out[197]: [<matplotlib.lines.Line2D at 0x14b456c32b0>]
In [198]: plt.show()
image.png
使用numpy.random
一次性产生每一步即可(random和numpy.random的一个大区别就是前者一次只能产生一个随机数,后者可以产生多个)
In [270]: cond=np.random.randint(0,2,size=1000)
In [271]: steps=np.where(cond,1,-1)
In [272]: walks=np.cussum(steps)##得到每一步的累加结果数组
求该漫步中第一次穿越30(绝对值大于30)的步点
In [36]: (np.abs(walks)>10).argmax()
Out[36]: 246
下面来求多个漫步过程
In [7]: nwalks=500
In [8]: nsteps=1000
In [9]: cond=np.random.randint(0,2,size=(nwalks,nsteps))
In [10]: steps=np.where(cond,1,-1)
In [11]: walks=np.cumsum(steps)
In [16]: walks
Out[16]:
array([[ 1, 2, 1, ..., 6, 5, 4],
[ 1, 0, 1, ..., 26, 25, 24],
[ -1, -2, -3, ..., 38, 37, 36],
...,
[ 1, 2, 3, ..., -44, -43, -44],
[ -1, 0, -1, ..., -32, -33, -34],
[ 1, 2, 3, ..., 36, 35, 34]], dtype=int32)
即得500个1000步的随机漫步过程
先筛选出穿越了30步的漫步过程:
In [23]: walks[(np.abs(walks)>30).any(1)]
Out[23]:
array([[ 1, 2, 1, ..., 6, 5, 4],
[ 1, 0, 1, ..., 26, 25, 24],
[ -1, -2, -3, ..., 38, 37, 36],
...,
[ 1, 2, 3, ..., -44, -43, -44],
[ -1, 0, -1, ..., -32, -33, -34],
[ 1, 2, 3, ..., 36, 35, 34]], dtype=int32)
求出被筛选出来的这些漫步过程中的首次穿越索引
In [25]: walks[(np.abs(walks)>30).any(1)].argmax(1)
numpy方法补充
np.cos():计算余弦值
np.log10():求底数为10的对数运算
np.average()与np.mean()类似,不过np.average可以接收参数weight(权重)
然后求出加权平均数
网友评论