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'的风险,因为缺少文档/标题迫使您按顺序更改字段名称避免误解。标题字符串可以在数据来源的位置进行修改,更改只是通过管道传播,而不必更改任何其他源代码。
答案 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)