我有一个2D的numpy数组,我想对每个列应用一个特定的dtype。
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')])我期望第41行应用我想要的dtype,但是通过创建一个新的轴,为每个dtype复制整个数组一次,从而实现它的“向上转换”:
Out[41]:
array([[(0, 0, 0, 0.0, 0.0), (1, 1, 1, 1.0, 1.0), (2, 2, 2, 2.0, 2.0),
(3, 3, 3, 3.0, 3.0), (4, 4, 4, 4.0, 4.0)],
[(5, 5, 5, 5.0, 5.0), (6, 6, 6, 6.0, 6.0), (7, 7, 7, 7.0, 7.0),
(8, 8, 8, 8.0, 8.0), (9, 9, 9, 9.0, 9.0)],
[(10, 10, 10, 10.0, 10.0), (11, 11, 11, 11.0, 11.0),
(12, 12, 12, 12.0, 12.0), (13, 13, 13, 13.0, 13.0),
(14, 14, 14, 14.0, 14.0)],
[(15, 15, 15, 15.0, 15.0), (16, 16, 16, 16.0, 16.0),
(17, 17, 17, 17.0, 17.0), (18, 18, 18, 18.0, 18.0),
(19, 19, 19, 19.0, 19.0)],
[(20, 20, 20, 20.0, 20.0), (21, 21, 21, 21.0, 21.0),
(22, 22, 22, 22.0, 22.0), (23, 23, 23, 23.0, 23.0),
(24, 24, 24, 24.0, 24.0)]],
dtype=[('width', '<i4'), ('height', '<i4'), ('depth', '<i4'), ('score', '<f4'), ('auc', '<f4')])既然dtype的数量与列的数量相匹配,为什么会发生这种情况(所以我没有期望向上转换)?
如何像我在第41行中所希望的那样,在内存中使用现有的数组并应用每列的dtype?谢谢。
发布于 2018-04-30 12:59:44
这是我从未遇到过的奇怪的情况,但我相信答案与这样一个事实有关:通常,numpy只支持结构化数组的很少形式的转让。
在这种情况下,我认为numpy遵循了标量分配用于结构化数组的约定,然后在整个输入数组上广播赋值,以生成与原始数组形状相同的结果。
为什么是极限?
我认为结构化数组的赋值形式是有限的,因为结构化数组的“列”与普通二维数组的列不太一样。实际上,将一个十行、三列结构化数组看作一个由原子行类型的十个实例组成的一维数组更有意义。
这些原子行类型称为"结构化标量“。它们有一个固定的内部内存布局,不能被动态地重新塑造,所以用与二维数组的行相同的方式对待它们是没有意义的。
如何创建现有数组的结构化视图
老实说,我不知道!如果我找到了一个好的方法,我会更新这个答案。但是我不认为我会找到一个好的方法,因为如上所述,结构化标量有自己独特的内存布局。可以使用具有正确布局的缓冲区来破解一些东西,但是您需要深入了解numpy内部的内容,这并不理想。话虽如此,请看疯狂物理学家的这个答案,他所做的比我想象的更优雅。
同样值得一提的是,astype 默认情况下创建副本。您可以通过copy=False,但是如果某些需求没有得到满足,numpy可能仍然会复制。
其他选择..。
我很少发现我的实际上需要一个视图;经常创建一个副本不会导致性能上的明显变化。我解决这个问题的第一种方法就是使用记录数组的标准分配策略之一。在这种情况下,这可能意味着使用子阵列分配。首先,我们创建数组。注意到了元组。它们是预期行为所必需的。
>>> 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 元组。这覆盖了整个数组:
>>> 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')])如果您的目标只是创建一个新数组,那么请查看这个问题的答案。它们涵盖了几种有用的方法,包括numpy.core.records.fromarrays和numpy.core.records.fromrecords。还请参阅保罗·潘泽的答案,其中讨论了如何创建新的记录数组(允许属性访问列的结构化数组)。
发布于 2018-04-30 13:32:52
正如@senderle正确地指出的那样,您很少需要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,),以消除a中保留的额外维度,并简化下面的赋值。
这将为您提供一个视图b,它具有所有所需的属性,但当然会搞乱a的内容。
>>> a
array([[ 0, 1, 2, 1077936128, 1082130432],
[ 5, 6, 7, 1090519040, 1091567616],
[ 10, 11, 12, 1095761920, 1096810496],
[ 15, 16, 17, 1099956224, 1100480512],
[ 20, 21, 22, 1102577664, 1103101952]])
>>> b
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')])发布于 2018-04-30 13:51:41
下面是使用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,以获得“干净”的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')])https://stackoverflow.com/questions/50100246
复制相似问题