结构化数组在使用field-name索引时会删除字段标题

时间:2017-08-29 12:43:35

标签: arrays python-3.x numpy field

numpy(v1.13.1)的以下行为是错误还是设计?

>>>import numpy as np
>>>a = np.zeros((1,), dtype=[(('title 1', 'x'), '|i1'), (('title 2', 'y'), '>f4')])

>>>a.dtype.descr
[(('title 1', 'x'), '|i1'), (('title 2', 'y'), '>f4')]

>>>a[['y','x']].dtype.descr
[('y', '>f4'), ('x', '|i1')]

# I would have expected the previous expression to have returned this instead: 
# [(('title 2', 'y'), '>f4'), (('title 1', 'x'), '|i1')]

请注意,只要使用字段名称索引数组,字段的field- title 就会消失。 (我的意思是字段 - 标题,而不是字段 - 名称!)

此问题是否有解决方法?

附录:为什么标题是一个很酷的功能

由于很多人认为标题应该(或者甚至已经被弃用),我会尝试提倡为什么它们是dtypes的一个很好的特性:

我获得了包含许多字段的大型数据系列,并发现为了代码可读性而拥有简洁的字段名称非常有用。标题允许我另外拥有人类可读形式的现场文档,而不会牺牲名称的简洁性。

特别方便的是,名称和标题都被分配到数据源自第一位的地方,并且只是通过管道传递:获取 - >处理 - >存储...... 沿着该管道丢失标题是特别不方便的,因为它们往往在管道的下游更有用,例如,已经被序列化以进行存储或交换的numpy数组:只需要这两行代码就可以将 任何 ndarray保存到数据库或通过网络发送:

serialized_array.dtype = repr(numpy_array.dtype.descr)
serialized_array.buffer = base64.b64encode(numpy_array.tostring())

该数据的任何消费者/接收者都可以在可读的纯文本中获得完整记录的dtype,其中标题记录了字段的重要方面,例如物理单位(例如:mm或英寸?)

重新创建如上所示已经序列化的numpy数组也是在两行代码中完成的,每个字段的文档都包含在内:

bytes_buffer = base64decode(serialized_array.buffer)
numpy_array = np.frombuffer(bytes_buffer, dtype=eval(serialized_array.dtype))

还要注意标题的存在实际上是如何允许使用甚至更简洁的字段名称,例如而不是

('PosX_mm', '>f4') 

你可以

(('Position along X-axis [mm]', 'x'), '>f4')

后者看起来很麻烦,但考虑使用,例如在pyplot:

plt.plot(a['x'])
plt.ylabel(a.dtype.fields['x'][2])

请注意这不仅具有可读性,而且还降低了重构所有字段名称'x'的风险,因为缺少文档/标题迫使您按顺序更改字段名称避免误解。标题字符串可以在数据来源的位置进行修改,更改只是通过管道传播,而不必更改任何其他源代码。

2 个答案:

答案 0 :(得分:1)

所以你的dtype是:

In [68]: dt3=np.dtype([(('title 1', 'x'), '|i1'), (('title 2', 'y'), '>f4')])
In [69]: dt3
Out[69]: dtype([(('title 1', 'x'), 'i1'), (('title 2', 'y'), '>f4')])
In [70]: dt3.descr
Out[70]: [(('title 1', 'x'), '|i1'), (('title 2', 'y'), '>f4')]
In [71]: dt3.fields
Out[71]: 
mappingproxy({'title 1': (dtype('int8'), 0, 'title 1'),
              'title 2': (dtype('>f4'), 1, 'title 2'),
              'x': (dtype('int8'), 0, 'title 1'),
              'y': (dtype('>f4'), 1, 'title 2')})
In [72]: dt3.names
Out[72]: ('x', 'y')

在文档中,titles通常以这种格式提供:

In [79]: np.dtype({'names':['x','y'],
                   'formats':['|i1','>f4'],
                   'titles':['title 1','title 2']})
Out[79]: dtype([(('title 1', 'x'), 'i1'), (('title 2', 'y'), '>f4')])

标题是罕见的功能,因此更容易包含粗糙的边缘,不符合用户期望的东西。

那么标题的用途是什么?一种可能性是他们提供替代的字段访问密钥:

In [82]: a['x']
Out[82]: array([0], dtype=int8)
In [83]: a['title 1']
Out[83]: array([0], dtype=int8)

请注意,在fields字典中,有标题和名称的条目。

使用字段名称或标题进行索引会返回一个包含更简单dtype的数组,在本例中为native dtype:

In [86]: a['title 1'].dtype
Out[86]: dtype('int8')
In [87]: a['title 1'].dtype.descr
Out[87]: [('', '|i1')]
In [88]: a['title 1'].dtype.fields
In [94]: a['title 1'].dtype.isnative
Out[94]: True
In [96]: a['title 1'].dtype.type
Out[96]: numpy.int8
In [97]: dt3.type
Out[97]: numpy.void

化合物dtypes也可以嵌套,例如

In [98]: np.dtype([('foo',dt3)])
Out[98]: dtype([('foo', [(('title 1', 'x'), 'i1'), (('title 2', 'y'), '>f4')])])

并使用' foo'将其编入索引将生成dt3 dtype。

在任何情况下,使用字段名称或标题进行索引都会在嵌套中逐步降低级别,并且不会保留偏移量或标题。这些是父dtype的属性dt3,而不是sub-dtype的属性。

使用列表建立索引会创建一个新的dtype,其中包含名称,但没有标题:

In [102]: a[['x']]
Out[102]: 
array([(0,)],
      dtype=[('x', 'i1')])
In [103]: a[['x','title 1']]
Out[103]: 
array([(0, 0)],
      dtype=[('x', 'i1'), ('title 1', 'i1')])

我可能没有添加任何新信息,但它至少向我澄清了标题的作用。

如果您需要在新视图中使用标题,那么您必须明确提供标题。例如:

In [113]: dt3x = np.dtype([(('title 1', 'x'), '|i1')])
In [114]: a['x'].view(dt3x)
Out[114]: 
array([(0,)],
      dtype=[(('title 1', 'x'), 'i1')])
In [115]: _['title 1']
Out[115]: array([0], dtype=int8)

说到嵌套的dtypes,我们可以保留标题1'如果它是嵌套的

In [117]: dt4=np.dtype([('x', np.dtype([('title 1','|i1')])), ('y', np.dtype([('
     ...: title 2', '>f4')]))])
In [118]: dt4
Out[118]: dtype([('x', [('title 1', 'i1')]), ('y', [('title 2', '>f4')])])
In [119]: b = np.zeros((2,), dt4)
In [120]: b
Out[120]: 
array([((0,), ( 0.,)), ((0,), ( 0.,))],
      dtype=[('x', [('title 1', 'i1')]), ('y', [('title 2', '>f4')])])
In [121]: b['x']
Out[121]: 
array([(0,), (0,)],
      dtype=[('title 1', 'i1')])
In [122]: b['x']['title 1']
Out[122]: array([0, 0], dtype=int8)

答案 1 :(得分:0)

我曾将此报告为issue,Allan Haldane报告fixed