1、简介
数值计算NumPy为Python(wwwxihefangpeicom)带来了真正的多维数组功能,它对常用的数学函数进行数组化,使这些数学函数能直接对数组进行运算,将本来需要在Python中进行的循环运算,转移到高效率的库函数中,充分利用这些函数能明显提高程序的运算速度。
SciPy则在Numpy的基础上添加了许多科学计算的函数库,比如LAPACK\FFTPACK\ODEPACK\MINPACK 。
SymPy是一套数学符号运算的扩展库,可以帮助我们尽心公式推导,做一些简单的符号运算工作。
界面设计:Python 可以使用多种界面库编写GUI程序,比如以TK为基础的Tkinter、以wxWidgets为基础的wxPython、以QT为基础的pyQt4.但是仍然比较繁琐。使用Traits库设计界面更加简单,能把注意力集中到数据处理上。Traits库分为Traits和TraitsUI两部分。
绘图与可视化:matplotlib和Chaco是两个很优秀的二维绘图库。matplotlib库能够快速绘制精美的图标、以多种格式输出,并带有简单的三维绘图功能。而Chaco则以Traits为基础,能够很方便地编写出交互式的图标控件,并嵌入到TraitsUI编写的界面程序中。TVTK库对标准的VTK库用Traits进行了封装,如果要在Python中应用VTK,用TVTK是最方便的选择。Mayavi则在TVTK的基础上添加了一套面向应用的工具,它既可以单独作为三维可视化应用,可以很方便地嵌入到TraitsUI编写的界面程序中。
2、Numpy
标准的Python中用列表(list)保存一组值,可以当作数组使用。但由于列表的元素是任意对象,因此,列表中保存的是对象的指针。这样以来,为了保存一个简单的列表[1,2,3],就需要有三个指针和三个整数对象。对于数值运算来说,这种结构显然比较浪费内存和CPU 计算时间。
Python还提供了array模块,它所提供的array对象能保存数值,但和C语言的一维数组类似。由于不支持多维数组,也没有各种运算函数,因此不适合做数值计算。
Numpy的诞生弥补了这些不足,NumPy提供了两种基本的对象,ndarray和ufunc。ndarray是存储单一数据类型的多维数组,而ufunc则是能偶对数组进行处理的函数。
2.1 ndarry多维数组
2.1.1 创建数组
需要创建数组才能对数组进行运算和操作,可以通过给array()函数传递Python的列表对象来创建数组,但是效率比较低。NumPy提供了很多专门用于创建数组的函数。
arrange()类似于内置函数range()通过指定开始值、终止值和步长创建表示等差数列的一维数组;linspace()通过指定开始至、终止值、和元素个数创建表示等差数列的一维数组,logspace与linspace类似,但用来创建等比数列。
zeors()、ones()、empty()可以创建指定形状和类型的数组,empty()仅仅分配内存,不对元素进行初始化操作,因此它的速度是最快的。zero_like()、ones_like()、empty_like()等函数可创建与参数数组的形状及类型相同的数组。因此,zero_likes(a)和zeros(a.shape,a.dtype)的效果相同。
使用frombuffer()、fromstring()、fromfile()、fromfunction()等函数可以从字节序列或文件创建数组。
2.1.2存取元素
可以使用和列表相同的方式对数组的元素进行存取,和列表不同的是,通过切片获取的新数组是原始数组的一个视图,它与原始数组共享同一块数存储空间。
除了使用切片下标存取元素之外,NumPy还提供了整数列表、整数数组和布尔数组等几种高级下标存取方法。当使用整数列表对数组元素进行存取时,将使用列表中的每个元素作为下标。使用列表作为下标得到的数组不和原始数组共享数据。当使用整数数组作为数组下标时,将得到一个形状和下标数组相同的新数组,新数组的每个元素都是用下标数组中对应位置的值作为下标从原数组获得的值。当下标数组是一维时,结果和用列表作为下标的结果相同。而当下标是多维数组时,则得到的也是多维数组。我们可以将其理解为:先将下标数组展平为一维数组,并作为下标获得一个新的一维数组,然后再将其形状修改为下标数组的形状。
2.1.3 多维数组
多维数组的存取和一维数组类似,NumPy采用元组(tuple)作为数组的下标,元组中的每个元素和数组的每个轴对应。虽然在Python 程序中,经常用圆括号将元组的元素括起来,但其实元组的
语法只需要用逗号隔开元素即可。
在[]中可以使用以冒号隔开的两个或三个整数表示切片,但是单独生成切片对象时需要使用selice()创建。它有三个参数,分别为开始值、结束值和间隔步长,当这些值需要省略时可以使用None。例如,a[slice(None,None,None),2]和a[:,2]相同。用Python 内置的slice()函数创建下标比较麻烦,因此NumPy 提供了一个s_对象来帮助我们创建数组下标。
2.1.4 结构数组
在C 语言中可以通过struct 关键字定义结构类型,结构中的字段占据连续的内存空间。类型相同的两个结构体所占用的内存大小相同,因此可以很容易定义结构数组。和C 语言一样,在NumPy 中也很容易对这种结构数组进行操作。只要NumPy 中的结构定义和C 语言中的结构定义相同,就可以很方便地读取C 语言的结构数组的二进制数据,将其转换为NumPy 的结构数组。
2.1.5 内存结构
数组的描述信息保存在一个数据结构中,用于保存数据的存储区域和用于描述元素类型的dtype对象。数据存储区域保存着数组中所有元素的二进制数据,dtype 对象则知道如何将元素的二进制数据转换为可用的值。数组的维数和形状等信息都保存在ndarray 数组对象的数据结构中。数组对象使用其strides 属性保存每个轴上相邻两个元素的地址差,即当某个轴的下标增加1 时,数据存储区域指针所增加的字节数。
2.2 ufunc运算
ufunc是universal function的缩写,它是一种能对数组中每个元素进行操作的函数。NumPy内置的许多ufunc函数都是在C语言级别实现的,因此它们的计算速度非常快,比通过循环计算快很多。通过测试发现,numpy.sin快于math.sin loop快于numpy.sin loop。
compare the time consume
import time
import math
import numpy as np
x = [i 0.001 for i in range(1000000)]
start = time.clock()
for i, t in enumerate(x):
x[i]= math.sin(t)
print ("math.sin:", time.clock()- start)
x = [i 0.001 for i in range(1000000)]
x = np.array(x)
start = time.clock()
np.sin(x,x)
print ("numpy.sin:", time.clock()- start)
x = [i * 0.001 for i in range(1000000)]
start = time.clock()
for i, t in enumerate(x):
x[i]= np.sin(t)
print ("numpy.sin loop:", time.clock()- start)
math.sin: 0.35965526958131483
numpy.sin: 0.013096042308926803
numpy.sin loop: 1.086287574551942
实际上,标准python中有比for循环更快的方案——使用列表推导式。。但是列表推导式将产生一个新的列表,而不是直接修改原来列表中的元素。下面的语句执行时,将计算出一个新的列表来保存每个正弦值:比如x = [math.sin(t)for t in x]
2.2.1 四则运算
NumPy提供许多ufunc运算,支持的四则运算有add(), substract(),multiply(), divide(), true_divide(), floor_divide(),negative(), power(), remainder().
数组对象支持操作符,极大简化了表达式的编写。
2.2.2 比较和布尔运算
使用“==”、“>”等比较运算符对两个数组进行比较,将返回一个布尔数组,它的每个元素值都是两个数组对应元素的比较结果。比如:>>> np.array([1,2,3]) < np.array([3,2,1])
array([ True, False, False], dtype=bool)
每个比较运算符都对应一个ufunc函数,
由于Python 中的布尔运算使用and、or 和not 等关键字,它们无法被重载,因此数组的布尔运算只能通过相应的ufunc 函数进行。这些函数名都以“logical_”开头。
2.2.3 自定义ufunct函数
通过NumPy提供的标准ufunc函数,可以组合出复杂的表达式,在C语言级别对数组的每个元素进行计算。但有时这种表达式不易编写,而对每个元素进行计算的程序却很容易用Python 实现,这时可以用frompyfunc()将一个计算单个元素的函数转换成ufunc 函数。这样就 可以方便地用所产生的ufunc 函数对数组进行计算了。
一般先用求值函数和列表推导式计算出一个列表,然后用array()将列表转换为数组,这种做法每次都要用列表推导式语法调用函数,对于多维数组是很麻烦的。通过frompyfunc()可以将计算单个值的函数转换为一个能对数组中每个元素进行计算的ufunc函数。frompyfunc()的调用格式为:
frompyfunc(func, nin, nout)
其中,func是计算单个元素的函数,nin是func输入参数的个数,nout是func返回值的个数。函数的返回数组类型是object,因此还需要调用数组的astype()方法以将其转换为双精度浮点数组。
比如下面的例子,triangle_ufunc1 就是自定义的ufunc函数。
def triangle_wave(x, c, c0, hc):
x = x - int(x) # 三角波的周期为1,因此只取x 坐标的小数部分进行计算
if x >= c:r = 0.0
elif x< c0: r = x / c0 hc
else: r = (c-x) / (c-c0) hc
return r
x=np.linspace(0,2,1000)
y=np.array([triangle_wave(t,0.6,0.4,0.1) for t in x])
mpl.plot(x,y)
mpl.show()
triangle_ufunc1 = np.frompyfunc(triangle_wave,4,1)
y2 =triangle_ufunc1(x,0.6,0.4,0.1)
y2 = y2.astype(np.float)
y2.dtype
mpl.plot(x,y2)
mpl.show()
2.2.4 广播
当使用ufunc 函数对两个数组进行计算时,ufunc 函数会对这两个数组的对应元素进行计算,因此要求这两个数组的形状相同。如果形状不同,会进行如下的广播(broadcasting)处理:
(1) 让所有输入数组都向其中维数最多的数组看齐,shape 属性中不足的部分都通过在前面加1 补齐。
(2) 输出数组的shape 属性是输入数组的shape 属性在各个轴上的最大值。
(3) 如果输入数组的某个轴长度为1 或与输出数组对应轴的长度相同,这个数组就能够用来计算,否则出错。
(4) 当输入数组的某个轴长度为1 时,沿着此轴运算时都用此轴上的第一组值。
a=np.arange(0,60,10).reshape(0-1,1)
b = np.arange(0,5)
a+b
array([[ 0, 1, 2, 3, 4], [10, 11, 12, 13, 14], [20, 21, 22, 23,24], [30, 31, 32, 33, 34], [40, 41, 42, 43, 44], [50, 51, 52, 53, 54]])
本作品采用《CC 协议》,转载必须注明作者和本文链接