Python 添加和访问numpy结构化数组的对象类型字段

Python 添加和访问numpy结构化数组的对象类型字段,python,numpy,structured-array,Python,Numpy,Structured Array,我正在使用NUMPY1.16.2 简而言之,我想知道如何将对象类型字段添加到结构化数组。通过recfunctions模块的标准方法会抛出错误,我想这是有原因的。因此,我想知道我的变通方法是否有问题。此外,我想了解为什么需要这种变通方法,以及在访问新创建的阵列时是否需要格外小心 现在详细信息来了: 我有一个numpy结构阵列: import numpy as np a = np.zeros(3, dtype={'names':['A','B','C'], 'formats':['int','int

我正在使用NUMPY1.16.2

简而言之,我想知道如何将对象类型字段添加到结构化数组。通过
recfunctions
模块的标准方法会抛出错误,我想这是有原因的。因此,我想知道我的变通方法是否有问题。此外,我想了解为什么需要这种变通方法,以及在访问新创建的阵列时是否需要格外小心

现在详细信息来了:

我有一个numpy结构阵列:

import numpy as np
a = np.zeros(3, dtype={'names':['A','B','C'], 'formats':['int','int','float']})
for i in range(len(a)):
    a[i] = i
我想在数组
a
中添加另一个类型为
object
的字段“test”。执行此操作的标准方法是使用numpy的
recfunctions
模块:

import numpy.lib.recfunctions as rf
b = rf.append_fields(a, "test", [None]*len(a)) 
此代码引发一个错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-4a7be4f94686> in <module>
----> 1 rf.append_fields(a, "test", [None]*len(a))

D:\_Programme\Anaconda3\lib\site-packages\numpy\lib\recfunctions.py in append_fields(base, names, data, dtypes, fill_value, usemask, asrecarray)
    718     if dtypes is None:
    719         data = [np.array(a, copy=False, subok=True) for a in data]
--> 720         data = [a.view([(name, a.dtype)]) for (name, a) in zip(names, data)]
    721     else:
    722         if not isinstance(dtypes, (tuple, list)):

D:\_Programme\Anaconda3\lib\site-packages\numpy\lib\recfunctions.py in <listcomp>(.0)
    718     if dtypes is None:
    719         data = [np.array(a, copy=False, subok=True) for a in data]
--> 720         data = [a.view([(name, a.dtype)]) for (name, a) in zip(names, data)]
    721     else:
    722         if not isinstance(dtypes, (tuple, list)):

D:\_Programme\Anaconda3\lib\site-packages\numpy\core\_internal.py in _view_is_safe(oldtype, newtype)
    492 
    493     if newtype.hasobject or oldtype.hasobject:
--> 494         raise TypeError("Cannot change data-type for object array.")
    495     return
    496 

TypeError: Cannot change data-type for object array.
这很有效。不过,我有以下问题:

问题

  • 为什么需要这种解决方法?这只是一个bug吗
  • 使用新阵列
    b
    与使用
    a
    似乎没有什么不同。变量
    c=b[[“A”,“test”]
    显然是
    b
    数据的视图。那么为什么数组
    b
    上的视图不受支持呢?我必须格外小心地对待
    c
按字段名将值从
a
复制到
b

In [168]: for name in a.dtype.names: 
     ...:     b[name] = a[name] 
     ...:                                                                       
In [169]: b                                                                     
Out[169]: 
array([(0, 0, 0., None), (1, 1, 1., None), (2, 2, 2., None)],
      dtype=[('A', '<i8'), ('B', '<i8'), ('C', '<f8'), ('test', 'O')])
rf.append\u字段
在初始化其
输出
数组后使用它

在早期版本中,多字段索引生成一个副本,因此像
b[list(a.dtype.names)]=a
这样的表达式不起作用


我不知道是否值得尝试去弄清楚
rf.append\u字段
在做什么。这些函数有些陈旧,使用不多(请注意特殊导入)。所以他们很可能有bug,或者边缘案例,这些都不起作用。我所研究的函数与我所演示的非常相似——创建一个新的数据类型和结果数组,并按字段名复制数据

在最近的版本中,访问多个字段的方式发生了变化。
recfunctions
中有一些新函数,以便于使用结构化数组,例如
repack\u字段

我不知道这是否适用于
append\u字段
问题。我看到还有一节是关于带对象的结构化数组的,但我没有研究过:

为了防止在numpy.object类型的字段中重击对象指针,numpy当前不允许查看包含对象的结构化数组

这一行显然是指使用
view
方法。通过字段索引创建的视图,无论是单字段列表还是多字段列表,都不受影响


append\u字段中的错误来自此操作:

In [183]: data = np.array([None,None,None])                                          
In [184]: data                                                                       
Out[184]: array([None, None, None], dtype=object)
In [185]: data.view([('test',object)])                                               
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-185-c46c4464b53c> in <module>
----> 1 data.view([('test',object)])

/usr/local/lib/python3.6/dist-packages/numpy/core/_internal.py in _view_is_safe(oldtype, newtype)
    492 
    493     if newtype.hasobject or oldtype.hasobject:
--> 494         raise TypeError("Cannot change data-type for object array.")
    495     return
    496 

TypeError: Cannot change data-type for object array.
但是我没有看到任何能够连接
a
数据的
recfunctions


view
可用于更改
a
的字段名:

In [219]: a.view([('AA',int),('BB',int),('cc',float)])                               
Out[219]: 
array([(0, 0, 0.), (1, 1, 1.), (2, 2, 2.)],
      dtype=[('AA', '<i8'), ('BB', '<i8'), ('cc', '<f8')])

我从一个对象数据类型数组开始,尝试使用
i8
(相同大小的数据类型)查看
view
,我得到了同样的错误。因此,对对象数据类型的
视图
的限制并不局限于结构化数组。在对象指针指向
i8
的情况下,需要这样的限制是有意义的。在将对象指针嵌入复合数据类型的情况下,需要这样的限制可能不是很有说服力。这甚至可能是矫枉过正,或者只是一个简单安全的例子

In [267]: x.dtype                                                                    
Out[267]: dtype('O')
In [268]: x.shape                                                                    
Out[268]: (3,)
In [269]: x.dtype.itemsize                                                           
Out[269]: 8
In [270]: x.view('i8')                                                               
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-270-30c78b13cd10> in <module>
----> 1 x.view('i8')

/usr/local/lib/python3.6/dist-packages/numpy/core/_internal.py in _view_is_safe(oldtype, newtype)
    492 
    493     if newtype.hasobject or oldtype.hasobject:
--> 494         raise TypeError("Cannot change data-type for object array.")
    495     return
    496 

TypeError: Cannot change data-type for object array.
但尝试在
b
或其字段的子集上执行相同操作会产生常见错误:

rf.structured_to_unstructured(b)
rf.structured_to_unstructured(b[['A','B','C']]) 
我必须首先使用
repack
制作无对象副本:

rf.structured_to_unstructured(rf.repack_fields(b[['A','B','C']])) 

我不认为
recfunctions
是标准的。它们是可以使用的实用程序,但您不必使用它们。它们没有被编译,因此不能做任何没有它们不能做的事情。-基本上是相同的问题,尝试使用对象数据类型的
append\u fields
。另一种方法是附加一个
datetime64
字段。谢谢您的回答。你能指出你的解决方案和我最初的不同吗?numpy最近的变化正是我不得不问这个问题的原因。你介意回答我提出的其他问题吗?到目前为止,对我来说唯一新的信息是,
recfunctions
是旧的和非标准的。在您的解决方案和我的解决方案之间可能没有任何真正的区别。我只是想自己思考一下这个问题。谢谢你更新你的答案。我知道错误是在哪里产生的;我把错误信息放在我的问题里。(我真的很感谢您的努力,但您真的读过这个问题吗?)生成的数组的行为是否会与标准数组不同(由于提到的“视图”问题)?您是否在寻求更深入的答案
b.view(b.dtype)
似乎是唯一有效的
.view
表达式。使用
a.view(…)
我可以更改字段名。我不能用
b
。它会引发相同的
\u内部
错误。通过
b
的字段索引创建的
视图
是另一回事。您对
b[list(a.dtype.names)]=a
的使用取决于最新版本,其中多字段索引生成视图。以前的版本复制了一份。我迭代了字段名,这在前面的代码中起作用。参见
rf.递归填充字段(a,b)
In [183]: data = np.array([None,None,None])                                          
In [184]: data                                                                       
Out[184]: array([None, None, None], dtype=object)
In [185]: data.view([('test',object)])                                               
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-185-c46c4464b53c> in <module>
----> 1 data.view([('test',object)])

/usr/local/lib/python3.6/dist-packages/numpy/core/_internal.py in _view_is_safe(oldtype, newtype)
    492 
    493     if newtype.hasobject or oldtype.hasobject:
--> 494         raise TypeError("Cannot change data-type for object array.")
    495     return
    496 

TypeError: Cannot change data-type for object array.
In [186]: np.array([None,None,None], dtype=[('test',object)])                        
Out[186]: array([(None,), (None,), (None,)], dtype=[('test', 'O')])
In [219]: a.view([('AA',int),('BB',int),('cc',float)])                               
Out[219]: 
array([(0, 0, 0.), (1, 1, 1.), (2, 2, 2.)],
      dtype=[('AA', '<i8'), ('BB', '<i8'), ('cc', '<f8')])
In [220]: b.view([('AA',int),('BB',int),('cc',float),('d',object)])                  
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-220-ab0a6e4dd57f> in <module>
----> 1 b.view([('AA',int),('BB',int),('cc',float),('d',object)])

/usr/local/lib/python3.6/dist-packages/numpy/core/_internal.py in _view_is_safe(oldtype, newtype)
    492 
    493     if newtype.hasobject or oldtype.hasobject:
--> 494         raise TypeError("Cannot change data-type for object array.")
    495     return
    496 

TypeError: Cannot change data-type for object array.
In [267]: x.dtype                                                                    
Out[267]: dtype('O')
In [268]: x.shape                                                                    
Out[268]: (3,)
In [269]: x.dtype.itemsize                                                           
Out[269]: 8
In [270]: x.view('i8')                                                               
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-270-30c78b13cd10> in <module>
----> 1 x.view('i8')

/usr/local/lib/python3.6/dist-packages/numpy/core/_internal.py in _view_is_safe(oldtype, newtype)
    492 
    493     if newtype.hasobject or oldtype.hasobject:
--> 494         raise TypeError("Cannot change data-type for object array.")
    495     return
    496 

TypeError: Cannot change data-type for object array.
In [283]: rf.structured_to_unstructured(a)                                           
Out[283]: 
array([[ 3.,  3.,  0.],
       [12., 10.,  1.],
       [ 2.,  2.,  2.]])
rf.structured_to_unstructured(b)
rf.structured_to_unstructured(b[['A','B','C']]) 
rf.structured_to_unstructured(rf.repack_fields(b[['A','B','C']]))