数组读写

从文本中读取数组

In [1]:

  1. import numpy as np

空格(制表符)分割的文本

假设我们有这样的一个空白分割的文件:

In [2]:

  1. %%writefile myfile.txt
  2. 2.1 2.3 3.2 1.3 3.1
  3. 6.1 3.1 4.2 2.3 1.8
  1. Writing myfile.txt

为了生成数组,我们首先将数据转化成一个列表组成的列表,再将这个列表转换为数组:

In [3]:

  1. data = []
  2.  
  3. with open('myfile.txt') as f:
  4. # 每次读一行
  5. for line in f:
  6. fileds = line.split()
  7. row_data = [float(x) for x in fileds]
  8. data.append(row_data)
  9.  
  10. data = np.array(data)

In [4]:

  1. data

Out[4]:

  1. array([[ 2.1, 2.3, 3.2, 1.3, 3.1],
  2. [ 6.1, 3.1, 4.2, 2.3, 1.8]])

不过,更简便的是使用 loadtxt 方法:

In [5]:

  1. data = np.loadtxt('myfile.txt')
  2. data

Out[5]:

  1. array([[ 2.1, 2.3, 3.2, 1.3, 3.1],
  2. [ 6.1, 3.1, 4.2, 2.3, 1.8]])

逗号分隔文件

In [6]:

  1. %%writefile myfile.txt
  2. 2.1, 2.3, 3.2, 1.3, 3.1
  3. 6.1, 3.1, 4.2, 2.3, 1.8
  1. Overwriting myfile.txt

对于逗号分隔的文件(通常为.csv格式),我们可以稍微修改之前繁琐的过程,将 split 的参数变成 ','即可。

不过,loadtxt 函数也可以读这样的文件,只需要制定分割符的参数即可:

In [7]:

  1. data = np.loadtxt('myfile.txt', delimiter=',')
  2. data

Out[7]:

  1. array([[ 2.1, 2.3, 3.2, 1.3, 3.1],
  2. [ 6.1, 3.1, 4.2, 2.3, 1.8]])

loadtxt 函数

  1. loadtxt(fname, dtype=<type 'float'>,
  2. comments='#', delimiter=None,
  3. converters=None, skiprows=0,
  4. usecols=None, unpack=False, ndmin=0)

loadtxt 有很多可选参数,其中 delimiter 就是刚才用到的分隔符参数。

skiprows 参数表示忽略开头的行数,可以用来读写含有标题的文本

In [8]:

  1. %%writefile myfile.txt
  2. X Y Z MAG ANG
  3. 2.1 2.3 3.2 1.3 3.1
  4. 6.1 3.1 4.2 2.3 1.8
  1. Overwriting myfile.txt

In [9]:

  1. np.loadtxt('myfile.txt', skiprows=1)

Out[9]:

  1. array([[ 2.1, 2.3, 3.2, 1.3, 3.1],
  2. [ 6.1, 3.1, 4.2, 2.3, 1.8]])

此外,有一个功能更为全面的 genfromtxt 函数,能处理更多的情况,但相应的速度和效率会慢一些。

  1. genfromtxt(fname, dtype=<type 'float'>, comments='#', delimiter=None,
  2. skiprows=0, skip_header=0, skip_footer=0, converters=None,
  3. missing='', missing_values=None, filling_values=None, usecols=None,
  4. names=None, excludelist=None, deletechars=None, replace_space='_',
  5. autostrip=False, case_sensitive=True, defaultfmt='f%i', unpack=None,
  6. usemask=False, loose=True, invalid_raise=True)

loadtxt 的更多特性

对于这样一个文件:

In [10]:

  1. %%writefile myfile.txt
  2. -- BEGINNING OF THE FILE
  3. % Day, Month, Year, Skip, Power
  4. 01, 01, 2000, x876, 13 % wow!
  5. % we don't want have Jan 03rd
  6. 04, 01, 2000, xfed, 55
  1. Overwriting myfile.txt

In [11]:

  1. data = np.loadtxt('myfile.txt',
  2. skiprows=1, #忽略第一行
  3. dtype=np.int, #数组类型
  4. delimiter=',', #逗号分割
  5. usecols=(0,1,2,4), #指定使用哪几列数据
  6. comments='%' #百分号为注释符
  7. )
  8. data

