Numpy模块

导入

import numpy as np

创建

通过Python列表

直接传入1层,2层嵌套列表,变为1维,2维数组

a = np.array([1,2,3,4])

b = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])

通常,我们无法事先知道数组元素的具体值,但是数组大小是已知的。 这时可以用下面几种方法生成数组。

zeros 函数生成元素全部为0的数组。

ones函数生成元素全部为1的数组。

empty函数生成元素没有赋值的数组,这时元素值由内存中原来的内容决定。 默认生成的数组元素类型为float64.

>>> np.zeros( (3,4) )
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> np.ones( (2,3,4), dtype=int16 ) # dtype can also be specified
array([[[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1]],
[[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1]]], dtype=int16)
>>> np.empty( (2,3) )
array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],
[ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])

属性

ndarray.ndim:数组的维数,也称为rank

ndarray.shape:数组各维的大小tuple 类型,对一个n 行m 列的矩阵来说, shape 为 (n,m)。

ndarray.size:元素的总数。

ndarray.dtype:每个元素的类型,可以是 numpy.int32, numpy.int16, and numpy.float64 等。

ndarray.itemsize:每个元素占用的字节数。

ndarray.data:指向数据内存。

长度

访问shape属性得到数组长度

a.shape #(4,) #一维

b.shape #(4,3) #二维

修改shape属性修改数组长度

b.shape = 3,4

#b = array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])

#当某个轴的元素为-1时,长度将自动计算

b.shape = 2,-1 #将计算为2x6

#b = array([[1,2],[3,4],[5,6],[7,8],[9,10],[11,12]])

#注意修改时元素总个数不能变 3x4 = 4x3 = 2x6

使用reshape创建新数组,原数组的shape不变

c = a.reshape((2,2))

#c = array([[1,2],[3,4]])

#a = array([1,2,3,4])

#注意,a和c指向相同地址空间,修改其中任意一个,另一个也会改变

a[1] = 100

#a = array([1,100,3,4])

#c = array([[1,100],[3,4]])

类型

dtype 属性可以获得数组元素的类型,该属性也可以在创建数组时指定

d = np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]], dtype=np.float)

#d = array([[1., 2., 3., 4.],[4., 5., 6., 7.],[7., 8., 9., 10.]])

其他创建方法

arange()方法

指定开始值,终值和步长创建1维数组(注意不包括终值)

e = np.arange(0,1,0.1) #开始为0,结束为1(不包括1),每0.1算一个

#e = array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

linspace()方法

指定开始值,终值和元素个数创建1维数组,等差(可以用endpoint指定包不包括终值,默认包括)

f = np.linspace(0, 1, 12)
#f = array([ 0. , 0.09090909, 0.18181818, 0.27272727, 0.36363636,
0.45454545, 0.54545455, 0.63636364, 0.72727273, 0.81818182,
0.90909091, 1. ])

logspace()方法

logspace函数和linspace类似,不过它创建等比数列,下面的例子产生1(10的0次)到100(10的2次)、有20个元素的等比数列

g = np.logspace(0, 2, 20)
#g = array([1. , 1.27427499, 1.62377674, 2.06913808,
2.6366509 , 3.35981829, 4.2813324 , 5.45559478,
6.95192796 , 8.8586679 , 11.28837892, 14.38449888,
18.32980711, 23.35721469, 29.76351442, 37.92690191,
48.32930239, 61.58482111, 78.47599704, 100. ])

字节方法

frombuffer(), fromstring(), fromfile()等函数可以从字节序列创建数组,以fromstring为例:

s = "abcdefgh"

Python的字符串实际上是字节序列,每个字符占一个字节,因此如果从字符串s创建一个8bit的整数数组的话,所得到的数组正好就是字符串中每个字符的ASCII编码:

h = np.fromstring(s, dtype=np.int8)

#h = array([ 97, 98, 99, 100, 101, 102, 103, 104], dtype=int8)

如果从字符串s创建16bit的整数数组,那么两个相邻的字节就表示一个整数,把字节98和字节97当作一个16位的整数,它的值就是98*256+97 = 25185。可以看出内存中是以little endian(低位字节在前)方式保存数据的。

j = np.fromstring(s, dtype=np.int16)

#j= array([25185, 25699, 26213, 26727], dtype=int16)

