numpy recarray append_fields:无法附加包含日期时间的numpy数组

3
我有一个包含各种字段的 recarray,我想将一个 datetime 对象数组附加到它上面。
然而,似乎 numpy.lib.recfunctions 中的 append_fields 函数不允许我添加对象数组。
下面是一些示例代码:
import numpy as np
import datetime
import numpy.lib.recfunctions as recfun

dtype= np.dtype([('WIND_WAVE_HGHT', '<f4'), ('WIND_WAVE_PERD', '<f4')])
obs = np.array([(0.1,10.0),(0.2,11.0),(0.3,12.0)], dtype=dtype)

dates = np.array([datetime.datetime(2001,1,1,0),
    datetime.datetime(2001,1,1,0),
    datetime.datetime(2001,1,1,0)])

# This doesn't work:
recfun.append_fields(obs,'obdate',dates,dtypes=np.object)

我不断收到错误消息 TypeError: Cannot change data-type for object array.

似乎只有 np.object 数组存在这个问题,因为我可以正常添加其他字段。我是否遗漏了什么?


obs 最初是一个空的 recarray,然后变成了一个列表。这不是向 recarray 添加值的方法。对于 append 字段,您考虑过使用 np.datetime64 吗? - hpaulj
@hpaulj 抱歉 - 我的错误; 已经修复了obs数组初始化。我不能使用np.datetime64,因为它是一个期望datetime对象的更大代码集的一部分。 - ccbunney
dtype是指日期的数据类型。 - hpaulj
@hpaulj dates 的数据类型为 np.object - ccbunney
我需要检查函数的代码。或者更简单的方法是创建自己的“out”数组,并复制字段。 - hpaulj
1个回答

4

问题

In [143]: recfun.append_fields(obs,'test',np.array([None,[],1]))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-143-5c3de23b09f7> in <module>()
----> 1 recfun.append_fields(obs,'test',np.array([None,[],1]))

/usr/local/lib/python3.5/dist-packages/numpy/lib/recfunctions.py in append_fields(base, names, data, dtypes, fill_value, usemask, asrecarray)
    615     if dtypes is None:
    616         data = [np.array(a, copy=False, subok=True) for a in data]
--> 617         data = [a.view([(name, a.dtype)]) for (name, a) in zip(names, data)]
    618     else:
    619         if not isinstance(dtypes, (tuple, list)):

/usr/local/lib/python3.5/dist-packages/numpy/lib/recfunctions.py in <listcomp>(.0)
    615     if dtypes is None:
    616         data = [np.array(a, copy=False, subok=True) for a in data]
--> 617         data = [a.view([(name, a.dtype)]) for (name, a) in zip(names, data)]
    618     else:
    619         if not isinstance(dtypes, (tuple, list)):

/usr/local/lib/python3.5/dist-packages/numpy/core/_internal.py in _view_is_safe(oldtype, newtype)
    363 
    364     if newtype.hasobject or oldtype.hasobject:
--> 365         raise TypeError("Cannot change data-type for object array.")
    366     return
    367 

TypeError: Cannot change data-type for object array.

所以问题在于这个 a.view([(name, a.dtype)]) 表达式。它试图从 a 中创建一个单字段结构化数组。这适用于 int 和 str 等 dtypes,但对于 object 则失败。这个失败是在核心的 view 处理中发生的,所以不太可能改变。
In [148]: x=np.arange(3)

In [149]: x.view([('test', x.dtype)])
Out[149]: 
array([(0,), (1,), (2,)], 
      dtype=[('test', '<i4')])

In [150]: x=np.array(['one','two'])

In [151]: x.view([('test', x.dtype)])
Out[151]: 
array([('one',), ('two',)], 
      dtype=[('test', '<U3')])

In [152]: x=np.array([[1],[1,2]])

In [153]: x
Out[153]: array([[1], [1, 2]], dtype=object)

In [154]: x.view([('test', x.dtype)])
...
TypeError: Cannot change data-type for object array.
recfunctions需要单独加载,这表明它有点过时,使用不多,并且没有得到积极的开发。我没有详细检查代码,但我怀疑修复方法可能是一个权宜之计。

解决方法

以下是一种从头开始添加新字段的方法。它执行与append_fields相同的基本操作:

定义一个新的dtype,使用obs和新字段名称和dtype:

In [158]: obs.dtype.descr
Out[158]: [('WIND_WAVE_HGHT', '<f4'), ('WIND_WAVE_PERD', '<f4')]

In [159]: obs.dtype.descr+[('TEST',object)]
Out[159]: [('WIND_WAVE_HGHT', '<f4'), ('WIND_WAVE_PERD', '<f4'), ('TEST', object)]

In [160]: dt1  =np.dtype(obs.dtype.descr+[('TEST',object)])

创建一个空目标数组,通过字段名称复制数据来填充它:
In [161]: newobs = np.empty(obs.shape, dtype=dt1)    
In [162]: for n in obs.dtype.names:
     ...:     newobs[n]=obs[n]

In [167]: dates
Out[167]: 
array([datetime.datetime(2001, 1, 1, 0, 0),
       datetime.datetime(2001, 1, 1, 0, 0),
       datetime.datetime(2001, 1, 1, 0, 0)], dtype=object)

In [168]: newobs['TEST']=dates

In [169]: newobs
Out[169]: 
array([( 0.1       ,  10., datetime.datetime(2001, 1, 1, 0, 0)),
       ( 0.2       ,  11., datetime.datetime(2001, 1, 1, 0, 0)),
       ( 0.30000001,  12., datetime.datetime(2001, 1, 1, 0, 0))], 
      dtype=[('WIND_WAVE_HGHT', '<f4'), ('WIND_WAVE_PERD', '<f4'), ('TEST', 'O')])

datetime64替代方案

使用numpy原生时间戳时,可以使用append方法。

In [179]: dates64 = dates.astype('datetime64[D]')

In [180]: recfun.append_fields(obs,'test',dates64,usemask=False)
Out[180]: 
array([( 0.1       ,  10., '2001-01-01'),
       ( 0.2       ,  11., '2001-01-01'), ( 0.30000001,  12., '2001-01-01')], 
      dtype=[('WIND_WAVE_HGHT', '<f4'), ('WIND_WAVE_PERD', '<f4'), ('test', '<M8[D]')])
< p > append_fields 具有一些我版本中没有的功能 - 填充值、掩码数组、recarray等。

结构化日期数组

我可以创建一个包含日期的结构化数组。

In [197]: sdates = np.array([(i,) for i in dates],dtype=[('test',object)])
In [198]: sdates
Out[198]: 
array([(datetime.datetime(2001, 1, 1, 0, 0),),
       (datetime.datetime(2001, 1, 1, 0, 0),),
       (datetime.datetime(2001, 1, 1, 0, 0),)], 
      dtype=[('test', 'O')])

一定有一个函数能够合并已存在的数组字段,但是我找不到它。

之前的工作

这感觉很熟悉:

https://github.com/numpy/numpy/issues/2346

向大小为1的结构化数组附加字段时出现TypeError

将日期时间字段添加到recarray中


感谢您抽出时间查看此内容。感觉numpy中仍有一些需要修复/优化的功能!我可能会采用您的第二个建议并构建一个新数组...这可能就是append_fields在后台执行的操作 :) - ccbunney

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