NumPy数组的就地类型转换


127

给定一个NumPy数组int32,如何将其转换为float32 原位?所以基本上,我想做

a = a.astype(numpy.float32)

而不复制阵列。好大

这样做的原因是我有两种算法来计算a。其中一个返回一个数组int32,另一个返回一个数组float32(这是两种不同算法固有的)。所有进一步的计算都假定a是的数组float32

目前,我在C函数中通过via进行转换ctypes。有没有办法在Python中做到这一点?


使用ctypes既是“在Python”的使用numpy。:)
Karl Knechtel 2010年

3
@Karl:不,因为我必须自己编写和编译C函数。
Sven Marnach 2010年

哦,我明白了。我认为您可能对此很满意。
Karl Knechtel 2010年

3
@Andrew:有很多方法可以判断它是否返回副本。其中之一是阅读文档
Sven Marnach 2011年

1
就地简单意味着“使用与原始数组相同的内存”。看一下已接受的答案-最后一部分显示新值确实覆盖了相同的内存。
Sven Marnach 2012年

Answers:


110

您可以使用不同的dtype创建视图,然后就地复制到视图中:

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])

26
对于那些想要在不同字节大小(例如32到16位)的dtype之间进行转换的人(如我)注意:该方法失败,因为y.size <> x.size。一旦您考虑了,就
合乎

此解决方案是否适用于某些较旧版本的Numpy?当我np.arange(10, dtype=np.int32).view(np.float32)对Numpy 1.8.2进行操作时,我得到array([ 0.00000000e+00, 1.40129846e-45, ... [snip] ... 1.26116862e-44], dtype=float32)
Bas Swinckels

3
@BasSwinckels:可以预期。分配时发生转换y[:] = x
unutbu

阐明有关原始答案和@Juh_所引用的项大小(位数)的观点,例如:a = np.arange(10, dtype='float32'); b = a[::-1]; c = np.vstack((a,b)); d = c.view('float64')此代码采用10 + 10 float32,结果为10,而不是20 float64
dcanelhas'Aug

1
这种就地更改可以节省内存使用,但比简单的x.astype(float)转换要慢。除非您的脚本在MemoryError上接壤,否则我不建议这样做。
hpaulj

158

更新:此功能仅在可能的情况下避免复制,因此这不是此问题的正确答案。unutbu的答案是正确的。


a = a.astype(numpy.float32, copy=False)

numpy astype具有复制标志。我们为什么不应该使用它?


14
一旦NumPy发行版支持此参数,我们当然可以使用它,但是当前仅在development分支中可用。在我问这个问题时,它根本不存在。
Sven Marnach

2
@SvenMarnach至少在我的版本(1.7.1)中现在受支持。
PhilMacKay

它在最新的numpy版本的python3.3中似乎可以完美工作。
CHM

1
我发现这是围绕700X比= a.view((浮动,LEN(a.dtype.names)))更慢
JJ

14
复制标志仅表示如果更改可以不进行复制,则更改将不进行复制。但是,它的类型不同,它仍将始终复制。
coderforlife 2015年

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')

...然后在完成后更改dtype。

这是一个功能,可以完成所有兼容dtypes的任务(仅适用于具有相同大小项目的dtypes),并通过用户控制块大小来处理任意形状的数组:

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

1
感谢您的回答。老实说,我认为这对大型阵列不是很有用-太慢了。将数组数据重新解释为其他类型很容易-例如通过调用a.view(numpy.float32)。困难的部分实际上是转换数据。 numpy.ctypeslib仅有助于重新解释数据,而无助于实际转换数据。
Sven Marnach 2010年

好。我不确定您的内存/处理器限制是多少。看到我的编辑。
保罗2010年

感谢更新。逐块执行是一个好主意-可能是使用当前NumPy接口可以获得的最佳效果。但是在这种情况下,我可能会坚持使用当前的ctypes解决方案。
Sven Marnach 2010年

-1
import numpy as np
arr_float = np.arange(10, dtype=np.float32)
arr_int = arr_float.view(np.float32)

使用view()和参数'dtype'更改数组。


这个问题的目的是实际上数据转换到位。将最后一行中的类型更正后int,此答案只会将现有数据重新解释为其他类型,这不是我要的。
Sven Marnach

你什么意思?dtype只是内存中数据的外观,它确实起作用。但是在np.astype中,参数'casting'可以控制转换方法默认的'unsafe'。
蒋志强

是的,我同意第一个被接受的答案。但是arr_.astype(new_dtype,copy = False)仍返回新分配的数组。如何满足dtypeorder以及subok要求返回数组的副本?我没有解决。
蒋志强

-5

用这个:

In [105]: a
Out[105]: 
array([[15, 30, 88, 31, 33],
       [53, 38, 54, 47, 56],
       [67,  2, 74, 10, 16],
       [86, 33, 15, 51, 32],
       [32, 47, 76, 15, 81]], dtype=int32)

In [106]: float32(a)
Out[106]: 
array([[ 15.,  30.,  88.,  31.,  33.],
       [ 53.,  38.,  54.,  47.,  56.],
       [ 67.,   2.,  74.,  10.,  16.],
       [ 86.,  33.,  15.,  51.,  32.],
       [ 32.,  47.,  76.,  15.,  81.]], dtype=float32)

5
您确定不是副本吗?您可以检查一下并解释更多吗?
米歇尔·达米科2015年

-5

a = np.subtract(a, 0., dtype=np.float32)


1
尽管此代码段可能是解决方案,但包括说明确实有助于提高帖子的质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
Sebastialonso

为什么这应该是就地转换?numpy.subtract正在返回副本,不是吗?只有名称可a用于其他数据块...如果我对此有误,请解释。
koffein

感谢您指出这一点,看来您是正确的-已生成副本。
MIO
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.