Out[11]:

  1. array([[ 1, 1, 2000, 13],
  2. [ 4, 1, 2000, 55]])

loadtxt 自定义转换方法

In [12]:

  1. %%writefile myfile.txt
  2. 2010-01-01 2.3 3.2
  3. 2011-01-01 6.1 3.1
  1. Overwriting myfile.txt

假设我们的文本包含日期,我们可以使用 datetimeloadtxt 中处理:

In [13]:

  1. import datetime
  2.  
  3. def date_converter(s):
  4. return datetime.datetime.strptime(s, "%Y-%m-%d")
  5.  
  6. data = np.loadtxt('myfile.txt',
  7. dtype=np.object, #数据类型为对象
  8. converters={0:date_converter, #第一列使用自定义转换方法
  9. 1:float, #第二第三使用浮点数转换
  10. 2:float})
  11.  
  12. data

Out[13]:

  1. array([[datetime.datetime(2010, 1, 1, 0, 0), 2.3, 3.2],
  2. [datetime.datetime(2011, 1, 1, 0, 0), 6.1, 3.1]], dtype=object)

移除 myfile.txt

In [14]:

  1. import os
  2. os.remove('myfile.txt')

读写各种格式的文件

如下表所示:

文件格式 使用的包 函数
txt numpy loadtxt, genfromtxt, fromfile, savetxt, tofile
csv csv reader, writer
Matlab scipy.io loadmat, savemat
hdf pytables, h5py
NetCDF netCDF4, scipy.io.netcdf netCDF4.Dataset, scipy.io.netcdf.netcdf_file
文件格式 使用的包 备注
wav scipy.io.wavfile 音频文件
jpeg,png,… PIL, scipy.misc.pilutil 图像文件
fits pyfits 天文图像

此外, pandas ——一个用来处理时间序列的包中包含处理各种文件的方法,具体可参见它的文档:

http://pandas.pydata.org/pandas-docs/stable/io.html

将数组写入文件

savetxt 可以将数组写入文件,默认使用科学计数法的形式保存:

In [15]:

  1. data = np.array([[1,2],
  2. [3,4]])
  3.  
  4. np.savetxt('out.txt', data)

In [16]:

  1. with open('out.txt') as f:
  2. for line in f:
  3. print line,
  1. 1.000000000000000000e+00 2.000000000000000000e+00
  2. 3.000000000000000000e+00 4.000000000000000000e+00

也可以使用类似C语言中 printf 的方式指定输出的格式:

In [17]:

  1. data = np.array([[1,2],
  2. [3,4]])
  3.  
  4. np.savetxt('out.txt', data, fmt="%d") #保存为整数

In [18]:

  1. with open('out.txt') as f:
  2. for line in f:
  3. print line,
  1. 1 2
  2. 3 4

逗号分隔的输出:

In [19]:

  1. data = np.array([[1,2],
  2. [3,4]])
  3.  
  4. np.savetxt('out.txt', data, fmt="%.2f", delimiter=',') #保存为2位小数的浮点数,用逗号分隔

In [20]:

  1. with open('out.txt') as f:
  2. for line in f:
  3. print line,
  1. 1.00,2.00
  2. 3.00,4.00

复数值默认会加上括号:

In [21]:

  1. data = np.array([[1+1j,2],
  2. [3,4]])
  3.  
  4. np.savetxt('out.txt', data, fmt="%.2f", delimiter=',') #保存为2位小数的浮点数,用逗号分隔

In [22]:

  1. with open('out.txt') as f:
  2. for line in f:
  3. print line,
  1. (1.00+1.00j), (2.00+0.00j)
  2. (3.00+0.00j), (4.00+0.00j)

更多参数:

  1. savetxt(fname,
  2. X,
  3. fmt='%.18e',
  4. delimiter=' ',
  5. newline='\n',
  6. header='',
  7. footer='',
  8. comments='# ')

移除 out.txt

In [23]:

  1. import os
  2. os.remove('out.txt')

