NumPy数组的原地类型转换
给定一个包含 int32
类型的 NumPy 数组,我该如何就地将它转换为 float32
呢?也就是说,我想要这样做:
a = a.astype(numpy.float32)
而不复制这个数组,因为它很大。
这样做的原因是我有两个算法来计算 a
。其中一个算法返回的是 int32
类型的数组,另一个返回的是 float32
类型的数组(这就是这两个算法的本质区别)。后续的所有计算都假设 a
是一个 float32
类型的数组。
目前我是在一个通过 ctypes
调用的 C 函数中进行转换。有没有办法在 Python 中做到这一点呢?
7 个回答
14
你可以不通过转换直接改变数组的类型,方法如下:
a.dtype = numpy.float32
但首先,你得把所有的整数换成可以被理解为对应浮点数的东西。一个非常慢的方法是使用Python的 struct
模块,像这样:
def toi(i):
return struct.unpack('i',struct.pack('f',float(i)))[0]
...这个方法要应用到你数组中的每一个元素上。
不过,也许有个更快的方法是利用numpy的ctypeslib工具(我对这个不太熟悉)。
- 编辑 -
因为ctypeslib似乎不太好用,所以我会用常规的 numpy.astype
方法来进行转换,但要确保每次处理的块大小在你的内存限制之内:
a[0:10000] = a[0:10000].astype('float32').view('int32')
...然后在完成后改变数据类型。
这里有一个函数可以完成这个任务,适用于任何兼容的数据类型(只适用于大小相同的数据类型),并且可以处理任意形状的数组,用户可以控制块的大小:
import numpy
def astype_inplace(a, dtype, blocksize=10000):
oldtype = a.dtype
newtype = numpy.dtype(dtype)
assert oldtype.itemsize is newtype.itemsize
for idx in xrange(0, a.size, blocksize):
a.flat[idx:idx + blocksize] = \
a.flat[idx:idx + blocksize].astype(newtype).view(oldtype)
a.dtype = newtype
a = numpy.random.randint(100,size=100).reshape((10,10))
print a
astype_inplace(a, 'float32')
print a
167
更新:这个函数只有在可以的情况下才会避免复制,所以这不是这个问题的正确答案。unutbu的回答才是正确的。
a = a.astype(numpy.float32, copy=False)
numpy的astype有一个复制标志。那我们为什么不应该使用它呢?
118
你可以创建一个不同数据类型的视图,然后直接在这个视图中进行复制:
import numpy as np
x = np.arange(10, dtype='int32')
y = x.view('float32')
y[:] = x
print(y)
这样会得到
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.], dtype=float32)
为了说明这个复制是在原地进行的,注意从 x
复制到 y
的过程中,x
的值也发生了变化:
print(x)
打印结果是
array([ 0, 1065353216, 1073741824, 1077936128, 1082130432,
1084227584, 1086324736, 1088421888, 1090519040, 1091567616])