Python 蟒蛇蟒蛇加入自己的名字?


标准安装的Python中用列表(list)保存一组值,可以用来当作数组使用,不过由于列表的元素可以是任何对象,因此列表中所保存的是对象的指 针。这样为了保存一个简单的[1,2,3],需要有3个指针和三个整数对象。对于数值运算来说这种结构显然比较浪费内存和CPU计算时间。
此外Python还提供了一个array模块,array对象和列表不同,它直接保存数值,和C语言的一维数组比较类似。但是由于它不支持多维,也没有各种运算函数,因此也不适合做数值运算。
NumPy的诞生弥补了这些不足,NumPy提供了两种基本的对象:ndarray(N-dimensional array object)和 ufunc(universal function
object)。ndarray(下文统一称之为数组)是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。喜欢的话关注收藏评论转发比心么么哒!Python学习交流企-鹅-群-719-139-688内有大量的项目开发和新手教学视频PDF书籍的千人大群等着你来加入
2.1 ndarray对象
函数库的导入
本书的示例程序假设用以下推荐的方式导入NumPy函数库:
import numpy as np
2.1.1 创建
首先需要创建数组才能对其进行其它操作。
我们可以通过给array函数传递Python的序列对象创建数组,如果传递的是多层嵌套的序列,将创建多维数组(下例中的变量c):
数组a的shape只有一个元素,因此它是一维数组。而数组c的shape有两个元素,因此它是二维数组,其中第0轴的长度为3,第1轴的长度为 4。还可以通过修改数组的shape属性,在保持数组元素个数不变的情况下,改变数组每个轴的长度。下面的例子将数组c的shape改为(4,3),注意
从(3,4)改为(4,3)并不是对数组进行转置,而只是改变每个轴的大小,数组元素在内存中的位置并没有改变:
当某个轴的元素为-1时,将根据数组元素的个数自动计算此轴的长度,因此下面的程序将数组c的shape改为了(2,6):
使用数组的reshape方法,可以创建一个改变了尺寸的新数组,原数组的shape保持不变:
数组a和d其实共享数据存储内存区域,因此修改其中任意一个数组的元素都会同时修改另外一个数组的内容:
数组的元素类型可以通过dtype属性获得。上面例子中的参数序列的元素都是整数,因此所创建的数组的元素类型也是整数,并且是32bit的长整型。可以通过dtype参数在创建时指定元素类型:
上面的例子都是先创建一个Python序列,然后通过array函数将其转换为数组,这样做显然效率不高。因此NumPy提供了很多专门用来创建数组的函数。下面的每个函数都有一些关键字参数,具体用法请查看函数说明。
arange函数类似于python的range函数,通过指定开始值、终值和步长来创建一维数组,注意数组不包括终值:
linspace函数通过指定开始值、终值和元素个数来创建一维数组,可以通过endpoint关键字指定是否包括终值,缺省设置是包括终值:
logspace函数和linspace类似,不过它创建等比数列,下面的例子产生1(10^0)到100(10^2)、有20个元素的等比数列:
此外,使用frombuffer, fromstring, fromfile等函数可以从字节序列创建数组,下面以fromstring为例:
Python的字符串实际上是字节序列,每个字符占一个字节,因此如果从字符串s创建一个8bit的整数数组的话,所得到的数组正好就是字符串中每个字符的ASCII编码:
如果从字符串s创建16bit的整数数组,那么两个相邻的字节就表示一个整数,把字节98和字节97当作一个16位的整数,它的值就是98*256+97 = 25185。可以看出内存中是以little endian(低位字节在前)方式保存数据的。
如果把整个字符串转换为一个64位的双精度浮点数数组,那么它的值是:
显然这个例子没有什么意义,但是可以想象如果我们用C语言的二进制方式写了一组double类型的数值到某个文件中,那们可以从此文件读取相应的数据,并通过fromstring函数将其转换为float64类型的数组。
我们可以写一个Python的函数,它将数组下标转换为数组中对应的值,然后使用此函数创建数组:
fromfunction函数的第一个参数为计算每个数组元素的函数,第二个参数为数组的大小(shape),因为它支持多维数组,所以第二个参数必须是一个序列,本例中用(10,)创建一个10元素的一维数组。
下面的例子创建一个二维数组表示九九乘法表,输出的数组a中的每个元素a[i, j]都等于func2(i, j):
2.1.2 存取元素
数组元素的存取方法和Python的标准方法相同:
和Python的列表序列不同,通过下标范围获取的新的数组是原始数组的一个视图。它与原始数组共享同一块数据空间:
除了使用下标范围存取元素之外,NumPy还提供了两种存取元素的高级方法。
使用整数序列
当使用整数序列对数组元素进行存取时,将使用整数序列中的每个元素作为下标,整数序列可以是列表或者数组。使用整数序列作为下标获得的数组不和原始数组共享数据空间。
使用布尔数组
当使用布尔数组b作为下标存取数组x中的元素时,将收集数组x中所有在数组b中对应下标为True的元素。使用布尔数组作为下标获得的数组不和原始数组共享数据空间,注意这种方式只对应于布尔数组,不能使用布尔列表。
布尔数组一般不是手工产生,而是使用布尔运算的ufunc函数产生,关于ufunc函数请参照 ufunc运算 一节。
2.1.3 多维数组
多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标。如图2.1所示,a为一个6x6的数组,图中用颜色区分了各个下标以及其对应的选择区域。
组元不需要圆括号
虽然我们经常在Python中用圆括号将组元括起来,但是其实组元的语法定义只需要用逗号隔开即可,例如 x,y=y,x 就是用组元交换变量值的一个例子。
如何创建这个数组
你也许会对如何创建a这样的数组感到好奇,数组a实际上是一个加法表,纵轴的值为0, 10, 20, 30, 40, 50;横轴的值为0, 1, 2, 3, 4, 5。纵轴的每个元素都和横轴的每个元素求和,就得到图中所示的数组a。你可以用下面的语句创建它,至于其原理我们将在后面的章节进行讨论:
多维数组同样也可以使用整数序列和布尔数组进行存取。
a[(0,1,2,3,4),(1,2,3,4,5)] : 用于存取数组的下标和仍然是一个有两个元素的组元,组元中的每个元素都是整数序列,分别对应数组的第0轴和第1轴。从两个序列的对应位置取出两个整数组成 下标: a[0,1], a[1,2], ..., a[4,5]。a[3:, [0, 2, 5]] :
下标中的第0轴是一个范围,它选取第3行之后的所有行;第1轴是整数序列,它选取第0, 2, 5三列。a[mask, 2] : 下标的第0轴是一个布尔数组,它选取第0,2,5行;第1轴是一个整数,选取第2列。
2.1.4 结构数组
在C语言中我们可以通过struct关键字定义结构类型,结构中的字段占据连续的内存空间,每个结构体占用的内存大小都相同,因此可以很容易地定义 结构数组。和C语言一样,在NumPy中也很容易对这种结构数组进行操作。只要NumPy中的结构定义和C语言中的定义相同,NumPy就可以很方便地读 取C语言的结构数组的二进制数据,转换为NumPy的结构数组。
假设我们需要定义一个结构数组,它的每个元素都有name, age和weight字段。在NumPy中可以如下定义:
我们先创建一个dtype对象persontype,通过其字典参数描述结构类型的各个字段。字典有两个关键字:names,formats。每个关键字对应的值都是一个列表。names定义结构中的每个字段名,而formats则定义每个字段的类型:
S32 : 32个字节的字符串类型,由于结构中的每个元素的大小必须固定,因此需要指定字符串的长度i : 32bit的整数类型,相当于np.int32f : 32bit的单精度浮点数类型,相当于np.float32
然后我们调用array函数创建数组,通过关键字参数 dtype=persontype, 指定所创建的数组的元素类型为结构persontype。运行上面程序之后,我们可以在IPython中执行如下的语句查看数组a的元素类型
这里我们看到了另外一种描述结构类型的方法: 一个包含多个组元的列表,其中形如 (字段名, 类型描述) 的组元描述了结构中的每个字段。类型描述前面为我们添加了 '|', '<' 等字符,这些字符用来描述字段值的字节顺序:
: 忽视字节顺序< : 低位字节在前> : 高位字节在前
结构数组的存取方式和一般数组相同,通过下标能够取得其中的元素,注意元素的值看上去像是组元,实际上它是一个结构:
2.1.5 内存结构
下面让我们来看看ndarray数组对象是如何在内存中储存的。如图2.3所示,关于数组的描述信息保存在一个数据结构中,这个结构引用两个对象:一块用于保存数据的存储区域和一个用于描述元素类型的dtype对象。
图2.3 ndarray数组对象在内存中的储存方式
数据存储区域保存着数组中所有元素的二进制数据,dtype对象则知道如何将元素的二进制数据转换为可用的值。数组的维数、大小等信息都保存在ndarray数组对象的数据结构中。图中显示的是如下数组的内存结构:
>>> a = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32)
strides中保存的是当每个轴的下标增加1时,数据存储区中的指针所增加的字节数。例如图中的strides为12,4,即第0轴的下标增加1 时,数据的地址增加12个字节:即a[1,0]的地址比a[0,0]的地址要高12个字节,正好是3个单精度浮点数的总字节数;第1轴下标增加1时,数据 的地址增加4个字节,正好是单精度浮点数的字节数。
如果strides中的数值正好和对应轴所占据的字节数相同的话,那么数据在内存中是连续存储的。然而数据并不一定都是连续储存的,前面介绍过通过下标范围得到新的数组是原始数组的视图,即它和原始视图共享数据存储区域:
由于数组b和数组a共享数据存储区,而b中的第0轴和第1轴都是数组a中隔一个元素取一个,因此数组b的strides变成了24,8,正好都是数组a的两倍。 对照前面的图很容易看出数据0和2的地址相差8个字节,而0和6的地址相差24个字节。
元素在数据存储区中的排列格式有两种:C语言格式和Fortan语言格式。在C语言中,多维数组的第0轴是最上位的,即第0轴的下标增加1时,元素 的地址增加的字节数最多;而Fortan语言的多维数组的第0轴是最下位的,即第0轴的下标增加1时,地址只增加一个元素的字节数。在NumPy中,元素
在内存中的排列缺省是以C语言格式存储的,如果你希望改为Fortan格式的话,只需要给数组传递order="F"参数:
>>> c = np.array([[0,1,2],[3,4,5],[6,7,8]], dtype=np.float32, order="F")>>> c.strides(4, 12)
2.2 ufunc运算
ufunc是universal function的缩写,它是一种能对数组的每个元素进行操作的函数。NumPy内置的许多ufunc函数都是在C语言级别实现的,因此它们的计算速度非常快。让我们来看一个例子:
先用linspace产生一个从0到2*PI的等距离的10个数,然后将其传递给sin函数,由于np.sin是一个ufunc函数,因此它对x中 的每个元素求正弦值,然后将结果返回,并且赋值给y。计算之后x中的值并没有改变,而是新创建了一个数组保存结果。如果我们希望将sin函数所计算的结果
直接覆盖到数组x上去的话,可以将要被覆盖的数组作为第二个参数传递给ufunc函数。例如::
sin函数的第二个参数也是x,那么它所做的事情就是对x中的每给值求正弦值,并且把结果保存到x中的对应的位置中。此时函数的返回值仍然是整个计算的结果,只不过它就是x,因此两个变量的id是相同的(变量t和变量x指向同一块内存区域)。
我用下面这个小程序,比较了一下numpy.math和Python标准库的math.sin的计算速度::
在我的电脑上计算100万次正弦值,numpy.sin比math.sin快10倍多。这得利于numpy.sin在C语言级别的循环计算。 numpy.sin同样也支持对单个数值求正弦,例如:numpy.sin(0.5)。不过值得注意的是,对单个数的计算math.sin则比 numpy.sin快得多了,让我们看下面这个测试程序:
请注意numpy.sin的计算速度只有math.sin的1/5。这是因为numpy.sin为了同时支持数组和单个值的计算,其C语言的内部实 现要比math.sin复杂很多,如果我们同样在Python级别进行循环的话,就会看出其中的差别了。此外,numpy.sin返回的数的类型和
math.sin返回的类型有所不同,math.sin返回的是Python的标准float类型,而numpy.sin则返回一个 numpy.float64类型:
通过上面的例子我们了解了如何最有效率地使用math库和numpy库中的数学函数。因为它们各有长短,因此在导入时不建议使用*号全部载入,而是应该使用import numpy as np的方式载入,这样我们可以根据需要选择合适的函数调用。
NumPy中有众多的ufunc函数为我们提供各式各样的计算。除了sin这种单输入函数之外,还有许多多个输入的函数,add函数就是一个最常用的例子。先来看一个例子:
add函数返回一个新的数组,此数组的每个元素都为两个参数数组的对应元素之和。它接受第3个参数指定计算结果所要写入的数组,如果指定的话,add函数就不再产生新的数组。
由于Python的操作符重载功能,计算两个数组相加可以简单地写为a+b,而np.add(a,b,a)则可以用a+=b来表示。下面是数组的运 算符和其对应的ufunc函数的一个列表,注意除号"/"的意义根据是否激活__future__.pi...
}

我要回帖

更多关于 世界上最大的蛇叫什么名字 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信