NumPy数组的原地类型转换

140 投票
7 回答
142650 浏览
提问于 2025-04-16 08:14

给定一个包含 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])

撰写回答