为ASCII数据添加名称和分配数据类型

3
我的教授使用IDL并给我发送了一个ASCII数据文件,我最终需要能够读取和操作该文件。他使用以下命令来读取数据:
readcol, 'sn-full.txt', format='A,X,X,X,X,X,F,A,F,A,X,X,X,X,X,X,X,X,X,A,X,X,X,X,A,X,X,X,X,F,X,I,X,F,F,X,X,F,X,F,F,F,F,F,F', $
sn, off1, dir1, off2, dir2, type, gal, dist, htype, d1, d2, pa, ai, b, berr, b0, k, kerr

这是前两行的图片:http://i.imgur.com/hT7YIE3.png 由于我不会成为一名天文学家,所以我使用Python,但我对它还很陌生,因此我很难读取数据。
我知道他的代码将数据类型A(字符串数据)分配给第一列,通过使用X跳过第二到六列,然后将数据类型F(浮点数)分配给第七列等。然后sn被分配到第一个未被跳过的列中。
我一直在尝试使用numpy.loadtxt("sn-full.txt")ascii.read("sn-full.txt")来复制这个过程,但我不确定如何输入dtype参数。我知道我可以将所有东西都分配为某种数据类型,但如何为单独的列分配数据类型呢?

尝试使用genfromtxt函数,同时可以参考导入数据中的好用部分。如果列是字符串,则必须以字节为单位给出确切的大小。一种简单的分配dtypes的方法是作为元组,例如:dtype=[('str1',str,8), ('var1',float), ('var2', int)]。特别是请查看关于数据类型的部分。 - Mark Mikofski
即使你想成为一名天文学家,你也应该使用Python(或者也许是Julia,甚至是Ruby),而不是限制自己在一个陈旧的专有语言中 :) - Iguananaut
3个回答

4
使用astropy.io.ascii,您应该能够相对容易地读取您的文件:
from astropy.io import ascii
# Give names for ALL of the columns, as there is no easy way to skip columns
# for a table with no column header.
colnames = ('sn', 'gal_name1', 'gal_name2', 'year', 'month', 'day', ...)
table = ascii.read('sn_full.txt', Reader=ascii.NoHeader, names=colnames)

这将为您提供一个包含所有数据列的表格。除非表格非常长,您不需要的某些列不是问题。对于您展示的表格,您不需要明确指定dtypes,因为io.ascii.read会正确地找出它们。
这里有一个小问题,您展示的表格实际上是一个定宽表格,意味着所有列在垂直方向上排列整齐。请注意第一行以1998S NGC 3877开头。只要每行具有相同的模式,三个用空格分隔的列表示超新星名称和作为两个单词的星系名称,那么就没有问题了。但如果任何一个星系名称仅为一个单词,则解析将失败。我怀疑如果IDL的readcol正常工作,则相应的io.ascii版本应该可以直接使用。如果不行,则io.ascii有一种读取固定宽度表格的方式,其中您需要明确提供列名和位置。
[编辑] 看起来在这种情况下需要一个定宽读取器来告知解析器如何分割列,而不仅仅使用空格作为分隔符。因此,基本上您需要在表文件顶部添加两行,其中第一行给出列名,第二行则有破折号表示每个列的跨度。
  a       b          c        
----  ------------  ------
 1.2  hello there    2
 2.4  worlds         3

如果您无法修改输入数据文件,也可以在astropy.io.ascii中通过代码指定每个列的起始和停止位置,例如:

>>> ascii.read(table, Reader=ascii.FixedWidthNoHeader,
               names=('Name', 'Phone', 'TCP'),
               col_starts=(0, 9, 18),
               col_ends=(5, 17, 28),
              )

谢谢您的回复。我相信这应该可以解决问题。但是,我遇到了一个问题。在一些行中,最后一列的条目是两个单词,这会导致“InconsistentTableError: Number of header columns (47) inconsistent with data columns (48) at data line 26”的错误。如果我添加另一列标题,我会得到相同的错误,但这次是在第0行。有什么建议可以解决这个问题吗? - user2909019
1
所以你可能需要在文件中添加一个标题,并使用FixedWidthTwoLine读取器。如果你遵循这里的第一个例子,那么应该相对清楚该怎么做。 - Tom Aldcroft
最后一个问题。我终于能够读取这些数据了,但我想知道是否有更简单的方法来操作这些数据。例如,假设我想为三个不同的超新星添加off1的值。目前我只是使用SNdata['off1'][0] + SNdata['off1'][1] + SNdata['off1'][2]。是否有更简单的方法? - user2909019

2

readcol(http://code.google.com/p/agpy/source/browse/trunk/agpy/readcol.py)是一个很好的解决方案,我仍然在使用它(我写的),但是`np.loadtxt`现在几乎可以做到所有相同的好事情。 - keflavich

0

我会使用Pandas来完成这个特定的目的。最简单的方法是,假设你的列是单个制表符分隔的:

import pandas as pd
import scipy as sp   # Provides all functionality from numpy, too
mydata = pd.read_table(
             'filename.dat', sep='\t', header=None, 
             names=['sn', 'gal_name1', 'gal_name2', 'year', 'month',...],
             dtype={'sn':sp.float64, 'gal_name1':object, 'year':sp.int64, ...},)

(这里的字符串属于一般的“对象”数据类型)。

现在每列都有一个名称,可以像常规的numpy 1D数组一样访问它们,例如mydata['colname'],然后可以像mydata['colname'][20:50]等一样对其进行切片等等。

Pandas具有内置的matplotlib绘图调用功能,因此您可以通过mydata['column'].plot()快速获得数字类型列的概述,或者将两个不同的列相互比较,如mydata.plot('col1', 'col2')。所有正常的绘图关键字都可以传递。

如果您想在普通的matplotlib例程中绘制数据,只需将列传递给matplotlib即可,其中它们将被视为普通的Numpy向量。每列都可以像普通的Numpy向量一样访问,例如mydata['colname'].values

编辑

如果您的数据不是均匀分隔的,则numpy的genfromtxt()函数更好。然后,您可以通过以下方式将其转换为Pandas DataFrame

mydf = pd.DataFrame(myarray, columns=['col1', 'col2', ...],
                    dtype={'col1':sp.float64, 'col2':object, ...})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接