首先,你正在学习的代码有缺陷。根据代码中的注释,它几乎肯定不会执行原始作者认为它可以执行的操作。
作者可能的意思是这样的:
def to_1d(array):
"""prepares an array into a 1d real vector"""
return array.astype(np.float64).ravel()
然而,如果array
始终是由复数数组组成,则原始代码有些意义。
仅当数组类型为复数(numpy.complex128
)或128位浮点数数组时,查看数组(a.dtype = 'float64'
等同于a = a.view('float64')
)可能会使其大小加倍。 对于任何其他dtype,这并不太合理。
对于复数数组的特定情况,原始代码将类似于np.array([0.5+1j, 9.0+1.33j])
的东西转换为np.array([0.5, 1.0, 9.0, 1.33])
。
更简洁的写法如下:
def complex_to_iterleaved_real(array):
"""prepares a complex array into an "interleaved" 1d real vector"""
return array.copy().view('float64').ravel()
(暂不考虑返回原始数据类型和形状的部分。)
numpy数组的背景
要解释这里发生的事情,您需要了解一些关于numpy数组的内容。
一个numpy数组由“原始”内存缓冲区组成,通过“视图”被解释为数组。你可以将所有的numpy数组都看作视图。
在numpy中,视图只是不同的方式来切片和处理同一块内存缓冲区,而无需复制。
视图具有形状、数据类型(dtype)、偏移量和步幅。在可能的情况下,对numpy数组进行索引/重塑操作将只返回原始内存缓冲区的视图。
这意味着像y = x.T
或y = x[::2]
这样的操作不会使用任何额外的内存,并且不会复制x
。
因此,如果我们有一个类似于这样的数组:
import numpy as np
x = np.array([1,2,3,4,5,6,7,8,9,10])
我们可以通过以下两种方式来重塑它:
x = x.reshape((2, 5))
或者
x.shape = (2, 5)
为了可读性,第一个选项更好。它们(几乎)完全等效。但是两者都不会产生使用更多内存的副本(第一个将导致一个新的Python对象,但就目前而言,这是无关紧要的)。
数据类型和视图
对于数据类型(dtype)也是同样的道理。我们可以通过设置x.dtype
或调用x.view(...)
来将一个数组视为不同的数据类型。
因此,我们可以像这样做:
import numpy as np
x = np.array([1,2,3], dtype=np.int)
print 'The original array'
print x
print '\n...Viewed as unsigned 8-bit integers (notice the length change!)'
y = x.view(np.uint8)
print y
print '\n...Doing the same thing by setting the dtype'
x.dtype = np.uint8
print x
print '\n...And we can set the dtype again and go back to the original.'
x.dtype = np.int
print x
产生以下结果:
The original array
[1 2 3]
...Viewed as unsigned 8-bit integers (notice the length change!)
[1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0]
...Doing the same thing by setting the dtype
[1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0]
...And we can set the dtype again and go back to the original.
[1 2 3]
请记住,这会让你对内存缓冲区的解释方式进行低级别控制。
例如:
import numpy as np
x = np.arange(10, dtype=np.int)
print 'An integer array:', x
print 'But if we view it as a float:', x.view(np.float)
print "...It's probably not what we expected..."
这将得到:
An integer array: [0 1 2 3 4 5 6 7 8 9]
But if we view it as a float: [ 0.00000000e+000 4.94065646e-324
9.88131292e-324 1.48219694e-323 1.97626258e-323
2.47032823e-323 2.96439388e-323 3.45845952e-323
3.95252517e-323 4.44659081e-323]
...It's probably not what we expected...
因此,在这种情况下,我们将原始内存缓冲区的底层位解释为浮点数。
如果我们想要制作一个新副本,其中整数被重新分配为浮点数,我们将使用x.astype(np.float)。
复数
复数(在C、Python和NumPy中)是作为两个浮点数存储的。第一个是实部,第二个是虚部。
所以,如果我们执行:
import numpy as np
x = np.array([0.5+1j, 1.0+2j, 3.0+0j])
我们可以看到复数的实部(x.real
)和虚部(x.imag
)。如果我们将其转换为浮点数,会收到关于舍弃虚部的警告,并得到只有实部的数组。
print x.real
print x.astype(float)
astype
会复制并将数值转换为新类型。
但是,如果我们将此数组视为浮点数,则会得到一个序列item1.real, item1.imag, item2.real, item2.imag, ...
。
print x
print x.view(float)
产生:
[ 0.5+1.j 1.0+2.j 3.0+0.j]
[ 0.5 1. 1. 2. 3. 0. ]
每个复数实际上是两个浮点数,因此如果我们改变NumPy解释底层内存缓冲的方式,就可以得到长度为两倍的数组。
希望这有助于澄清问题...