如何将多维数组写入文本文件?

149 投票
14 回答
349581 浏览
提问于 2025-04-16 03:58

在另一个问题中,有其他用户提供了一些帮助,前提是我能提供我遇到问题的数组。不过,我连一个基本的输入输出任务,比如把数组写入文件,都会失败。

有没有人能解释一下,我需要什么样的循环才能把一个4x11x14的numpy数组写入文件呢?

这个数组由四个11 x 14的数组组成,所以我应该把它格式化得好看一些,加上换行,这样别人看文件的时候会更容易。

编辑: 我试过使用numpy.savetxt这个函数。奇怪的是,它给了我以下错误:

TypeError: float argument required, not numpy.ndarray

我猜这可能是因为这个函数不支持多维数组?有没有什么解决办法,因为我希望它们能放在一个文件里?

14 个回答

13

如果你不需要人类能看懂的输出,另一个可以尝试的办法是把数组保存为MATLAB的.mat文件,这是一种结构化的数组。我个人不太喜欢MATLAB,但我觉得用很少的代码就能读写.mat文件,这点还是挺方便的。

和Joe Kington的回答不同,这种方法的好处是你不需要知道.mat文件中数据的原始形状,也就是说读取的时候不需要重新调整形状。而且,和使用pickle不同,.mat文件可以被MATLAB读取,可能还有其他一些程序或语言也能读取。

这里有个例子:

import numpy as np
import scipy.io

# Some test data
x = np.arange(200).reshape((4,5,10))

# Specify the filename of the .mat file
matfile = 'test_mat.mat'

# Write the array to the mat file. For this to work, the array must be the value
# corresponding to a key name of your choice in a dictionary
scipy.io.savemat(matfile, mdict={'out': x}, oned_as='row')

# For the above line, I specified the kwarg oned_as since python (2.7 with 
# numpy 1.6.1) throws a FutureWarning.  Here, this isn't really necessary 
# since oned_as is a kwarg for dealing with 1-D arrays.

# Now load in the data from the .mat that was just saved
matdata = scipy.io.loadmat(matfile)

# And just to check if the data is the same:
assert np.all(x == matdata['out'])

如果你忘记了数组在.mat文件中叫什么名字,你可以随时这样做:

print matdata.keys()

当然,你也可以用更多的键来存储多个数组。

所以是的——虽然你可能看不懂里面的内容,但写和读数据只需要两行代码,我觉得这个交换是值得的。

可以看看关于scipy.io.savematscipy.io.loadmat的文档,还有这个教程页面:scipy.io 文件输入输出教程

42

我不太确定这是否符合你的要求,因为我觉得你是想让文件对人类可读,但如果这不是主要考虑的事情,那就直接用pickle吧。

要保存文件的话:

import pickle

my_data = {'a': [1, 2.0, 3, 4+6j],
           'b': ('string', u'Unicode string'),
           'c': None}
output = open('data.pkl', 'wb')
pickle.dump(my_data, output)
output.close()

要读取文件的话:

import pprint, pickle

pkl_file = open('data.pkl', 'rb')

data1 = pickle.load(pkl_file)
pprint.pprint(data1)

pkl_file.close()
227

如果你想把数据写入磁盘,以便以后能方便地读回作为numpy数组,可以看看numpy.save。用pickle也可以,但对于大数组来说效率没那么高(不过你这个数组不大,所以两种方法都可以)。

如果你希望数据能被人直接看懂,可以看看numpy.savetxt

补充: 看起来对于维度大于2的数组,savetxt 不是个好选择……不过我还是把事情说清楚:

我刚意识到,numpy.savetxt 对于超过2维的数组会出问题……这可能是故意设计的,因为在文本文件中没有明确的方法来表示额外的维度。

比如,这个(一个2D数组)就能正常工作:

import numpy as np
x = np.arange(20).reshape((4,5))
np.savetxt('test.txt', x)

而同样的操作在3D数组上就会失败(错误信息也不太好理解:TypeError: float argument required, not numpy.ndarray):

import numpy as np
x = np.arange(200).reshape((4,5,10))
np.savetxt('test.txt', x)

