如何使用python高效地加载此类ASCII文件?

如何使用python高效地加载此类ASCII文件?,python,numpy,io,Python,Numpy,Io,我有大型fortran生成的ASCII文件,格式如下: x y z num_line index 1 float 2 float ... num_line float x2 y2 z2 num_line2 index2 1 float 2 float ... num_line2 float ... 块的数量可以达到数千,每个块中的行数可以达到数百 让我们举一个我得到的例子: 0.0 0.0 0.0 4 0 1 0.5 2 0.9 3 0.4 4 0.1 0.0 0.0 1.0 4 1 1

我有大型fortran生成的ASCII文件,格式如下:

x y z  num_line index
1 float
2 float
...
num_line float
x2 y2 z2 num_line2 index2
1 float
2 float
...
num_line2 float
...
块的数量可以达到数千,每个块中的行数可以达到数百

让我们举一个我得到的例子:

0.0 0.0 0.0  4 0
1 0.5
2 0.9
3 0.4
4 0.1
0.0 0.0 1.0  4 1
1 0.2
2 0.2
3 0.4
4 0.9
0.0 1.0 2.0  5 2
1 0.7
2 0.6
3 0.9
4 0.2
5 0.7
我想从中得到什么(作为numpy矩阵):

当然,我可以使用:

my_mat = []
with open("myfile", "r") as f_in:
    niter = int(f_in.readline().split()[3])
    while niter:
        curr_vect = zeros(niter)
        for i in xrange(niter):
            curr_vect[i] = float(f_in.readline().split()[1])
        my_mat.append(curr_vect)
        line = f_in.readline()
        if line is not None:
            niter = int(line.split()[3])
        else:
            niter = False
my_mat = array(my_mat)
问题是,这并不是真正有效的,也太复杂了。我已经知道numpy的
loadtxt
genfromtxt
但是它们似乎不适用于那里

我在寻找更快、更可读的东西。有什么想法吗

编辑:

请原谅我,我的问题不完整,你们中的一些人因为我而失去了时间。以下是此类块的真实示例:

3.571428571429E-02 3.571428571429E-02-3.571428571429E-02         1   35  
       1 -0.493775207966779     
       2  0.370269037864060     
       3  0.382332033744703     
       4  0.382332033744703     
       5  0.575515346181205     
       6  0.575515346181216     
       7  0.575562530624028     
       8  0.639458035564442     
       9  0.948445367602052     
      10  0.948445367602052     
      11  0.975303238888803     
      12   1.20634795229899     
      13   1.21972845646758     
      14   1.21972845646759     
      15   1.52659950368213     
      16   2.07381346028515     
      17   2.07629743909555     
      18   2.07629743909555     
      19   2.15941179949552     
      20   2.15941179949552     
      21   2.30814240005132     
      22   2.30814240005133     
      23   2.31322868361483     
      24   2.53625115348660     
      25   2.55301153157825     
      26   2.55301153157826     
      27   2.97152031842301     
      28   2.98866790318661     
      29   2.98866790318662     
      30   3.24757159459268     
      31   3.27186643004142     
      32   3.27186643004143     
      33   3.37632477135298     
      34   3.37632477135299     
      35   3.55393884607834     

如果确保每个块都有相同数量的行(即使填充了零),速度会快得多。这样,您就可以使用
loadtxt
重新格式化读取的数组。但考虑到这一限制,这里有一个可能更快一些的示例:

import numpy as np

data = np.loadtxt("myfile", usecols=(0, 1), unpack=True)
nx = np.sum(data[0] == 0)
ny = np.max(data[0])
my_mat = np.empty((nx, ny), dtype='d')
my_mat[:] = np.nan   # if you really want to populate it with NaNs for missing
tr_ind = data[0, list(np.nonzero(np.diff(data[0]) < 0)[0]) + [-1]].astype('i')
buf = np.squeeze(data[1, np.nonzero(data[0])])

idx = 0
for i in range(nx):
    my_mat[i, :tr_ind[i]] = buf[idx : idx + tr_ind[i]]
    idx += tr_ind[i]
更新:正如TheodrosZelleke所指出的,当
x2
(第一列)为非零时,上述解决方案将失败。我第一次没有注意到这一点。以下是解决此问题的更新:

# this will give a conversion warning because column number varies
blk_sizes = np.genfromtxt("myfile", invalid_raise=False, usecols=(-2,))

nx = blk_sizes.size
ny = np.max(blk_sizes)

data = np.loadtxt("myfile", usecols=(1,))
my_mat = np.empty((nx, ny), dtype='d')
my_mat[:] = np.nan

idx = 1
for i in range(nx):
    my_mat[i, :blk_sizes[i]] = data[idx : idx + blk_sizes[i]]
    idx += blk_sizes[i] + 1