Numpy 二进制格式

数组可以储存成二进制格式,单个的数组保存为 .npy 格式,多个数组保存为多个.npy文件组成的 .npz 格式,每个 .npy 文件包含一个数组。

与文本格式不同,二进制格式保存了数组的 shape, dtype 信息,以便完全重构出保存的数组。

保存的方法:

  • save(file, arr) 保存单个数组,.npy 格式
  • savez(file, args, *kwds) 保存多个数组,无压缩的 .npz 格式
  • savez_compressed(file, args, *kwds) 保存多个数组,有压缩的 .npz 格式 读取的方法:

  • load(file, mmap_mode=None) 对于 .npy,返回保存的数组,对于 .npz,返回一个名称-数组对组成的字典。

单个数组的读写

In [24]:

  1. a = np.array([[1.0,2.0], [3.0,4.0]])
  2.  
  3. fname = 'afile.npy'
  4. np.save(fname, a)

In [25]:

  1. aa = np.load(fname)
  2. aa

Out[25]:

  1. array([[ 1., 2.],
  2. [ 3., 4.]])

删除生成的文件:

In [26]:

  1. import os
  2. os.remove('afile.npy')

二进制与文本大小比较

In [27]:

  1. a = np.arange(10000.)

保存为文本:

In [28]:

  1. np.savetxt('a.txt', a)

查看大小:

In [29]:

  1. import os
  2. os.stat('a.txt').st_size

Out[29]:

  1. 260000L

保存为二进制:

In [30]:

  1. np.save('a.npy', a)

查看大小:

In [31]:

  1. os.stat('a.npy').st_size

Out[31]:

  1. 80080L

删除生成的文件:

In [32]:

  1. os.remove('a.npy')
  2. os.remove('a.txt')

可以看到,二进制文件大约是文本文件的三分之一。

保存多个数组

In [33]:

  1. a = np.array([[1.0,2.0],
  2. [3.0,4.0]])
  3. b = np.arange(1000)

保存多个数组:

In [34]:

  1. np.savez('data.npz', a=a, b=b)

查看里面包含的文件:

In [35]:

  1. !unzip -l data.npz
  1. Archive: data.npz
  2. Length Date Time Name
  3. --------- ---------- ----- ----
  4. 112 2015/08/10 00:46 a.npy
  5. 4080 2015/08/10 00:46 b.npy
  6. --------- -------
  7. 4192 2 files

载入数据:

In [36]:

  1. data = np.load('data.npz')

载入后可以像字典一样进行操作:

In [37]:

  1. data.keys()

Out[37]:

  1. ['a', 'b']

In [38]:

  1. data['a']

Out[38]:

  1. array([[ 1., 2.],
  2. [ 3., 4.]])

In [39]:

  1. data['b'].shape

Out[39]:

  1. (1000L,)

删除文件:

In [40]:

  1. # 要先删除 data,否则删除时会报错
  2. del data
  3.  
  4. os.remove('data.npz')

压缩文件

当数据比较整齐时:

In [41]:

  1. a = np.arange(20000.)

无压缩大小:

In [42]:

  1. np.savez('a.npz', a=a)
  2. os.stat('a.npz').st_size

Out[42]:

  1. 160188L

有压缩大小:

In [43]:

  1. np.savez_compressed('a2.npz', a=a)
  2. os.stat('a2.npz').st_size

Out[43]:

  1. 26885L

大约有 6x 的压缩效果。

当数据比较混乱时:

In [44]:

  1. a = np.random.rand(20000.)

无压缩大小:

In [45]:

  1. np.savez('a.npz', a=a)
  2. os.stat('a.npz').st_size

Out[45]:

  1. 160188L

有压缩大小:

In [46]:

  1. np.savez_compressed('a2.npz', a=a)
  2. os.stat('a2.npz').st_size

Out[46]:

  1. 151105L

只有大约 1.06x 的压缩效果。

In [47]:

  1. os.remove('a.npz')
  2. os.remove('a2.npz')

原文: https://nbviewer.jupyter.org/github/lijin-THU/notes-python/blob/master/03-numpy/03.19-reading-and-writing-arrays.ipynb