jmd*_*_dk 8 python arrays types numpy python-3.x
假设我有一个很大的 NumPy 数组dtype int32
import numpy as np
N = 1000  # (large) number of elements
a = np.random.randint(0, 100, N, dtype=np.int32)
但现在我希望数据是uint32. 我可以
import numpy as np
N = 1000  # (large) number of elements
a = np.random.randint(0, 100, N, dtype=np.int32)
甚至
b = a.astype(np.uint32)
但在这两种情况下b都是 的副本a,而我想简单地将 中的数据重新解释a为uint32,以免复制内存。同样,使用np.asarray()也没有帮助。
起作用的是
b = a.astype(np.uint32, copy=False)
它只是改变了dtype而不改变数据。这是一个引人注目的例子:
import numpy as np
a = np.array([-1, 0, 1, 2], dtype=np.int32)
print(a)
a.dtype = np.uint32
print(a)  # shows "overflow", which is what I want
dtype我的问题是关于简单地覆盖数组的解决方案:
a并b共享相同的数据,但将其视为不同的,dtype该怎么办?我发现以下方法可行,但我再次担心这是否真的可以做到:
a.dtpye = np.uint32
data两个数组的底层似乎并不位于内存中的同一位置,这很奇怪:
import numpy as np
a = np.array([-1, 0, 1, 2], dtype=np.int32)
print(a)
a.dtype = np.uint32
print(a)  # shows "overflow", which is what I want
dtype,其中最极端的可能是混合 32 和 64 位浮点数:
import numpy as np
a = np.array([0, 1, 2, 3], dtype=np.int32)
b = a.view(np.uint32)
print(a)  # [0  1  2  3]
print(b)  # [0  1  2  3]
a[0] = -1
print(a)  # [-1  1  2  3]
print(b)  # [4294967295  1  2  3]
Jér*_*ard 10
\n\n\n
\n- 这是合法的吗?您能指出该功能的文档记录在哪里吗?\n
这是合法的。然而,使用np.view(等效)更好,因为它与静态分析器兼容(因此它在某种程度上更安全)。事实上,文档指出:
\n\n它\xe2\x80\x99s 可以
\ndtype在运行时改变数组的 。[...]\n类型不允许这种突变。想要编写静态类型代码的用户应该使用该numpy.ndarray.view方法来创建具有不同dtype.
\n\n\n
\n- 它实际上是否使数组的数据保持不变,即没有数据重复?\n
是的。由于数组仍然是同一内部内存缓冲区(基本字节数组)上的视图。Numpy 只会以不同的方式重新解释它(这是直接完成每个 Numpy 计算函数的 C 代码)。
\n\n\n\n
\n- 如果我想要两个数组\n
a并b共享相同的数据,但将其视为不同的,该怎么办dtypes?[...]
np.view可以在这种情况下使用,就像您在示例中所做的那样。然而,结果是平台相关的。事实上,Numpy 只是重新解释内存字节,理论上负数的表示可以从一台机器改变到另一台机器。希望现在所有主流现代处理器都使用二进制补码(来源)。这意味着np.in32像这样的值-1将被重新解释为2**32-1 = 4294967295类型的视图np.uint32。正符号值不变。只要您意识到这一点,就可以了,并且行为是可以预测的。
\n\n\n
\n- 这可以扩展到其他\n
dtypes,其中最极端的可能是混合 32 位和 64 位浮点数。
好吧,简单来说,这真的就像玩火一样。在这种情况下,这肯定不安全,尽管它可能在您的特定机器上工作。让我们浑水摸鱼吧。
\n首先,np.view状态文档:
\n\n仅从 的表面外观无法预测视图的行为
\na。它还取决于a内存中的存储方式。因此,如果a是 C 顺序与 Fortran 顺序、与定义为切片或转置等,视图可能会给出不同的结果。
问题是 Numpy 使用 C 代码重新解释指针。因此,据我所知,严格的别名规则适用。这意味着将np.float32值重新解释为会np.float64导致未定义的行为。np.float32原因之一是(通常为 4)和(通常为 8)的对齐要求不同,np.float32因此np.float64从内存中读取未对齐的值可能会导致某些架构(例如 POWER)崩溃,尽管 x86-64 处理器支持这一点。另一个原因来自编译器,由于严格的别名规则,编译器可能会在您的情况下做出错误的假设,从而过度优化代码(例如值np.float32和np.float64值不能在内存中重叠,因此视图的修改不应更改原始数组) 。然而,由于 Numpy 是从 CPython 调用的,并且没有从解释器内联函数调用(可能不是 Cython),所以最后一点应该不是问题(如果您使用 Numba 或任何 JIT,则可能是这种情况)。请注意,获取np.uint8a 的视图是安全的np.float32,因为它不会违反严格的别名规则(并且对齐是好的)。这对于有效序列化 Numpy 数组很有用。相反的操作并不安全(特别是由于对齐)。
关于上一节的更新:对Numpy 代码的更深入分析表明,代码的某些部分(例如类型转换函数)使用 C 调用执行安全类型双关memmove,而其他一些函数(例如所有基本一元运算符或二元运算符)似乎并未执行此操作。做一个适当的类型双关语吧!此外,此类功能几乎没有经过用户测试,并且棘手的极端情况可能会导致奇怪的错误(特别是如果您在同一数组的两个视图中进行读写)。因此,使用它需要您自担风险。