- Python科学计算(第2版)
- 张若愚
- 1540字
- 2025-03-09 06:39:26
2.1.3 自动生成数组
前面的例子都是先创建一个Python的序列对象,然后通过array()将其转换为数组,这样做显然效率不高。因此NumPy提供了很多专门用于创建数组的函数。下面的每个函数都有一些关键字参数,具体用法请查看函数说明。
arange()类似于内置函数range(),通过指定开始值、终值和步长来创建表示等差数列的一维数组,注意所得到的结果中不包含终值。例如下面的程序创建开始值为0、终值为1、步长为0.1的等差数组,注意终值1不在数组中:
np.arange(0, 1, 0.1) array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
linspace()通过指定开始值、终值和元素个数来创建表示等差数列的一维数组,可以通过endpoint参数指定是否包含终值,默认值为True,即包含终值。下面两个例子分别演示了endpoint为True和False时的结果,注意endpoint的值会改变数组的等差步长:

np.linspace(0, 1, 10, endpoint=False) # 步长为1/10 array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
logspace()和linspace()类似,不过它所创建的数组是等比数列。下面的例子产生从100到102、有5个元素的等比数列,注意起始值0表示100,而终值2表示102:

基数可以通过base参数指定,其默认值为10。下面通过将base参数设置为2,并设置endpoint参数为False,创建一个比例为21/12的等比数组,此等比数组的比值是音乐中相差半音的两个音阶之间的频率比值,因此可以用它计算一个八度中所有半音的频率:

zeros()、ones()、empty()可以创建指定形状和类型的数组。其中empty()只分配数组所使用的内存,不对数组元素进行初始化操作,因此它的运行速度是最快的。下面的程序创建一个形状为(2,3)、元素类型为整数的数组,注意其中的元素值没有被初始化:
np.empty((2,3), np.int) array([[1078523331, 1065353216, 1073741824], [1077936128, 1082130432, 1084227584]])
而zeros()将数组元素初始化为0,ones()将数组元素初始化为1。下面创建一个长度为4、元素类型为整数的一维数组,并且元素全部被初始化为0:
np.zeros(4, np.int) array([0, 0, 0, 0])
full()将数组元素初始化为指定的值:
np.full(4, np.pi) array([ 3.14159265, 3.14159265, 3.14159265, 3.14159265])
此外,zeros_like()、ones_like()、empty_like()、full_like()等函数创建与参数数组的形状和类型相同的数组,因此zeros_like(a)和zeros(a.shape, a.dtype)的效果相同。
frombuffer()、fromstring()、fromfile()等函数可以从字节序列或文件创建数组。下面以fromstring()为例介绍它们的用法,先创建含8个字符的字符串s:
s = "abcdefgh"
Python的字符串实际上是一个字节序列,每个字符占一个字节。因此如果从字符串s创建一个8位的整数数组,所得到的数组正好就是字符串中每个字符的ASCII编码:
np.fromstring(s, dtype=np.int8) array([ 97, 98, 99, 100, 101, 102, 103, 104], dtype=int8)
如果从字符串s创建16位的整数数组,那么两个相邻的字节就表示一个整数,把字节98和字节97当作一个16位的整数,它的值就是98*256+97=25185。可以看出,16位的整数是以低位字节在前(little-endian)的方式保存在内存中的。
print 98*256+97 np.fromstring(s, dtype=np.int16) 25185 array([25185, 25699, 26213, 26727], dtype=int16)
如果把整个字符串转换为一个64位的双精度浮点数数组,那么它的值是:
np.fromstring(s, dtype=np.float) array([ 8.54088322e+194])
显然这个结果没有什么意义,但是如果我们用C语言的二进制方式写了一组double类型的数值到某个文件中,那就可以从此文件读取相应的数据,并通过fromstring()将其转换为float64类型的数组,或者直接使用fromfile()从二进制文件读取数据。
fromstring()会对字符串的字节序列进行复制,而使用frombuffer()创建的数组与原始字符串共享内存。由于字符串是只读的,因此无法修改所创建的数组的内容:
buf = np.frombuffer(s, dtype=np.int16) buf[1] = 10 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-52-f523db231ae5> in <module>() 1 buf = np.frombuffer(s, dtype=np.int16) ----> 2 buf[1] = 10 ValueError: assignment destination is read-only
Python中还有一些类型也支持buffer接口,例如bytearray、array.array等。在后面的章节中,我们会介绍如何使用这些对象实现动态数组的功能。
还可以先定义一个从下标计算数值的函数,然后用fromfunction()通过此函数创建数组:
def func(i): return i % 4 + 1 np.fromfunction(func, (10,)) array([ 1., 2., 3., 4., 1., 2., 3., 4., 1., 2.])
fromfunction()的第一个参数是计算每个数组元素的函数,第二个参数指定数组的形状。因为它支持多维数组,所以第二个参数必须是一个序列。上例中第二个参数是长度为1的元组(10,),因此创建了一个有10个元素的一维数组。
下面的例子创建一个表示九九乘法表的二维数组,输出的数组a中的每个元素a[i, j]都等于func2(i, j):
def func2(i, j): return (i + 1) * (j + 1) np.fromfunction(func2, (9,9)) array([[ 1., 2., 3., 4., 5., 6., 7., 8., 9.], [ 2., 4., 6., 8., 10., 12., 14., 16., 18.], [ 3., 6., 9., 12., 15., 18., 21., 24., 27.], [ 4., 8., 12., 16., 20., 24., 28., 32., 36.], [ 5., 10., 15., 20., 25., 30., 35., 40., 45.], [ 6., 12., 18., 24., 30., 36., 42., 48., 54.], [ 7., 14., 21., 28., 35., 42., 49., 56., 63.], [ 8., 16., 24., 32., 40., 48., 56., 64., 72.], [ 9., 18., 27., 36., 45., 54., 63., 72., 81.]])