(然后取
my_mat.T

输出:

编辑:


至于性能,我用
np.genfromtxt
解决方案@TheodrosZelleke对它进行了测试,速度似乎快了五倍左右。

您可以使用numpy.genfromtxt:

  • 读取一列,由换行符删除
  • 提供自定义转换器功能
  • 例如:

    import numpy as np
    from StringIO import StringIO
    
    # your data from above as string
    raw = '''0.0 0.0 0.0  4 0
    1 0.5
    ...
    5 0.7
    '''
    
    这是转换器:

    def custom_converter(line):
        token = line.split()
        if len(token) == 2:
            return float(token[1])
        else:
            return np.NaN
    
    加载数据:

    data = np.genfromtxt(StringIO(raw),
                         delimiter='\n',
                         converters={0: custom_converter})
    
    print data
    
    其中打印:

    [ nan  0.5  0.9  0.4  0.1  nan  0.2  0.2  0.4  0.9  nan  0.7  0.6  0.9  0.2
      0.7]
    
    现在构建最终的数据结构:

    delims, = np.where(np.isnan(data))
    max_block = np.max(np.diff(delims))
    nblocks = delims.size
    final_data = np.empty([max_block, nblocks]) + np.NaN
    
    delims = delims.tolist()
    delims.append(data.size)
    low = delims[0] + 1
    for i, up in enumerate(delims[1:]):
        final_data[0: up-low , i] = data[low:up]
        low = up + 1
    
    print final_data
    
    哪张照片

    [[ 0.5  0.2  0.7]
     [ 0.9  0.2  0.6]
     [ 0.4  0.4  0.9]
     [ 0.1  0.9  0.2]
     [ nan  nan  0.7]]
    

    我不认为有一种简单而干净的方法可以做到这一点,除了像您那样直接编写特定于该格式的阅读器的代码。我想你需要多次处理这个文件?然后,我建议您使用上面提到的方法读取此文件一次,并将其转换为python或numpy易于读取的文件结构。如果文件发生了变化,您可能需要考虑在FORTRAN中更改输出格式(如果您可以访问该代码)。是的,<代码> PyTabs< /Code >将很乐意这样做。问题是我有很多这样格式化的文件。。。我也有很多文件格式相似,但不完全相同。比如说,在我听说loadtxt之前,我已经为csv表编写了多年这种程序。。。这是一个耻辱。不,有些人可能已经尝试了一些与蒂亚戈相同的假设(尚未出版)。我还没有回答你。我会的。我对你解决这个问题的方式很感兴趣,我想在评论之前先做一点实验@盖尔,在你的真实例子中,你有两个数字(一个负数),没有空格分隔它们。这将是一个非常烦人的问题,你必须使用固定的间距来阅读这些列。你是绝对正确的。Fortran Powa!然后我应该用硬编码的列范围替换拆分。-1)抱歉,但是您的算法取决于这样一个事实,即要排除的行以
    0开头。
    。虽然OP没有明确说明情况并非如此,但人们可能会猜测这些线(包含x、y、z坐标)将具有其他x值。我声称,仅仅通过查看前两列是不可能提供解决方案的——您需要查看每行上的令牌计数,您是正确的。我现在没有时间,但是改变算法避免零并不难。您可以始终查看第一列的导数(即使在某些情况下需要一些开关),或者通过查看其他列来找到提取这些数字的方法。我的解决方案不是一个完整的答案,而是一个起点。它是一个没有起点的起点——这个问题需要一个答案,这个答案并不意味着对前两列的数值有任何假设——你不能处理任何类型的导数。。。您需要查看每行上的令牌计数。前两列中有足够的信息来确定它何时跳转到另一个块。不管你相信与否都没有关系。如果数据直接保存在HDF5中,速度会更快,但事实并非如此。我可以调整我的fortran程序版本,但我不能为其他版本做。顺便说一句,我给了你一个街区的真实例子。零比其他数字更容易找到。。。对不起。我必须同意Theodros所写的:依靠数据推断数据结构似乎不是一个好主意。我刚才添加的示例中的下一个x可以等于3.6E01。这将导致无法识别的错误。不过,谢谢你的回答:我没有考虑这个方法,我会记住的。
    data = np.genfromtxt(StringIO(raw),
                         delimiter='\n',
                         converters={0: custom_converter})
    
    print data
    
    [ nan  0.5  0.9  0.4  0.1  nan  0.2  0.2  0.4  0.9  nan  0.7  0.6  0.9  0.2
      0.7]
    
    delims, = np.where(np.isnan(data))
    max_block = np.max(np.diff(delims))
    nblocks = delims.size
    final_data = np.empty([max_block, nblocks]) + np.NaN
    
    delims = delims.tolist()
    delims.append(data.size)
    low = delims[0] + 1
    for i, up in enumerate(delims[1:]):
        final_data[0: up-low , i] = data[low:up]
        low = up + 1
    
    print final_data
    
    [[ 0.5  0.2  0.7]
     [ 0.9  0.2  0.6]
     [ 0.4  0.4  0.9]
     [ 0.1  0.9  0.2]
     [ nan  nan  0.7]]