如果把整个字符串转换为一个64位的双精度浮点数数组,那么它的值是:

k = np.fromstring(s, dtype=np.float)

#k = array([ 8.54088322e+194])

显然这个例子没有什么意义,但是可以想象如果我们用C语言的二进制方式写了一组double类型的数值到某个文件中,那们可以从此文件读取相应的数据,并通过fromstring函数将其转换为float64类型的数组。

函数方法

我们可以写一个Python的函数,它将数组下标转换为数组中对应的值,然后使用此函数创建数组(传入下标,计算,传出元素值):

fromfunction函数的第一个参数为计算函数,第二个参数为数组的大小(shape),因为它支持多维数组,所以第二个参数必须是一个序列,本例中用(10,)创建一个10元素的一维数组。

def func(i):
return i%4+1
l = np.fromfunction(func, (10,)) #下标0~9
#l = array([ 1., 2., 3., 4., 1., 2., 3., 4., 1., 2.])

下面的例子创建一个二维数组表示九九乘法表,输出的数组a中的每个元素a[i, j]都等于func2(i, j):

def func2(i, j):
return (i+1) * (j+1)
m = np.fromfunction(func2, (9,9))
#m = 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.]])

存取(一维)

切片

数组元素存取和python切片操作一致

注意:通过下标范围获取的新的数组(b = a[3:7])是原始数组的一个视图。它与原始数组共享同一块数据空间(一个改变,都改变)

其他存取方法

使用整数序列

使用整数序列(列表或者数组)中的每个元素作为下标。这样获得的新数组不和原始数组共享数据空间。

x = np.arange(10,1,-1)
#x = array([10, 9, 8, 7, 6, 5, 4, 3, 2])
x[[3, 3, 1, 8]] # 获取x中的下标为3, 3, 1, 8的4个元素,组成一个新的数组
#array([7, 7, 9, 2])
b = x[np.array([3,3,-3,8])] #下标可以是负数
b[2] = 100
#b = array([7, 7, 100, 2])
#x = array([10, 9, 8, 7, 6, 5, 4, 3, 2]) x不变
x[[3,5,1]] = -1, -2, -3 # 整数序列下标也可以用来修改元素的值
#x = array([10, -3, 8, -1, 6, -2, 4, 3, 2])

使用布尔数组

当使用布尔数组b作为下标存取数组x中的元素时,将收集数组x中所有在数组b中对应下标为True的元素。使用布尔数组作为下标获得的数组不和原始数组共享数据空间,注意只对应于布尔数组,不能使用布尔列表。(取True位置的元素)

x = np.arange(5,0,-1)

#x = array([5, 4, 3, 2, 1])

x[np.array([True, False, True, False, False])]

# 布尔数组中下标为0,2的元素为True,因此获取x中下标为0,2的元素

#array([5, 3])

x[[True, False, True, False, False]]

# 如果是布尔列表,则把True当作1, False当作0,按照整数序列方式获取x中的元素

#array([4, 5, 4, 5, 5])

x[np.array([True, False, True, True])]

# 布尔数组的长度不够时,不够的部分都当作False

#array([5, 3, 2])

x[np.array([True, False, True, True])] = -1, -2, -3

# 布尔数组下标也可以用来修改元素

#x = array([-1, 4, -2, -3, 1])

布尔数组一般不是手工产生,而是使用布尔运算的ufunc函数产生。

x = np.random.rand(10) # 产生一个长度为10,元素值为0-1的随机数的数组

#x = array([ 0.72223939, 0.921226 , 0.7770805 , 0.2055047 , 0.17567449,

0.95799412, 0.12015178, 0.7627083 , 0.43260184, 0.91379859])

x>0.5

# 数组x中的每个元素和0.5进行大小比较,得到一个布尔数组,True表示x中对应的值大于0.5

#array([ True, True, True, False, False, True, False, True, False, True], dtype=bool)

x[x>0.5]

# 使用x>0.5返回的布尔数组收集x中的元素,因此得到的结果是x中所有大于0.5的元素的数组

#array([ 0.72223939, 0.921226, 0.7770805, 0.95799412, 0.7627083, 0.91379859])

二维数组

注意:axis第0轴为纵y轴,第1轴为横x轴

多维数组访问

与一维类似,多维用,分割选取行,列,注意不要用'()'

