将第一行中给出的 headers 的 ascii 文件读取到 pandas 数据帧中

reading an ascii file with headers given in the first rows into a pandas dataframe

我有一大组目录,其中有不同的列和每个列的不同 header 名称,其中每个 header 名称的描述在我的 ascii 开头作为注释给出连续的文件。将它们读入 pandas.DataFrame 的最佳方法是什么,同时它也可以设置列的名称,而无需从头开始定义它。以下是我的目录示例:

#   1 MAG_AUTO            Kron-like elliptical aperture magnitude         [mag]
#   2 rh                  half light radius (analyse)                     [pixel]
#   3 MU_MAX              Peak surface brightness above background        [mag * arcsec**(-2)]
#   4 FWHM_IMAGE          FWHM assuming a gaussian core                   [pixel]
#   5 CLASS_STAR          S/G classifier output                          
18.7462 4.81509 20.1348 6.67273 0.0286538
18.2440 7.17988 20.6454 21.6235 0.0286293
18.3102 3.11273 19.0960 8.26081 0.0430532
21.1751 2.92533 21.9931 5.52080 0.0290418
19.3998 1.86182 19.3166 3.42346 0.986598
20.0801 3.52828 21.3484 6.76799 0.0303842
21.9427 2.08458 22.0577 5.59344 0.981466
20.7726 1.86017 20.8130 3.69570 0.996121
23.0836 2.23427 23.3689 4.49985 0.706207
23.2443 1.62021 23.1089 3.54191 0.973419
20.6343 3.99555 21.9426 6.94700 0.0286164
23.4012 2.00408 23.3412 4.35926 0.946349
23.8427 1.54819 23.8241 3.83407 0.897079
20.3344 2.69910 20.9401 4.38988 0.0355277
21.7506 2.43451 22.2115 4.62045 0.0786921

好的,假设您的所有 header 信息都以完全相同的方式编码,下面是我将如何做到这一点:

import re

import pandas

COMMENT_CHAR = '#'
columns = []
with open('test.dat', 'r') as td:
    for line in td:

        # find the commented lines
        if line[0] == COMMENT_CHAR:
            info = re.split(' +', line)
            columns.append(info[2])

        # when we seethe first line that doesn't start with 
        # COMMENT_CHAR, we pass the remaining lines of the 
        # file to pandas.read_table and break our loop
        else:
            _dfs = [
                pandas.DataFrame([line.split(' ')], columns=columns, dtype=float),
                pandas.read_table(td, sep='\s', header=None, names=columns)
            ]
            df = pandas.concat(_dfs, ignore_index=True)

为了稍微分解一下初始解析,re.split(' +', line) 变成了这个:

# 1 MAG_AUTO Kron-like elliptical aperture magnitude [mag]

进入

['#', '1', 'MAG_AUTO', 'Kron-like', 'elliptical', 'aperture', 'magnitude', '[mag]']

所以我们取列名作为第3个元素(索引=2)

所有这些都会产生一个如下所示的数据框:

print(df.head())
   MAG_AUTO       rh   MU_MAX  FWHM_IMAGE  CLASS_STAR
0   18.7462  4.81509  20.1348     6.67273    0.0286538
1   18.2440  7.17988  20.6454    21.62350    0.028629
2   18.3102  3.11273  19.0960     8.26081    0.043053
3   21.1751  2.92533  21.9931     5.52080    0.029042
4   19.3998  1.86182  19.3166     3.42346    0.986598

df.info() 给我们:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 15 entries, 0 to 14
Data columns (total 5 columns):
MAG_AUTO      15 non-null float64
rh            15 non-null float64
MU_MAX        15 non-null float64
FWHM_IMAGE    15 non-null float64
CLASS_STAR    15 non-null float64
dtypes: float64(5)
memory usage: 720.0 bytes

这是 Sextractor 格式的文件。 astropy.io.ascii reader 本身就理解这种格式,因此阅读起来很容易:

>>> from astropy.io import ascii
>>> dat = ascii.read('table.dat')
>>> dat
<Table masked=False length=3>
MAG_AUTO    rh       MU_MAX    FWHM_IMAGE CLASS_STAR
  mag            mag / arcsec2    pix               
float64  float64    float64     float64    float64  
-------- ------- ------------- ---------- ----------
 18.7462 4.81509       20.1348    6.67273  0.0286538
  18.244 7.17988       20.6454    21.6235  0.0286293
 18.3102 3.11273        19.096    8.26081  0.0430532
...

请注意,使用 astropy ASCII reader 你会得到一个 table,它也保留了单位元数据。

如果您仍想将其转换为 pandas 数据框,使用 DataFrame(dat.as_array()) 也很容易。 astropy 的 1.1 版(和当前的 master)将有方法 to_pandasfrom_pandas 使这种转换更加稳健(参见 http://astropy.readthedocs.org/en/latest/table/pandas.html)。