将结构化数组转换为普通的NumPy数组。

58

我认为答案很明显,但我目前没有看到它。

如何将记录数组转换回普通的ndarray?

假设我有以下简单的结构化数组:

x = np.array([(1.0, 4.0,), (2.0, -1.0)], dtype=[('f0', '<f8'), ('f1', '<f8')])

然后我想将其转换为:

array([[ 1.,  4.],
       [ 2., -1.]])

我尝试了asarrayastype,但没起作用。

更新(已解决:使用float32(f4)代替float64(f8))

好的,我尝试了Robert的解决方案(x.view(np.float64).reshape(x.shape + (-1,))),对于一个简单的数组它完美地起作用。但是对于我想要转换的数组却给出了奇怪的结果:

data = np.array([ (0.014793682843446732, 0.006681123282760382, 0.0, 0.0, 0.0, 0.0008984912419691682, 0.0, 0.013475529849529266, 0.0, 0.0),
       (0.014793682843446732, 0.006681123282760382, 0.0, 0.0, 0.0, 0.0008984912419691682, 0.0, 0.013475529849529266, 0.0, 0.0),
       (0.014776384457945824, 0.006656022742390633, 0.0, 0.0, 0.0, 0.0008901208057068288, 0.0, 0.013350814580917358, 0.0, 0.0),
       (0.011928378604352474, 0.002819152781739831, 0.0, 0.0, 0.0, 0.0012627150863409042, 0.0, 0.018906937912106514, 0.0, 0.0),
       (0.011928378604352474, 0.002819152781739831, 0.0, 0.0, 0.0, 0.001259754877537489, 0.0, 0.01886274479329586, 0.0, 0.0),
       (0.011969991959631443, 0.0028706740122288465, 0.0, 0.0, 0.0, 0.0007433745195157826, 0.0, 0.011164642870426178, 0.0, 0.0)], 
      dtype=[('a_soil', '<f4'), ('b_soil', '<f4'), ('Ea_V', '<f4'), ('Kcc', '<f4'), ('Koc', '<f4'), ('Lmax', '<f4'), ('malfarquhar', '<f4'), ('MRN', '<f4'), ('TCc', '<f4'), ('Vcmax_3', '<f4')])

接着:

data_array = data.view(np.float).reshape(data.shape + (-1,))

给出:

In [8]: data_array
Out[8]: 
array([[  2.28080997e-20,   0.00000000e+00,   2.78023241e-27,
          6.24133580e-18,   0.00000000e+00],
       [  2.28080997e-20,   0.00000000e+00,   2.78023241e-27,
          6.24133580e-18,   0.00000000e+00],
       [  2.21114197e-20,   0.00000000e+00,   2.55866881e-27,
          5.79825816e-18,   0.00000000e+00],
       [  2.04776835e-23,   0.00000000e+00,   3.47457730e-26,
          9.32782857e-17,   0.00000000e+00],
       [  2.04776835e-23,   0.00000000e+00,   3.41189244e-26,
          9.20222417e-17,   0.00000000e+00],
       [  2.32706550e-23,   0.00000000e+00,   4.76375305e-28,
          1.24257748e-18,   0.00000000e+00]])

这是一个包含其他数字和形状的数组,我做错了什么?


np.asanyarray(x)会保持每列的复杂数据类型,否则使用np.array(x.tolist())。 - diliop
你需要将 np.float 替换为 data.dtype[0]。请在问题的末尾更新并发布解决方案,以便读者更加清晰明了。 - Atcold
5个回答

49

最简单的方法可能是

x.view((float, len(x.dtype.names)))

(float通常必须被x中元素的类型替换:x.dtype[0])。这假定所有的元素都有相同的类型。

这种方法可以一步得到常规的numpy.ndarray版本,而不是view(…).reshape(…)方法需要的两步。


1
我会添加一个改进:x.view((x.dtype[0], len(x.dtype.names)))。这样,我们甚至可以定义一个函数来完成这个操作,因为一切都是可参数化的。 - Atcold
答案中的链接已失效。我找不到它最初所指向的页面。 - Eric O. Lebigot
4
我已经使用这种方法有一段时间了,但不幸的是现在我收到了一个警告:"FutureWarning: Numpy has detected that you may be viewing or writing to an array returned by selecting multiple fields in a structured array. This code may break in numpy 1.13 because this will return a view instead of a copy -- see release notes for details." 你认为该如何处理? - bbengfort
我并不预见任何真正的问题。你只需要注意这个变化(实际上是计划在NumPy 1.14中实现),以确保你的代码不会出错。如果警告让你感到困扰,你可以将其关闭 - Eric O. Lebigot
8
在Numpy 1.14版本中,这对我无效。假设我有以下结构化数组:arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b', 'f4'), ('c', 'f4'), ('d', 'f4')])。然后尝试通过out = arr[0].view((np.float32, len(arr.dtype.names)))将其中的4个元素转换为常规数组,结果会产生ValueError: Changing the dtype of a 0d array is only supported if the itemsize is unchanged(值错误:如果项大小未更改,则仅支持更改0维数组的dtype)。 - Alex
显示剩余5条评论