a为6x6数组

a[0,3:5] #第0行,第3,4列的交叉元素

a[4:,4:] #第4,5行,第4,5列的交叉元素

a[:,2] #全部行,第2列的交叉元素

a[2::2,::2] #第2,4行,第0,2,4列的交叉元素

同样也可用数组和布尔方法进行存取

a[(0,1,2,3,4),(1,2,3,4,5)] #(0,1),(1,2),(2,3),(3,4),(4,5)这五个位置的元素

a[3:,[0,2,5]] #第3,4,5行,第0,2,5列的交叉元素

mask = np.array([1,0,1,0,0,1],dtype=np.bool)

a[mask,2] #第0,2,5行,第2列的交叉元素

多维数组迭代

多维数组迭代时以第一个维度为迭代单位:

a = array([[0 ,1 ,2 ,3] ,
[10 ,11 ,12 ,13] ,
[20, 21 ,22 ,23] ,
[30, 31, 32 ,33] ,
[40, 41 ,42 ,43] ])
>>> for row in b:
print (row)
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

如果我们想忽略维度,将所有元素迭代出来也是可以的

>>> for element in b.flat:
print (element)
0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43

ufunc运算

能对数组每个元素都进行操作

x = np.linspace(0, 2*np.pi, 10)

# 对数组x中的每个元素进行正弦计算,返回一个同样大小的新数组

y = np.sin(x)
#y = array([0.00000000e+00, 6.42787610e-01, 9.84807753e-01,
8.66025404e-01, 3.42020143e-01, -3.42020143e-01,
-8.66025404e-01, -9.84807753e-01, -6.42787610e-01,
-2.44921271e-16])

计算之后x中的值并没有改变,而是新创建了一个数组保存结果。如果我们希望将sin函数所计算的结果直接覆盖到数组x上去的话,可以将要被覆盖的数组作为第二个参数传递给ufunc函数。

t = np.sin(x,x)
#x = array([0.00000000e+00, 6.42787610e-01, 9.84807753e-01,
8.66025404e-01, 3.42020143e-01, -3.42020143e-01,
-8.66025404e-01, -9.84807753e-01, -6.42787610e-01,
-2.44921271e-16])
#id(t) == id(x)

np.sin()同时支持数组和单个运算

但对于数组,np.sin()的速度很快,但对单个元素,np.sin()的速度还不如Python中的math.sin()

Python中可以用符号代替计算函数

y = x1 + x2: add(x1, x2 [, y])
y = x1 - x2: subtract(x1, x2 [, y])
y = x1 * x2: multiply (x1, x2 [, y])
y = x1 / x2: divide (x1, x2 [, y]), 如果两个数组的元素为整数,那么用整数除法
y = x1 / x2: true divide (x1, x2 [, y]), 总是返回精确的商
y = x1 // x2: floor divide (x1, x2 [, y]), 总是对返回值取整
y = -x: negative(x [,y])
y = x1**x2: power(x1, x2 [, y])
y = x1 % x2: remainder(x1, x2 [, y]), mod(x1, x2, [, y])

自己创建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

显然triangle_wave函数只能计算单个数值,不能对数组直接进行处理。我们可以用下面的方法先使用列表包容(List comprehension),计算出一个list,然后用array函数将列表转换为数组:

x = np.linspace(0, 2, 1000)

y = np.array([triangle_wave(t, 0.6, 0.4, 1.0) for t in x])

这种做法每次都需要使用列表包容语法调用函数,对于多维数组是很麻烦的。我们用frompyfunc函数来解决这个问题:

triangle_ufunc = np.frompyfunc( lambda x: triangle_wave(x, 0.6, 0.4, 1.0), 1, 1)

y2 = triangle_ufunc(x)

frompyfunc的调用格式为frompyfunc(func, nin, nout),其中func是计算单个元素的函数,nin是此函数的输入参数的个数,nout是此函数的返回值的个数。

虽然triangle_wave函数有4个参数,但是由于后三个c, c0, hc在整个计算中值都是固定的,因此所产生的ufunc函数其实只有一个参数。为了满足这个条件,我们用一个lambda函数对triangle_wave的参数进行一次包装。这样传入frompyfunc的函数就只有一个参数了。这样子做,效率并不是太高,另外还有一种方法:

