从字符串列表中创建一个numpy结构化数组

时间:2015-12-21 21:39:06

标签: python arrays numpy

我正在使用python实用程序从Tycho 2星级目录中获取数据。我正在处理的一个函数查询目录并返回给定星号id(或一组星id)的所有信息。

我目前正在通过遍历目录文件的行来执行此操作,然后尝试将该行解析为numpy结构化数组(如果已查询)。 (注意,如果有更好的方法可以让我知道,即使这不是这个问题的意思 - 我这样做是因为目录太大而无法将所有内容加载到内存中一次)

无论如何,一旦我确定了我想保留的记录,我就遇到了问题...我无法弄清楚如何将其解析为结构化数组。

例如,我想保留的记录是:

record = '0002 00038 1| |  3.64121230|  1.08701186|   14.1|  -23.0| 69| 82| 1.8| 1.9|1968.56|1957.30| 3|1.0|3.0|0.9|3.0|12.444|0.213|11.907|0.189|999| |         |  3.64117944|  1.08706861|1.83|1.73| 81.0|104.7| | 0.0'

现在,我正在尝试使用dtype解析为numpy结构化数组:

        dform = [('starid', [('TYC1', int), ('TYC2', int), ('TYC3', int)]),
             ('pflag', str),
             ('starBearing', [('rightAscension', float), ('declination', float)]),
             ('properMotion', [('rightAscension', float), ('declination', float)]),
             ('uncertainty', [('rightAscension', int), ('declination', int), ('pmRA', float), ('pmDc', float)]),
             ('meanEpoch', [('rightAscension', float), ('declination', float)]),
             ('numPos', int),
             ('fitGoodness', [('rightAscension', float), ('declination', float), ('pmRA', float), ('pmDc', float)]),
             ('magnitude', [('BT', [('mag', float), ('err', float)]), ('VT', [('mag', float), ('err', float)])]),
             ('starProximity', int),
             ('tycho1flag', str),
             ('hipparcosNumber', str),
             ('observedPos', [('rightAscension', float), ('declination', float)]),
             ('observedEpoch', [('rightAscension', float), ('declination', float)]),
             ('observedError', [('rightAscension', float), ('declination', float)]),
             ('solutionType', str),
             ('correlation', float)]

这似乎应该是一件相当简单的事情,但我尝试的一切都会破坏......

我试过了:

np.genfromtxt(BytesIO(record.encode()),dtype=dform,delimiter=(' ','|'))
np.genfromtxt(BytesIO(record.encode()),dtype=dform,delimiter=(' ','|'),missing_values=' ',filling_values=None)

这两个都给了我

{TypeError}cannot perform accumulate with flexible type

这是没有意义的,因为它不应该做任何积累。

我也试过

np.array(re.split('\|| ',record),dtype=dform)

抱怨

{TypeError}a bytes-like object is required, not 'str'

和另一个变种

np.array([x.encode() for x in re.split('\|| ',record)],dtype=dform)

不会抛出错误,但也肯定没有返回正确的结果:

