我是编程新手,并且...在阅读教程和在 jupyter-notebook 上进行实验时......我想转换 numpy 数组的 dtype 如下:
import numpy as np
c = np.random.rand(4)*10
print c
#Output1: [ 0.12757225 5.48992242 7.63139022 2.92746857]
c.dtype = int
print c
#Output2: [4593764294844833304 4617867121563982285 4620278199966380988 4613774491979221856]
我知道正确的改变方法是:
c = c.astype(int)
但我想了解 Output2 中这些模棱两可的数字背后的原因。它们是什么,它们意味着什么?
浮点数和整数(numpy.float64
s 和numpy.int64
s(在内存中的表示方式不同。存储在这些不同类型中的值 42 对应于内存中的不同位模式。
当您重新分配数组的dtype
属性时,您可以保持基础数据不变,并且您告诉 numpy 以新的方式解释该位模式。由于现在的解释与数据的原始定义不匹配,因此最终会得到胡言乱语(无意义的数字(。
另一方面,通过.astype()
转换数组实际上会转换内存中的数据:
>>> import numpy as np
>>> arr = np.random.rand(3)
>>> arr.dtype
dtype('float64')
>>> arr
array([ 0.7258989 , 0.56473195, 0.20885672])
>>> arr.data
<memory at 0x7f10d7061288>
>>> arr.dtype = np.int64
>>> arr.data
<memory at 0x7f10d7061348>
>>> arr
array([4604713535589390862, 4603261872765946451, 4596692876638008676])
正确转换:
>>> arr = np.random.rand(3)*10
>>> arr
array([ 3.59591191, 1.21786042, 6.42272461])
>>> arr.astype(np.int64)
array([3, 1, 6])
如您所见,使用astype
将有意义地转换数组的原始值,在这种情况下,它将截断为整数部分,并返回一个具有相应值和dtype
的新数组。
请注意,分配新dtype
不会触发任何检查,因此您可以对数组执行非常奇怪的操作。在上面的示例中,64 位浮点数被重新解释为 64 位整数。但您也可以更改位大小:
>>> arr = np.random.rand(3)
>>> arr.shape
(3,)
>>> arr.dtype
dtype('float64')
>>> arr.dtype = np.float32
>>> arr.shape
(6,)
>>> arr
array([ 4.00690371e+35, 1.87285304e+00, 8.62005305e+13,
1.33751166e+00, 7.17894062e+30, 1.81315207e+00], dtype=float32)
通过告诉 numpy 您的数据占用的空间是原来的一半,numpy 将推断出您的数组具有两倍的元素!显然不是你应该想做的。
另一个例子:考虑 8 位无符号整数 255==2**8-1:它对应于二进制中的11111111。现在,尝试将其中两个数字重新解释为单个 16 位无符号整数:
>>> arr = np.array([255,255],dtype=np.uint8)
>>> arr.dtype = np.uint16
>>> arr
array([65535], dtype=uint16)
如您所见,结果是单个数字 65535。如果这没有敲响警钟,它正好是 2**16-1,二进制模式中有 16 个。两个全一模式被重新解释为单个 16 位数字,结果也相应更改。您经常看到更奇怪的数字的原因是,由于浮点数在内存中的表示方式,将浮点数重新解释为整数将导致更强烈的数据混乱。
正如 hpaulj 所指出的,您可以通过使用修改后的数组构建数组的新view
来直接执行dtype
对数据的重新解释。这可能比必须重新分配给定数组的dtype
更有用,但同样更改dtype
仅在相当罕见、非常具体的用例中有用。