def triangle_func(c, c0, hc):
def trifunc(x):
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

# 用trifunc函数创建一个ufunc函数,可以直接对数组进行计算, 不过通过此函数

# 计算得到的是一个Object数组,需要进行类型转换

return np.frompyfunc(trifunc, 1, 1)

y2 = triangle_func(0.6, 0.4, 1.0)(x)

我们通过函数triangle_func包装三角波的三个参数,在其内部定义一个计算三角波的函数trifunc,trifunc函数在调用时会采用triangle_func的参数进行计算。最后triangle_func返回用frompyfunc转换结果。

值得注意的是用frompyfunc得到的函数计算出的数组元素的类型为object,因为frompyfunc函数无法保证Python函数返回的数据类型都完全一致。因此还需要再次 y2.astype(np.float64)将其转换为双精度浮点数组

print(type(y2))
#object
y3 = y2.astype(np.float64)
print(type(y3))
#np.float64

广播

两个数组计算时,要求这两个数组有相同的大小(shape相同)。如果两个数组的shape不同的话,会进行如下的广播(broadcasting)处理:

处理规则

1.让所有输入数组都向其中shape最长的数组看齐,shape中不足的部分都通过在前面加1补齐

2.输出数组的shape是输入数组shape的各个轴上的最大值

3.如果输入数组的某个轴和输出数组的对应轴的长度相同或者其长度为1时,这个数组能够用来计算,否则出错

4.当输入数组的某个轴的长度为1时,沿着此轴运算时都用此轴上的第一组值

例子

先创建一个二维数组a,其shape为(6,1):

a = np.arange(0, 60, 10).reshape(-1, 1)

#a = array([[ 0], [10], [20], [30], [40], [50]])

#a.shape = (6, 1)

再创建一维数组b,其shape为(5,):

b = np.arange(0, 5)

#b = array([0, 1, 2, 3, 4])

#b.shape = (5,)

计算a和b的和,得到一个加法表,它相当于计算a,b中所有元素组的和,得到一个shape为(6,5)的数组:

c = a + b
#c = 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]])
#c.shape = (6, 5)

由于a和b的shape长度(也就是ndim属性)不同,根据规则1,需要让b的shape向a对齐(维数对齐),于是将b的shape前面加1,补齐为(1,5)。相当于做了如下计算:

b.shape=1,5

#b = array([[0, 1, 2, 3, 4]])

这样加法运算的两个输入数组的shape分别为(6,1)和(1,5),根据规则2,输出数组的各个轴的长度为输入数组各个轴上的长度的最大值,可知输出数组的shape为(6,5)。

由于b的第0轴上的长度为1,而a的第0轴上的长度为6,因此为了让它们在第0轴上能够相加,需要将b在第0轴上的长度扩展为6,这相当于:

b = b.repeat(6,axis=0) #复制第0轴(纵向↓)
#b = array([[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]])

由于a的第1轴的长度为1,而b的第一轴长度为5,因此为了让它们在第1轴上能够相加,需要将a在第1轴上的长度扩展为5,这相当于:

a = a.repeat(5, axis=1) #复制第1轴(横轴→)
#a = array([[ 0, 0, 0, 0, 0],
[10, 10, 10, 10, 10],
[20, 20, 20, 20, 20],
[30, 30, 30, 30, 30],
[40, 40, 40, 40, 40],
[50, 50, 50, 50, 50]])

经过上述处理之后,a和b就可以按对应元素进行相加运算了。

ogrid对象

numpy提供了一个快速产生一对数组的方法: ogrid对象:

x,y = np.ogrid[0:5,0:5]
#x = array([[0],
[1],
[2],
[3],
[4]])
#y = array([[0, 1, 2, 3, 4]])

ogrid是一个很有趣的对象,它像一个多维数组一样,用切片组元作为下标进行存取,返回的是一组可以用来广播计算的数组。其切片下标有两种形式:

1.开始值:结束值:步长,和np.arange(开始值, 结束值, 步长)类似

2.开始值:结束值:长度j,当第三个参数为虚数时,它表示返回的数组的长度,和np.linspace(开始值, 结束值, 长度)类似:

x, y = np.ogrid[0:1:4j, 0:1:3j]
#x = array([[ 0. ],
[ 0.33333333],
[ 0.66666667],
[ 1. ]])
#y = array([[ 0. , 0.5, 1. ]])