一种解决办法是把3D(或更高维度的)数组切分成2D的片段。例如:

x = np.arange(200).reshape((4,5,10))
with open('test.txt', 'w') as outfile:
    for slice_2d in x:
        np.savetxt(outfile, slice_2d)

不过,我们的目标是让数据既能被人看懂,又能用numpy.loadtxt轻松读回去。因此,我们可以稍微详细一点,用注释行来区分这些片段。默认情况下,numpy.loadtxt 会忽略以#开头的行(或者用comments参数指定的任何字符)。 (看起来比实际要详细得多……)

import numpy as np

# Generate some test data
data = np.arange(200).reshape((4,5,10))

# Write the array to disk
with open('test.txt', 'w') as outfile:
    # I'm writing a header here just for the sake of readability
    # Any line starting with "#" will be ignored by numpy.loadtxt
    outfile.write('# Array shape: {0}\n'.format(data.shape))
    
    # Iterating through a ndimensional array produces slices along
    # the last axis. This is equivalent to data[i,:,:] in this case
    for data_slice in data:

        # The formatting string indicates that I'm writing out
        # the values in left-justified columns 7 characters in width
        # with 2 decimal places.  
        np.savetxt(outfile, data_slice, fmt='%-7.2f')

        # Writing out a break to indicate different slices...
        outfile.write('# New slice\n')

这样就能得到:

# Array shape: (4, 5, 10)
0.00    1.00    2.00    3.00    4.00    5.00    6.00    7.00    8.00    9.00   
10.00   11.00   12.00   13.00   14.00   15.00   16.00   17.00   18.00   19.00  
20.00   21.00   22.00   23.00   24.00   25.00   26.00   27.00   28.00   29.00  
30.00   31.00   32.00   33.00   34.00   35.00   36.00   37.00   38.00   39.00  
40.00   41.00   42.00   43.00   44.00   45.00   46.00   47.00   48.00   49.00  
# New slice
50.00   51.00   52.00   53.00   54.00   55.00   56.00   57.00   58.00   59.00  
60.00   61.00   62.00   63.00   64.00   65.00   66.00   67.00   68.00   69.00  
70.00   71.00   72.00   73.00   74.00   75.00   76.00   77.00   78.00   79.00  
80.00   81.00   82.00   83.00   84.00   85.00   86.00   87.00   88.00   89.00  
90.00   91.00   92.00   93.00   94.00   95.00   96.00   97.00   98.00   99.00  
# New slice
100.00  101.00  102.00  103.00  104.00  105.00  106.00  107.00  108.00  109.00 
110.00  111.00  112.00  113.00  114.00  115.00  116.00  117.00  118.00  119.00 
120.00  121.00  122.00  123.00  124.00  125.00  126.00  127.00  128.00  129.00 
130.00  131.00  132.00  133.00  134.00  135.00  136.00  137.00  138.00  139.00 
140.00  141.00  142.00  143.00  144.00  145.00  146.00  147.00  148.00  149.00 
# New slice
150.00  151.00  152.00  153.00  154.00  155.00  156.00  157.00  158.00  159.00 
160.00  161.00  162.00  163.00  164.00  165.00  166.00  167.00  168.00  169.00 
170.00  171.00  172.00  173.00  174.00  175.00  176.00  177.00  178.00  179.00 
180.00  181.00  182.00  183.00  184.00  185.00  186.00  187.00  188.00  189.00 
190.00  191.00  192.00  193.00  194.00  195.00  196.00  197.00  198.00  199.00 
# New slice

只要我们知道原始数组的形状,读回数据非常简单。我们只需执行numpy.loadtxt('test.txt').reshape((4,5,10))。举个例子(你可以用一行代码完成,我只是为了说明问题而多说了几句):

# Read the array from disk
new_data = np.loadtxt('test.txt')

# Note that this returned a 2D array!
print new_data.shape

# However, going back to 3D is easy if we know the 
# original shape of the array
new_data = new_data.reshape((4,5,10))
    
# Just to check that they're the same...
assert np.all(new_data == data)

撰写回答