【问题标题】:Changing numpy structured array dtype names and formats更改 numpy 结构化数组 dtype 名称和格式
【发布时间】:2016-08-12 14:55:46
【问题描述】:

我正在用 numpy 中的结构化数组做一些工作(我最终将转换为 pandas 数据框)。

现在,我通过读入一些数据(实际上是对一些数据进行映射)然后通过用户指定的约束对其进行过滤来生成这个结构化数组。然后,我想将这些数据从我读取它的形式(一切都是 int 以节省我从中读取它的文件中的空间)转换为更可用的格式,以便我可以进行一些单位转换(即将它上转换为一个浮点数)。

在更改结构化数据类型的过程中,我注意到一个有趣的工件(或其他东西)。假设读取数据会产生与以下创建的相同的结构化数组(请注意,在实际代码中,dtype 更长且更复杂,但这对于 mwe 来说已经足够了):

import numpy as np

names = ['foo', 'bar']
formats = ['i4', 'i4']

dtype = np.dtype({'names': names, 'formats': formats})

data = np.array([(1, 2), (3, 4)], dtype=dtype)
print(data)
print(data.dtype)

这会创建

[(1, 2) (3, 4)]
[('foo', '<i4'), ('bar', '<i4')]

作为结构化数组

现在,假设我想将这两个 dtype 上转换为 double,同时重命名第二个组件。看起来应该很容易

names[1] = 'baz'

formats[0] = np.float
formats[1] = np.float

dtype_new = np.dtype({'names': names, 'formats': formats})

data2 = data.copy().astype(dtype_new)

print(data2)
print(data2.dtype)

但结果出乎意料

(1.0, 0.0) (3.0, 0.0)]
[('foo', '<f8'), ('baz', '<f8')]

第二个组件的数据发生了什么变化?我们可以进行这种转换,但是如果我们拆分它们

dtype_new3 = np.dtype({'names': names, 'formats': formats})

data3 = data.copy().astype(dtype_new3)

print(data3)
print(data3.dtype)

names[1] = 'baz'
data4 = data3.copy()
data4.dtype.names = names

print(data4)
print(data4.dtype)

这会导致正确的输出

[(1.0, 2.0) (3.0, 4.0)]
[('foo', '<f8'), ('bar', '<f8')]
[(1.0, 2.0) (3.0, 4.0)]
[('foo', '<f8'), ('baz', '<f8')]

看来astype使用结构化dtype调用时,numpy会匹配每个组件的名称,然后将指定的类型应用于内容(这里只是猜测,没看源码)。无论如何要一次完成所有转换(即格式的名称和上转换)还是只需要按步骤完成。 (如果需要分步完成,这并不是什么大不了的事,但我觉得很奇怪,没有一个步骤可以做到这一点。)

【问题讨论】:

    标签: python python-3.x numpy structured-array


    【解决方案1】:

    有一个函数库设计用于recarray(以及结构化数组)。它有点隐藏,所以我将不得不进行搜索以找到它。它具有重命名字段,添加和删除字段等功能。一般的操作模式是使用目标dtype创建一个新数组,然后一个接一个地复制字段。由于数组通常包含许多元素和少量字段,因此这不会减慢速度。

    看起来这个astype 方法正在使用其中的一些代码,或者可能是行为相同的编译代码。

    所以是的,看起来我们需要在单独的步骤中更改字段 dtypes 和名称。

    In [1279]: data=np.array([(1,2),(3,4)],dtype='i,i')
    In [1280]: data
    Out[1280]: 
    array([(1, 2), (3, 4)], 
          dtype=[('f0', '<i4'), ('f1', '<i4')])
    In [1281]: dataf=data.astype('f8,f8')     # change dtype, same default names
    In [1282]: dataf
    Out[1282]: 
    array([(1.0, 2.0), (3.0, 4.0)], 
          dtype=[('f0', '<f8'), ('f1', '<f8')])
    

    简单的名称更改:

    In [1284]: dataf.dtype.names=['one','two'] 
    In [1285]: dataf
    Out[1285]: 
    array([(1.0, 2.0), (3.0, 4.0)], 
          dtype=[('one', '<f8'), ('two', '<f8')])
    
    In [1286]: data.astype(dataf.dtype)
    Out[1286]: 
    array([(0.0, 0.0), (0.0, 0.0)], 
          dtype=[('one', '<f8'), ('two', '<f8')])
    

    名称中不匹配的astype 生成zero 数组,与np.zeros(data.shape,dataf.dtype) 相同。通过匹配名称,而不是在 dtype 中的位置,我可以重新排序值,甚至添加字段。

    In [1291]: data.astype([('f1','f8'),('f0','f'),('f3','i')])
    Out[1291]: 
    array([(2.0, 1.0, 0), (4.0, 3.0, 0)], 
          dtype=[('f1', '<f8'), ('f0', '<f4'), ('f3', '<i4')])
    

    【讨论】:

      【解决方案2】:

      这似乎在最近的numpy 版本上按预期工作:

      names[1] = 'baz'
      
      formats[0] = float
      formats[1] = float
      
      dtype_new = np.dtype({'names': names, 'formats': formats})
      
      data2 = data.copy().astype(dtype_new)
      
      print(data2)
      print(data2.dtype)
      

      结果

      [(1., 2.) (3., 4.)]
      [('foo', '<f8'), ('baz', '<f8')]
      

      这似乎与numpy 的更改有关,以便在执行操作时按位置而不是按名称匹配结构化数组字段(请参阅numpy PR#6053: “MAINT: struct assignment "by field position", multi-field indices return views”)。这个问题的相关错误报告似乎是issue #7058: “astype converts numpy array values to 0.0 for structured dtype”

      如果这确实是相关的更改,那么修复/实现它的 numpy 版本应该是 v1.14.0,请参阅 release notes for numpy 1.14.0: “Changes – Multiple-field indexing/assignment of structured arrays”

      【讨论】:

      • 我可以确认它在 1.17.2 中也可以按预期工作。我不确定如何处理不再适用的旧问题的“已接受”答案,因此如果有人想对此进行权衡,我可以进行必要的更改。也许在问题的顶部添加一条说明它不再适用?
      猜你喜欢
      • 1970-01-01
      • 2018-04-07
      • 2017-01-16
      • 1970-01-01
      • 1970-01-01
      • 2011-04-23
      • 2023-03-16
      • 1970-01-01
      • 2022-10-12
      相关资源
      最近更新 更多