在 python 中读取并保存具有可变列数的数据文件

Read and save data file with variable number of columns in python

我有一个 space 分隔的数据文件,看起来像这样(只是一个切片)

Wavelength  Ele   Excit   loggf       D0        
11140.324   108.0 3.44     -7.945    4.395
11140.357    26.1 12.09    -2.247
11140.361   108.0 2.39     -8.119    4.395
11140.365    25.0 5.85    -9.734
11140.388    23.0 4.56    -4.573
11140.424   608.0 5.12    -10.419    11.09 
11140.452   606.0 2.12    -11.054     6.25 
11140.496   108.0 2.39    -8.119      4.395
11140.509   606.0 1.70    -7.824      6.25 

第 1 部分

首先我想阅读 lá np.loadtxt 的文件。这不起作用,所以我尝试了

d = np.genfromtxt('file.dat', skiprows=1, filling_value=0.0, missing_values=' ')

以及它的不同版本。全部报错:Line #3 (got 4 columns instead of 5)。我想我已经接近能够读取该文件了。请注意,我更喜欢 np.genfromtxt 之类的解决方案,而不是打开文件并逐行查看它:

with open('test.dat', 'r') as lines:
    for line in lines:
        # put numbers in arrays/lists

第 2 部分

读取文件成功后,需要以特定格式保存。简而言之,此文件将作为 Fotran 程序的输入,每列 10 spaces 用于数字。如果没有最后一列 (D0),我可以使用(有一列我不使用,因此 '%27.1f'

fmt_ = ('%9.2f', '%7.1f', '%11.2f','%10.3f', '%27.1f')
np.savetxt('output.dat', data, fmt=fmt_)

但我怀疑这也行不通。所以 s np.genfromtxt 用于保存可能会有所帮助。

帮助全部,部分或只是一些指导表示赞赏。

第 1 部分:

使用pandas。它是专门为处理这种情况而设计的:

import pandas as pd
df = pd.read_csv('test.csv', sep='\s+')
print(df)

给你:

   Wavelength    Ele  Excit   loggf      D0
0   11140.324  108.0   3.44  -7.945   4.395
1   11140.357   26.1  12.09  -2.247     NaN
2   11140.361  108.0   2.39  -8.119   4.395
3   11140.365   25.0   5.85  -9.734     NaN
4   11140.388   23.0   4.56  -4.573     NaN
5   11140.424  608.0   5.12 -10.419  11.090
6   11140.452  606.0   2.12 -11.054   6.250
7   11140.496  108.0   2.39  -8.119   4.395
8   11140.509  606.0   1.70  -7.824   6.250

第 2 部分

您也可以为此使用 pandas,尽管正确设置格式有点复杂:

formatters  = ['{: >9.2f}'.format, '{: >7.1f}'.format, 
               '{: >11.2f}'.format,'{: >10.3f}'.format, 
               lambda x: ' '*27 if np.isnan(x) else '{: >27.1f}'.format(x)]

lines = df.to_string(index=False, header=False, formatters=formatters)

with open('out.dat', 'w') as outfile:
    outfile.write(lines)

给你:

 11140.32   108.0        3.44     -7.945                         4.4
 11140.36    26.1       12.09     -2.247                            
 11140.36   108.0        2.39     -8.119                         4.4
 11140.36    25.0        5.85     -9.734                            
 11140.39    23.0        4.56     -4.573                            
 11140.42   608.0        5.12    -10.419                        11.1
 11140.45   606.0        2.12    -11.054                         6.2
 11140.50   108.0        2.39     -8.119                         4.4
 11140.51   606.0        1.70     -7.824                         6.2

这是包含您的数据的示例 运行 的一部分。

In [62]: txt=b"""Wavelength  Ele   Excit   loggf       D0        
11140.324   108.0 3.44     -7.945    4.395
...
11140.509   606.0 1.70    -7.824      6.25 """

In [63]: txt=txt.splitlines()

In [64]: def foo(astr):
    # add a 'NaN' field to the short lines
    if len(astr)<35:
        astr += b'  NaN'  # or filler of your choice
    return astr
   ....: 

In [65]: data=np.loadtxt([foo(t) for t in txt], skiprows=1)

In [66]: data
Out[66]: 
array([[  1.11403240e+04,   1.08000000e+02,   3.44000000e+00,
         -7.94500000e+00,   4.39500000e+00],
       [  1.11403570e+04,   2.61000000e+01,   1.20900000e+01,
         -2.24700000e+00,              nan],
        ...
       [  1.11405090e+04,   6.06000000e+02,   1.70000000e+00,
         -7.82400000e+00,   6.25000000e+00]])

In [67]: np.savetxt('test.dat',x,fmt=fmt_)

In [69]: cat test.dat
 11140.32   108.0        3.44     -7.945                         4.4
 11140.36    26.1       12.09     -2.247                         nan
 11140.36   108.0        2.39     -8.119                         4.4
 11140.36    25.0        5.85     -9.734                         nan
 ...
 11140.51   606.0        1.70     -7.824                         6.2

文件可以像这样通过foo传递:

with open('test.dat') as f: 
     xx = np.loadtxt((foo(t) for t in f),skiprows=1)

savetxt 本质上是逐行 write,因此编写您自己的版本并不难。例如

In [120]: asbytes=np.lib.npyio.asbytes

In [121]: fmt__='%9.2f  %7.1f  %11.2f  %10.3f  %10.1f'

In [122]: with open('test.dat','wb') as f: 
     for row in x:
        f.write(asbytes(fmt__%tuple(row)+'\n'))
   .....:         

In [123]: cat test.dat
 11140.32    108.0         3.44      -7.945         4.4
 11140.36     26.1        12.09      -2.247         nan
 11140.36    108.0         2.39      -8.119         4.4
 11140.36     25.0         5.85      -9.734         nan
 ...
 11140.51    606.0         1.70      -7.824         6.2

有了这个就不难测试每一行,并对带有 nan.

的行使用不同的格式