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)
Run Code Online (Sandbox Code Playgroud)
但现在我希望数据是uint32
. 我可以
import numpy as np
N = 1000 # (large) number of elements
a = np.random.randint(0, 100, N, dtype=np.int32)
Run Code Online (Sandbox Code Playgroud)
甚至
b = a.astype(np.uint32)
Run Code Online (Sandbox Code Playgroud)
但在这两种情况下b
都是 的副本a
,而我想简单地将 中的数据重新解释a
为uint32
,以免复制内存。同样,使用np.asarray()
也没有帮助。
起作用的是
b = a.astype(np.uint32, copy=False)
Run Code Online (Sandbox Code Playgroud)
它只是改变了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
Run Code Online (Sandbox Code Playgroud)
dtype
我的问题是关于简单地覆盖数组的解决方案:
a
并b
共享相同的数据,但将其视为不同的,dtype
该怎么办?我发现以下方法可行,但我再次担心这是否真的可以做到:
a.dtpye = np.uint32
Run Code Online (Sandbox Code Playgroud)
虽然这似乎有效,但我发现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
Run Code Online (Sandbox Code Playgroud)
实际上,上面每次运行似乎都会给出不同的结果,所以我根本不明白那里发生了什么。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]
Run Code Online (Sandbox Code Playgroud)
再说一次,如果所获得的行为确实是我所追求的,这是否可以被宽恕?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- 如果我想要两个数组
\na
并b
共享相同的数据,但将其视为不同的,该怎么办dtypes
?[...]
np.view
可以在这种情况下使用,就像您在示例中所做的那样。然而,结果是平台相关的。事实上,Numpy 只是重新解释内存字节,理论上负数的表示可以从一台机器改变到另一台机器。希望现在所有主流现代处理器都使用二进制补码(来源)。这意味着np.in32
像这样的值-1
将被重新解释为2**32-1 = 4294967295
类型的视图np.uint32
。正符号值不变。只要您意识到这一点,就可以了,并且行为是可以预测的。
\n\n\n
\n- 这可以扩展到其他
\ndtypes
,其中最极端的可能是混合 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.uint8
a 的视图是安全的np.float32
,因为它不会违反严格的别名规则(并且对齐是好的)。这对于有效序列化 Numpy 数组很有用。相反的操作并不安全(特别是由于对齐)。
关于上一节的更新:对Numpy 代码的更深入分析表明,代码的某些部分(例如类型转换函数)使用 C 调用执行安全类型双关memmove
,而其他一些函数(例如所有基本一元运算符或二元运算符)似乎并未执行此操作。做一个适当的类型双关语吧!此外,此类功能几乎没有经过用户测试,并且棘手的极端情况可能会导致奇怪的错误(特别是如果您在同一数组的两个视图中进行读写)。因此,使用它需要您自担风险。
归档时间: |
|
查看次数: |
1001 次 |
最近记录: |