Numpy astype“upcasting”数组,而不是跨列应用dtypes

时间:2018-04-30 12:09:29

标签: python arrays numpy

我有一个2D numpy数组,我想在每列中应用特定的expand

dtype

我期待第41行应用我想要的a = np.arange(25).reshape((5,5)) In [40]: a Out[40]: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]]) In [41]: a.astype(dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')]) ,而是通过创建一个新轴“upcast”,为每个dtypes复制整个数组一次:

dtype

为什么会发生这种情况,因为dtypes的数量与列数匹配(所以我不期望向上转换)?

如何在内存中使用现有数组并应用每列dtypes,就像我在第41行所预期的那样?感谢。

3 个答案:

答案 0 :(得分:2)

正如@senderle正确指出的那样,您很少需要(150, 5),但这里有一个可行的解决方案,几乎就地为了好玩而做到这一点。您需要做的唯一修改是确保您的类型大小相同。

view

如果我们要做非推荐的事情,您也可以在获取视图后插入一行a = np.arange(25, dtype='<i4').reshape((5,5)) b = a.view(dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')]) b['score'] = a[:, -2, np.newaxis].astype('<f4') b['auc'] = a[:, -1, np.newaxis].astype('<f4') ,以消除b.shape = (5,)保留的额外维度,并使下面的分配更简单。< / p>

这将为您提供一个视图a,它具有所有需要的属性,但当然会弄乱b的内容:

a

答案 1 :(得分:2)

以下是使用np.rec.fromarrays的解决方法:

>>> dtype = [('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')]
>>> np.rec.fromarrays(a.T, dtype=dtype)
rec.array([( 0,  1,  2,  3.,  4.), ( 5,  6,  7,  8.,  9.),
           (10, 11, 12, 13., 14.), (15, 16, 17, 18., 19.),
           (20, 21, 22, 23., 24.)],
          dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])

这是recarray,但如果需要,我们可以投放到ndarray。另外,dtype是np.record我们需要(查看)将其转换为void以获得&#34;清洁&#34; numpy结果。

>>> np.asarray(np.rec.fromarrays(a.T, dtype=dtype)).view(dtype)
array([( 0,  1,  2,  3.,  4.), ( 5,  6,  7,  8.,  9.),
       (10, 11, 12, 13., 14.), (15, 16, 17, 18., 19.),
       (20, 21, 22, 23., 24.)],
      dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])

答案 2 :(得分:1)

这是我从未遇到过的一个奇怪的案例,但我相信答案与一般情况下numpy仅支持few forms of assignment结构化数组这一事实有关。< / p>

在这种特殊情况下,我认为numpy遵循用于scalar assignment的结构化数组的约定,然后在整个输入数组上广播赋值,以生成与原始阵列。

为什么限制?

我认为结构化数组的赋值形式是有限的,因为&#34;列&#34;结构化数组与普通2-d数组的列不太相似。实际上,将十行三列结构化数组视为原子行类型的十个实例的 1-d 数组更有意义。

这些原子行类型称为&#34; structured scalars&#34;。它们具有固定的内部存储器布局,无法动态重新整形,因此以与二维阵列的行相同的方式处理它们并不合理。

如何创建现有数组的结构化视图

老实说,我不知道!如果我找到一个好方法,我会更新这个答案。但我不认为我会找到一个好方法,因为如上所述,结构化标量有自己独特的记忆布局。使用具有正确布局的缓冲区来破解某些东西是可能的,但是你要深入numpy内部来做这件事,这并不理想。话虽如此,请参阅疯狂物理学家的this answer,他的表现比我想象的要优雅得多。

还值得一提的是astype creates a copy by default。您可以传递copy=False,但如果某些要求不满意,则numpy可能会复制。

替代...

我很少发现我实际上需要一个视图;经常创建副本不会导致性能发生明显变化。我解决这个问题的第一种方法就是使用记录数组的标准赋值策略之一。在这种情况下,这可能意味着使用subarray assignment。首先我们创建数组。 注意元组。它们是预期行为所必需的。

>>> a = np.array([(1, 2), (3, 4)], dtype=[('x', 'f8'), ('y', 'i8')])
>>> a
array([(1., 2), (3., 4)], dtype=[('x', '<f8'), ('y', '<i8')])

现在,如果我们尝试将普通的二维数组分配给a,我们会收到错误:

>>> a[:] = np.array([[11, 22], [33, 44]])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not broadcast input array from shape (2,2) into shape (2)

但我们可以轻松地以列方式分配:

>>> a['x'] = [11, 22]
>>> a['y'] = [33, 44]
>>> a
array([(11., 33), (22., 44)], dtype=[('x', '<f8'), ('y', '<i8')])

我们也可以使用Python tuples。这会覆盖整个数组:

>>> a[:] = [(111, 222), (333, 444)]
>>> a
array([(111., 222), (333., 444)], dtype=[('x', '<f8'), ('y', '<i8')])

我们还可以使用元组逐行分配数据:

>>> a[1] = (3333, 4444)
>>> a
array([( 111.,  222), (3333., 4444)], dtype=[('x', '<f8'), ('y', '<i8')])

同样,如果我们尝试传递列表或数组,则会失败:

>>> a[1] = [3333, 4444]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: setting an array element with a sequence.
>>> a[1] = np.array([3333, 4444])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: setting an array element with a sequence.

最后,请注意,当我们尝试从嵌套列表或astype数组创建结构化数组时,我们会看到您使用numpy看到的相同行为。 numpy只是根据数据类型广播输入数组,生成 2-d 结构化标量数组:

>>> a
array([[(1., 1), (2., 2)],
       [(3., 3), (4., 4)]], dtype=[('x', '<f8'), ('y', '<i8')])
>>> a = np.array(np.array([[1, 2], [3, 4]]), dtype=[('x', 'f8'), ('y', 'i8')])
>>> a
array([[(1., 1), (2., 2)],
       [(3., 3), (4., 4)]], dtype=[('x', '<f8'), ('y', '<i8')])

如果您的目标只是创建一个新数组,那么请查看this question的答案。它们涵盖了一些有用的方法,包括numpy.core.records.fromarraysnumpy.core.records.fromrecords。另请参阅Paul Panzer的答案,其中讨论了如何创建新记录数组(允许对列进行属性访问的结构化数组)。