ufuns方法

reduce方法

reduce 方法沿着axis轴对array进行操作,相当于将运算符插入到沿axis轴的所有子数组或者元素当中。

一维

>>> np.add.reduce([1,2,3]) # 1 + 2 + 3

6

二维

>>> np.add.reduce([[1,2,3],[4,5,6]], axis=0) # 1+4, 2+5, 3+6
array([5, 7, 9])
>>> np.add.reduce([[1,2,3],[4,5,6]], axis=1) # 1,4 + 2,5 + 3,6
array([6, 15])

accumulate 方法

和reduce方法类似,只是它返回的数组和输入的数组的shape相同,保存所有的中间计算结果:

一维

>>> np.add.accumulate([1,2,3]) # 1 + 2 + 3

array([1, 3, 6])

二维

>>> np.add.accumulate([[1,2,3],[4,5,6]], axis=0) # 1+4, 2+5, 3+6
array([[ 1, 3, 6],
[ 5, 7, 9]])
>>> np.add.accumulate([[1,2,3],[4,5,6]], axis=1) # 1,4 + 2,5 + 3,6
array([[ 1, 3, 6],
[ 4, 9, 15]])

reduceat 方法

reduceat 方法通过indices参数指定一系列reduce的起始和终了位置

对于indices中的每个元素都会调用reduce函数计算出一个值来,因此最终计算结果的长度和indices的长度相同。结果result数组中除最后一个元素之外,都按照如下计算得出:

最后一个元素前面的元素

if indices[i] < indices[i+1]:
result[i] = np.reduce(a[indices[i]:indices[i+1]])
else:
result[i] = a[indices[i]]

最后一个元素如下计算:

np.reduce(a[indices[-1]:])

例子

>>> a = np.array([1,2,3,4])
>>> result = np.add.reduceat(a,indices=[0,1,0,2,0,3,0])
>>> result
array([ 1, 2, 3, 3, 6, 4, 10]

结果的每个元素如下计算而得:

1 : a[0] = 1 result[0] = np.add.reduce(a[0:1]) = a[0]
2 : a[1] = 2 result[0] = a[1]
3 : a[0] + a[1] = 1 + 2 result[2] = np.add.reduce(a[0:2]) = a[0] + a[1]
3 : a[2] = 3 result[0] = a[2]
6 : a[0] + a[1] + a[2] = 1 + 2 + 3 = 6 result[4] = np.add.reduce(a[0:3]) = a[0] + a[1] + a[2]
4 : a[3] = 4 result[0] = a[3]
10: a[0] + a[1] + a[2] + a[4] = 1+2+3+4 = 10 result[0] = np.add.reduce(a[0:]) = a[0] + a[1] + a[2] + a[4]

outer 方法

outer 方法,.outer(a,b)方法的计算等同于如下程序:

例子

>>> np.multiply.outer([1,2,3,4,5],[2,3,4])
array([[ 2, 3, 4],
[ 4, 6, 8],
[ 6, 9, 12],
[ 8, 12, 16],
[10, 15, 20]])

outer方法相当于对a,b进行如下操作

>>> a.shape += (1,)*b.ndim

#a.shape = (5,) b.ndim=1(b的维数) (1,)*1=(1,) (5,)+(1,) = (5,1)

#进行完这步后a = array([[1],

[2],
[3],
[4],
[5]])
>>> (a,b)
#a*b
>>> a = a.squeeze()

#squeeze的功能是剔除数组a中长度为1的轴。

如果将这两个数组按照等同程序一步一步的计算的话,就会发现乘法表最终是通过广播的方式计算出来的。

文件读取

文件存取的格式分为两类:二进制和文本。而二进制格式的文件又分为NumPy专用的格式化二进制类型和无格式类型。

tofile,fromfile方法

使用数组的方法函数tofile可以方便地将数组中数据以二进制的格式写进文件。tofile输出的数据没有格式,因此用numpy.fromfile读回来的时候需要自己格式化数据(设定shape和dtype),并且tofile函数不管数组的排列顺序是C语言格式的还是Fortran语言格式的,统一使用C语言格式输出。:

>>> a = np.arange(0,12)
>>> a.shape = 3,4
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> a.tofile("a.bin")
>>> b = np.fromfile("a.bin", dtype=np.float) # 按照float类型读入数据
>>> b # 读入的数据是错误的
array([ 2.12199579e-314, 6.36598737e-314, 1.06099790e-313,
1.48539705e-313, 1.90979621e-313, 2.33419537e-313])
>>> a.dtype # 查看a的dtype
dtype('int32')
>>> b = np.fromfile("a.bin", dtype=np.int32) # 按照int32类型读入数据
>>> b # 数据是一维的
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> b.shape = 3, 4 # 按照a的shape修改b的shape
>>> b # 这次终于正确了
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

此外如果fromfile和tofile函数调用时指定了sep关键字参数的话,数组将以文本格式输入输出。

load,save方法

numpy.load和numpy.save函数以NumPy专用的二进制类型保存数据,会自动处理dtype和shape等信息,但是numpy.save输出的文件很难和其它语言编写的程序读入:

>>> np.save("a.npy", a)
>>> c = np.load( "a.npy" )
>>> c
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

如果你想将多个数组保存到一个文件中的话,可以使用numpy.savez函数。savez函数的第一个参数是文件名,其后的参数都是需要保存的数组,也可以使用关键字参数为数组起一个名字,非关键字参数传递的数组会自动起名为arr_0, arr_1, ...。savez函数输出的是一个压缩文件(扩展名为npz),其中每个文件都是一个save函数保存的npy文件,文件名对应于数组名。load函数自动识别npz文件,并且返回一个类似于字典的对象,可以通过数组名作为关键字获取数组的内容:

>>> a = np.array([[1,2,3],[4,5,6]])
>>> b = np.arange(0, 1.0, 0.1)
>>> c = np.sin(b)
>>> np.savez("result.npz", a, b, sin_array = c)
>>> r = np.load("result.npz")
>>> r["arr_0"] # 数组a
array([[1, 2, 3],
[4, 5, 6]])
>>> r["arr_1"] # 数组b
array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
>>> r["sin_array"] # 数组c
array([ 0. , 0.09983342, 0.19866933, 0.29552021, 0.38941834,
0.47942554, 0.56464247, 0.64421769, 0.71735609, 0.78332691])

如果你用解压软件打开result.npz文件的话,会发现其中有三个文件:arr_0.npy, arr_1.npy, sin_array.npy,其中分别保存着数组a, b, c的内容。

使用numpy.savetxt和numpy.loadtxt可以读写1维和2维的数组:

>>> a = np.arange(0,12,0.5).reshape(4,-1)
>>> np.savetxt("a.txt", a) # 缺省按照'%.18e'格式保存数据,以空格分隔
>>> np.loadtxt("a.txt")
array([[ 0. , 0.5, 1. , 1.5, 2. , 2.5],
[ 3. , 3.5, 4. , 4.5, 5. , 5.5],
[ 6. , 6.5, 7. , 7.5, 8. , 8.5],
[ 9. , 9.5, 10. , 10.5, 11. , 11.5]])
>>> np.savetxt("a.txt", a, fmt="%d", delimiter=",") #改为保存为整数,以逗号分隔
>>> np.loadtxt("a.txt",delimiter=",") # 读入的时候也需要指定逗号分隔
array([[ 0., 0., 1., 1., 2., 2.],
[ 3., 3., 4., 4., 5., 5.],
[ 6., 6., 7., 7., 8., 8.],
[ 9., 9., 10., 10., 11., 11.]])

文件名和文件对象

本节介绍所举的例子都是传递的文件名,也可以传递已经打开的文件对象,例如对于load和save函数来说,如果使用文件对象的话,可以将多个数组储存到一个npy文件中:

>>> a = np.arange(8)
>>> b = np.add.accumulate(a)
>>> c = a + b
>>> f = open("result.npy", "wb")
>>> np.save(f, a) # 顺序将a,b,c保存进文件对象f
>>> np.save(f, b)
>>> np.save(f, c)
>>> f.close()
>>> f = open("result.npy", "rb")
>>> np.load(f) # 顺序从文件对象f中读取内容
array([0, 1, 2, 3, 4, 5, 6, 7])
>>> np.load(f)
array([ 0, 1, 3, 6, 10, 15, 21, 28])
>>> np.load(f)
array([ 0, 2, 5, 9, 14, 20, 27, 35])