34
[~]
|5> x = np.array([(1.0, 4.0,), (2.0, -1.0)], dtype=[('f0', '<f8'), ('f1', '<f8')])

[~]
|6> x.view(np.float64).reshape(x.shape + (-1,))
array([[ 1.,  4.],
       [ 2., -1.]])

1
@joris:你的数组包含单精度(32位)浮点数。要将相同的内存重新解释为非结构化数组,请在上面的代码中使用.view(np.float32) - Sven Marnach
@joris,正确的,它不会复制。它只是在原始数组的内存上方提供了一个视图。 - Robert Kern
1
不需要使用元组构造: reshape(x.shape + (-1,)) 可以简化为 reshape(x.shape, -1)。我更新了答案。 - Eric O. Lebigot
1
@RobertKern:在reshape()的文档字符串中确实没有记录。然而,在官方文档的许多地方都使用了它,所以我认为它是相当正式的(例如,在http://scipy.org/Numpy_Example_List可以找到许多`reshape(i, j, k)`的实例)。我已经向NumPy社区询问了这个问题(http://projects.scipy.org/numpy/ticket/2110)。 - Eric O. Lebigot
@EOL 我仍然认为 reshape(i,j,k) 是一个不一致的缺陷,应该避免使用,但它已经存在了很长时间,我不会强烈要求将其删除。我从未在代码或文档中看到过 reshape(some_tuple, j)。无论如何,我不希望显得我在推荐它。 - Robert Kern
显示剩余4条评论

18
在处理多字段索引的方式发生变化的同时,numpy提供了两个新函数来帮助转换结构化数组:numpy.lib.recfunctions中的structured_to_unstructuredunstructured_to_structured。另一个新功能是repack_fields。从1.16版本发布说明中可以看到,多字段视图返回的是一个视图而不是副本。使用多个字段进行索引的结构化数组(如arr[['f1', 'f3']])将返回原始数组的视图,而不是副本。返回的视图通常具有与原始数组中介入字段对应的额外填充字节,这与以前不同,这会影响类似于arr[['f1', 'f3']].view('float64')的代码。自numpy 1.7以来,命中此路径的操作已发出FutureWarnings。在1.12中添加了关于这种更改的其他FutureWarnings。为帮助用户更新他们的代码以适应这些更改,numpy.lib.recfunctions模块中添加了许多函数,这些函数可以安全地执行此类操作。例如,上面的代码可以替换为structured_to_unstructured(arr[['f1','f3']],dtype='float64')。请参阅用户指南中的“访问多个字段”部分。

这是用户指南中访问多个字段部分的链接。 - djvg
1
这是一个比被选中的答案更好的回答。使用.view(float)无法提取和合并字段子集,而structured_to_unstructured可以。 - jeromerg
1
最佳答案!它运行良好。 from numpy.lib.recfunctions import structured_to_unstructured structured_to_unstructured(X) - kabhel
很棒的答案。这种方式感觉上是最“正式”的做法。比如,用plyfile库从PLY文件中读取点云数据非常适合。 - Ray

15
np.array(x.tolist())
array([[ 1.,  4.],
      [ 2., -1.]])

但也许有更好的方法...


7
这很慢,因为你首先需要将一个高效打包的NumPy数组转换为普通的Python列表。官方方法要快得多(请参见我的答案)。 - Eric O. Lebigot
这是最容易记住的...令人惊讶的是没有 x.toArray() 方法... - Atcold
1
不要在不必要的情况下创建Python列表,它们的开销更大。 - RBF06
3
确实很慢,但这是唯一可靠的答案。其他方法对我不起作用(numpy 1.14.x)。 - Jan Christoph Terasa
效率/速度并不总是那么重要。在我看来,这种解决方案比高效但晦涩的“视图”方法更易读。 - djvg
3
当结构化数组具有多个数据类型时,此方法有效,而其他方法在这种情况下会失败。对于单元测试非常有用,速度不重要,只需让比较工作即可。 - David Parks

0
一个非常简单的解决方案是使用root_numpy的函数rec2array:
np_array = rec2array(x)

root_numpy 已经被弃用,但是 rec2array 代码仍然有用(源代码 在这里):

def rec2array(rec, fields=None):

  simplify = False

  if fields is None:
      fields = rec.dtype.names
  elif isinstance(fields, string_types):
      fields = [fields]
      simplify = True

  # Creates a copy and casts all data to the same type
  arr = np.dstack([rec[field] for field in fields])

  # Check for array-type fields. If none, then remove outer dimension.
  # Only need to check first field since np.dstack will anyway raise an
  # exception if the shapes don't match
  # np.dstack will also fail if fields is an empty list
  if not rec.dtype[fields[0]].shape:
      arr = arr[0]

  if simplify:
      # remove last dimension (will be of size 1)
      arr = arr.reshape(arr.shape[:-1])

  return arr

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