分类: LINUX
2011-01-07 12:39:59
《用Python做科学计算》做得很漂亮,字体瞧着顺眼儿,代码部分不仅照例是等宽字符,还换了灰背景,作者是个有心人。今天答辩委员会的专家还提到了写论文要细致严谨,除了文字之外的其他细节包括图表的刻度、图例和图注还有论文字体大小对齐等等都要力争做到至少规范。因此在投入程度方面儿我要向作者致敬并学习。当然,还有他/她的开源精神~
从目录看只准备看NumPy,SciPy和matplotlib的部分。此外比较感兴趣的是3D演示方面的,因为之前没怎么接触过,不过那部分等先学biopython和wxPython、然后继续修炼一阵Python再说,现在还远照顾不到。吾生也有涯!
凡例:a. [float]表示对象类型是float,用时不用加'['和']'。
Chap 2 NumPy-快速处理数据
1. 两个科学计算Python合集:Python(x,y)和Enthought Python Distribution (EPD)。交互解释器:iPython(装了一个,偶尔用用,还没体会到比Python自带的shell的强大所在及程度)、spyder(Python(x,y)自带的IDE,模仿MATLAB的workspace功能)。比较有用的函数库:Numpy,SciPy,SymPy(符号运算系虾米?),Traits(界面设计库,说得我都想弃wxPython开始学这个了。在计算机领域学什么是个很重要的问题。),TVTK(用Traits库封装的标准VTK库),Visual(制作3D动画演示),OpenCV(这个前几天还碰到过,感觉跟图像生成渲染之类的有关,对于Python,也提供了API)。
2. 调整shape时数组(就是官方文档里的array)元素在内存中的位置并没有改变,也就是提供了一种view而非copy。如果某一维数值是-1,则按数组的长度和其他维度的数值自动计算。
3. reshape方法也是,返回一个shape修改过的数组,但是还是view而非copy。
4. 和linspace相似,logspace创建在log尺度均匀分布的数值组成的数列,即等比数列。
5. 此外,还可以使用frombuffer, fromstring, fromfile等函数可以从字节序列创建数组,也就是上次没有深入的领域。以fromstring为例,在内存中以字节方式存储。一个字节(byte)占8个bit(终于搞明白byte和bit的关系啦,呼瑞!),如果要转化成int16的话有个两个字节串联顺序的问题。Python里是以little endian(又见little endian!当年写tiqho的时候没整明白的问题终于搞明白了,happy)方式在内存中保存数据的,即低位字节在前,故int16值等于之前的字节*256(应该是因为是一个byte占8个bit,故用2^8,不过还是没彻底明白)加上后一个字节。
6. 还可以使用fromfunction利用自己写的函数进行转换。比如:np.fromfunction(func, (10,)),其中func是自写函数,(10,)是返回数组的shape。func的参数默认从0开始,直到填满指定的shape。
7. 数组的reference还可以用boolean array(一般不是手动一个个敲出来的,见9),此时必须是array,不能再用列表蒙混了,broadcast时都按False算。返回其中为True的元素的相应位置上的目标array中的元素。
8. np.random.rand([int]) :产生一个长度为[int],元素值为0-1的随机数的数组。
9. 对于数组,如果进行大小比较,和Python里类似,返回的也是bool,不过是个数组。这个数组就可以用来做7里的reference。见7。
10. seriously?——“组元不需要圆括号虽然我们经常在Python中用圆括号将组元括起来,但是其实组元的语法定义只需要用逗号隔开即可。
11. 结构数组(structured array)有点像类(或者我理解的类),定义一个dtype对象,有一定模式,然后指定其他对象的dtype是它。
12. 查看dtype:obj.dtype,可能返回如下:dtype([('name', '|S32'), ('age', '
13. 作者认为“在C语言中我们可以通过struct关键字定义结构类型,结构中的字段占据连续的内存空间,每个结构体占用的内存大小都相同”。受教了。
14. 通过调用数组的tostring或者tofile方法,可以直接输出数组的二进制形式。比如:a.tofile("test.bin")。
15. 和C语言协作使用时需要注意的是C语言的结构体为了内存寻址方便,会自动的添加一些填充用的字节,即内存对齐,以保持最终的结构体大小变。因此如果NumPy中的所配置的内存大小不符合C语言的对齐规范的话,将会出现数据错位。在创建dtype对象时,可以传递参数align=True以确保不发生数据错位。
16. 用字典参数也可以定义dtype对象时,因为字典的关键字是没有顺序的,所以需要在类型描述中给出字段的顺序。用法是在字段(field)后给出偏移量(即offset)的值,单位是byte,如:dtype([('surname', '|S25'), ('age', '|u1')])。
17. ndarray在内存中的数据结构太深,不过为了将来万一和其他语言的串接,硬着头皮看一看吧。ndarray数据结构引用两个对象:数据存储区和dtype对象存储区,具体包括dtype,dim count,dimensions,strides和data。dim count指维度的数目;dimesion指各维度的数字;strides指每个轴的下标增加1时数据存储区中的指针所增加的字节数,比如有个3*3的数组,元素类型是float32,那么每个数占4(32/8)字节。在C语言格式中,第二个维度的数字比第一个变得快,所以第二个维度数字增加1指针增加4字节,第一个的话就是12(4*3)个字节,所以strides(本身就有步伐”的意思)分别为12和4。不过这要求数据在内存中连续存储。另一种数字跳的方式是Fortran语言格式的,需要重设参数值,改变默认设定:order=''F''。
18. ufunc原来是说能对数组里每个元素都操作的函数,ft~
19. ufunc创建新数组并返回,如果要覆盖的话可以在第二个参数里指定被覆盖的对象,比如:t = np.sin(x,x),x表现为经过了更新。
20. 算数计算运算符和函数,其中[, y]是可选参数,用于指定覆盖对象,除号运算符的处理根据是否激活__future__.division有所不同:x1+x2,相当于add(x1, x2 [, y]);x1-x2,相当于subtract(x1, x2 [, y]);x1*x2,multiply(x1, x2 [, y]);x1/x2: divide(x1, x2 [, y]),整数除法;x1/x2: true divide (x1, x2 [, y]), 返回精确的商;x1//x2,floor divide (x1, x2 [, y]),地板除;-x,negative(x [,y]);x1**x2,power(x1, x2 [, y]);x1%x2,remainder(x1, x2 [, y]), mod(x1, x2, [, y])。
21. 复杂的算式处理大数组会产生大量的中间结果而导致速度减缓,一个小技巧是手工拆分算式分解,以尽量减少内存分配,提高速度。
22. 下一个是应该算是神器级别的函数:frompyfunc,把Python里的函数(可以是自写的)转化成ufunc,用法是frompyfunc(func, nin, nout),其中func是需要转换的函数,nin是函数的输入参数的个数,nout是此函数的返回值的个数。注意frompyfunc函数无法保证返回的数据类型都完全一致,因此返回一个中间类型object,需要再次obj.astype(np.float64)之类将其元素类型强制调齐。
23. 作者对broadcast的处理是直接翻成“广播”。补齐的规则除了以前记下的,还有:维度差异的部分通过在已有的维度前面加1进行补齐;输出数组的shape是输入数组shape的各个轴上的最大值;当输入数组的某个轴的长度为1时,沿着此轴运算时都用此轴上的第一组值。
24. repeat方法:b.repeat(6,axis=0),在第0轴(shape里的第一个维度)的方向上长度扩展为6,也就是第2个维度重复5次(有点绕,呵呵)。
25. ogrid对象:用切片组元作为下标进行存取,返回一到多个可以用来广播计算的数组。有两种用法:开始值:结束值:步长,和np.arange类似(不包含结束值喽); 开始值:结束值:长度j,当第三个参数为虚数时,它表示返回的数组的长度,和np.linspace类似(包含结束值)。
26. ufunc的reduce 方法:和Python的reduce函数类似,沿着一个指定的轴将运算符插入这根轴上所有子数组或者元素当中,并返回结果,比如:np.add.reduce([[1,2,3],[4,5,6]], axis=1),其中axis为1,指的是第2个维度上的元素,即[1,2,3]和[4,5,6]。
27. accumulate与reduce类似,不同之处在于返回的数组里包括所有中间结果,致使其shape与输入数组的相同。比如:np.add.accumulate([[1,2,3],[4,5,6]], axis=1),返回array([[1,3,6],[4,9,15]]),其中1(1),3(1+2),4(4),9(4+5)是中间结果。
28. reduceat方法通过indices参数(列表形式)指定一系列插入reduce的起始和终止位置(reduce at嘛),规则如下:如果indice中某元素小于其后元素,则相应结果为对以这两个元素为位置产生的slice里的数组元素进行reduce;否则结果是这个元素对应的数组元素。对于最后一个元素,因为其后再没元素,结果为对所有元素进行reduce。例子:np.add.reduceat(np.array([1,2,3,4]),indices=[0,1,0,2,0,3,0]),返回array([1,2,3,3,6,4,10])。
29. outer方法,
30. 终于进入矩阵部分了,NumPy和Matlab不一样,对于多维数组的运算,缺省情况下并不使用矩阵运算。但因为NumPy中同时存在ndarray和matrix类,用户很容易将两者弄混,有违the Zen of Python(import this!),因此matrix类的优先状态是不启用。
31. 矩阵乘法:如果需要将一维数组当作列矢量或者行矢量进行矩阵运算时,推荐先reshape之。
32. dot函数:对于一维数组,计算内积,就是两个数组对应下标元素的乘积(应该就是点积);对于二维数组计算矩阵乘积(嘛意思?);对于更高维数组,规则是:dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]),即结果数组中的每个元素都是数组a的最后一维上的所有元素与数组b的倒数第二维上的所有元素的乘积之和。
33. np.alltrue(),check是否数组每个元素均为True。
34. inner : 对于两个一维数组计算内积;对于多维数组,它计算的结果数组中的每个元素都是:数组a和b的最后一维的内积,因此数组a和b的最后一维的长度必须相同,即:inner(a, b)[i,j,k,m] = sum(a[i,j,:]*b[k,m,:])。
35. outer : 只按照一维数组进行计算,如果传入参数是多维数组,则先将此数组展平为一维数组之后再进行运算,结果是列向量和行向量的矩阵乘积。感觉跟multiply.outer没啥区别啊,见29。
36. np.linalg库还有很多线性代数领域的函数和功能,比如以前用过的np.linalg.det。这本书里提到了可求逆矩阵的inv函数,和求解多元一次方程组的solve函数。后者用法:np.linalg.solve(a,b),其中a是一个N*N的二维数组,而b是一个长度为N的一维数组,solve函数找到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。
37. 现在进入文件存取了。使用tofile可以方便地将数组中数据以二进制的格式写进文件。tofile输出的数据不带格式,shape和dtype都没有,且使用C语言格式输出,不管数组本身的格式。因此,用fromfile读回来的时候需要自己格式化数据。
38. tofile方法还可以输出为文本格式,此时需要指定sep参数。用fromfile读取时也需要指定此参数。
39. 较为方便的方法是用load和save函数进行数据存取,文件名后缀是.npy,格式是NumPy专用的二进制类型,不用再管shape和dtype这些,不过代价是很难与其他语言的程序协作。通用和专有的矛盾!
40. savez:多个数组保存在同一文件中,第一个参数是输出文件名,后面是其后的参数都是需要保存的数组,自动起名为arr_0, arr_1, ...,也可以使用关键字参数为数组起一个名字。输出文件扩展名为.npz,是一个压缩包,其中每个文件都是.npy,内含一个数组,文件名即内涵数组名。
41. 对于.npz文件,load函数自动识别并且返回一个类似于字典的对象,可以通过数组名作为关键字获取数组的内容。
42. savetxt和loadtxt分别存取txt文件,与tofile不同的是数字此时不是以二进制的方式保存的。用法:np.savetxt("a.txt", a, fmt="%d", delimiter=","),fmt默认为'%.18e',delimiter默认为' '。显然如果以非默认设置输出了读取时也要修改相应参数。
43. load和save函数还可以对已经打开的文件对象进行操作,比如:f = file("result.npy", "wb") np.save(f, a) f.close(),其中a是一个数组。读取时也是先定义一个句柄f,然后多次np.load(f),可以顺序(save时的顺序)读取数组。