[ ((842018864, 0, 0), '', (0.0, 0.0), (0.0, 0.0), (0, 0, 0.0, 0.0), (0.0, 0.0), 0, (0.0, 0.0, 0.0, 0.0), ((0.0, 0.0), (0.0, 0.0)), 0, '', '', (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), '', 0.0)...

那我该怎么做呢?我认为genfromtxt选项是要走的路(特别是因为偶尔可能会丢失数据)但我不明白为什么它不起作用。这是我必须自己编写解析器吗?

1 个答案:

答案 0 :(得分:3)

对不起,这个答案很长很漫无目的,但这就是弄清楚发生了什么。特别是dtype的复杂性因其长度而隐藏。

当我尝试TypeError: cannot perform accumulate with flexible type的列表时出现delimiter错误。详细信息显示LineSplitter中发生错误。没有详细说明,分隔符应该是一个字符(或默认的'空格')。

来自genfromtxt文档:

  

delimiter:str,int或sequence,可选           用于分隔值的字符串。默认情况下,任何连续           空格充当分隔符。整数或整数序列           也可以作为每个字段的宽度提供。

genfromtxt拆分器比.split使用的字符串loadtxt更强大,但不像re拆分器那样通用。

对于{TypeError}a bytes-like object is required, not 'str',您为几个字段指定dtype 'str'。这是字节字符串,其中record是unicode字符串(在Py3中)。但是你已经意识到BytesIO(record.encode())

我喜欢用以下内容测试genfromtxt个案例:

record = b'....'
np.genfromtxt([record], ....)

或者更好

records = b"""one line
tow line
three line
"""
np.genfromtxt(records.splitlines(), ....)

如果我让genfromtxt推断字段类型,只使用一个分隔符,我会得到32个字段:

In [19]: A=np.genfromtxt([record],dtype=None,delimiter='|')
In [20]: len(A.dtype)
Out[20]: 32
In [21]: A
Out[21]: 
array((b'0002 00038 1', False, 3.6412123, 1.08701186, 14.1, -23.0, 69, 82, 1.8, 1.9, 1968.56, 1957.3, 3, 1.0, 3.0, 0.9, 3.0, 12.444, 0.213, 11.907, 0.189, 999, False, False, 3.64117944, 1.08706861, 1.83, 1.73, 81.0, 104.7, False, 0.0), 
      dtype=[('f0', 'S12'), ('f1', '?'), ('f2', '<f8'), ('f3', '<f8'), ('f4', '<f8'), ... ('f26', '<f8'), ('f27', '<f8'), ('f28', '<f8'), ('f29', '<f8'), ('f30', '?'), ('f31', '<f8')])

当我们得到整个字节和分隔符问题时

np.array([x for x in re.split(b'\|| ',record)],dtype=dform)

确实在运行。我现在看到你的dform很复杂,有嵌套的复合字段。

但是要定义一个结构化数组,你可以给它一个记录列表,例如

np.array([(record1...), (record2...), ....], dtype([(field1),(field2 ),...]))

在这里,您尝试创建一条记录。我可以将你的列表包含在一个元组中,但是我得到的长度与dform长度之间的不匹配,66 v 17.如果算上所有子字段dform可能需要66个值,但我们可以'只需用一个元组就可以做到这一点。

我从来没有尝试过这样一个复杂的dtype创建一个数组,所以我正在寻找方法让它工作。

In [41]: np.zeros((1,),dform)
Out[41]: 
array([ ((0, 0, 0), '', (0.0, 0.0), (0.0, 0.0), (0, 0, 0.0, 0.0), (0.0, 0.0), 0, (0.0, 0.0, 0.0, 0.0), ((0.0, 0.0), (0.0, 0.0)), 0, '', '', (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), '', 0.0)], 
      dtype=[('starid', [('TYC1', '<i4'), ('TYC2', '<i4'), ('TYC3', '<i4')]), ('pflag', '<U'), ('starBearing', [('rightAscension', '<f8'), ('declination', '<f8')]), ('properMotion', [('rightAscension', '<f8'), ('declination', '<f8')]), ('uncertainty', [('rightAscension', '<i4'), ('declination', '<i4'), ('pmRA', '<f8'), ('pmDc', '<f8')]), ('meanEpoch', ....('solutionType', '<U'), ('correlation', '<f8')])

In [64]: for name in A.dtype.names:
    print(A[name].dtype)
   ....:     
[('TYC1', '<i4'), ('TYC2', '<i4'), ('TYC3', '<i4')]
<U1
[('rightAscension', '<f8'), ('declination', '<f8')]
[('rightAscension', '<f8'), ('declination', '<f8')]
[('rightAscension', '<i4'), ('declination', '<i4'), ('pmRA', '<f8'), ('pmDc', '<f8')]
[('rightAscension', '<f8'), ('declination', '<f8')]
int32
[('rightAscension', '<f8'), ('declination', '<f8'), ('pmRA', '<f8'), ('pmDc', '<f8')]
[('BT', [('mag', '<f8'), ('err', '<f8')]), ('VT', [('mag', '<f8'), ('err', '<f8')])]
int32
<U1
<U1
[('rightAscension', '<f8'), ('declination', '<f8')]
[('rightAscension', '<f8'), ('declination', '<f8')]
[('rightAscension', '<f8'), ('declination', '<f8')]
<U1
float64

我统计了34个原始dtype字段。大多数是'标量',大约2-4个术语,一个具有更高的嵌套水平。

如果我用|替换前两个分割空格,record.split(b'|')会给我34个字符串。

让我们在genfromtxt

中尝试一下
In [79]: np.genfromtxt([record],delimiter='|',dtype=dform)
Out[79]: 
array(((2, 38, 1), '', (3.6412123, 1.08701186), (14.1, -23.0), 
   (69, 82, 1.8, 1.9), (1968.56, 1957.3), 3, (1.0, 3.0, 0.9, 3.0),
   ((12.444, 0.213), (11.907, 0.189)), 999, '', '', 
   (3.64117944, 1.08706861), (1.83, 1.73), (81.0, 104.7), '', 0.0), 
      dtype=[('starid', [('TYC1', '<i4'), ('TYC2', '<i4'), ('TYC3', '<i4')]), 
 ('pflag', '<U'), 
 ('starBearing', [('rightAscension', '<f8'), ('declination', '<f8')]),  
 ('properMotion', [('rightAscension', '<f8'), ('declination', '<f8')]),
 ('uncertainty', [('rightAscension', '<i4'), ('declination', '<i4'), ('pmRA', '<f8'), ('pmDc', '<f8')]), 
 ('meanEpoch', [('rightAscension', '<f8'), ('declination', '<f8')]),   
 ('numPos', '<i4'), 
 ('fitGoodness', [('rightAscension', '<f8'), ('declination', '<f8'), ('pmRA', '<f8'), ('pmDc', '<f8')]), 
 ('magnitude', [('BT', [('mag', '<f8'), ('err', '<f8')]), ('VT', [('mag', '<f8'), ('err', '<f8')])]), 
 ('starProximity', '<i4'), ('tycho1flag', '<U'), ('hipparcosNumber', '<U'), 
 ('observedPos', [('rightAscension', '<f8'), ('declination', '<f8')]),
 ('observedEpoch', [('rightAscension', '<f8'), ('declination', '<f8')]), 
 ('observedError', [('rightAscension', '<f8'), ('declination', '<f8')]), ('solutionType', '<U'), ('correlation', '<f8')])

这几乎看起来很合理。 genfromtxt实际上可以在复合字段中拆分值。这就是我想用np.array()尝试的东西。

因此,如果你得到分隔符和字节/ unicode,genfromtxt可以处理这个混乱。