numpy数组的dtype如何内部计算?

lU5*_*5er 7 python arrays numpy

当我意识到dtypes参数的鲜为人知的行为时,我只是在弄乱numpy数组。

似乎随着输入的变化而变化。例如,

t = np.array([2, 2])
t.dtype
Run Code Online (Sandbox Code Playgroud)

dtype('int32')

然而,

t = np.array([2, 22222222222])
t.dtype
Run Code Online (Sandbox Code Playgroud)

dtype('int64')

所以,我的第一个问题是:这是如何计算的?它是否使数据类型适合于最大元素作为所有元素的数据类型?如果是这样,您是否不认为它需要更多空间,因为它不必要地存储多余的内存来将2作为64位整数存储在第二个数组中?

再说一次,如果我想更改array([2, 2])like 的第零个元素

t = np.array([2, 2])
t[0] = 222222222222222
Run Code Online (Sandbox Code Playgroud)

我懂了OverflowError: Python int too large to convert to C long

我的第二个问题是:如果更改特定值,为什么它不支持创建数组时的逻辑?为什么不重新计算和重新评估?

任何帮助表示赞赏。提前致谢。

Pau*_*zer 5

让我们尝试在文档中找到相关的部分。

np.array文档字符串:

大批(...)

[...]

参数

[...]

dtype:数据类型,可选数组所需的数据类型。如果没有给出,那么类型将被确定为在序列中保存对象所需的最小类型。 此参数只能用于“向上转换”数组。对于向下转换,请使用 .astype(t) 方法。

[...]

(我的重点)

应该注意的是,这并不完全准确,例如对于整数数组,系统 (C) 默认整数优先于较小的整数类型,这从您的示例中可以明显看出。

请注意,要使 numpy 更快,数组的所有元素必须具有相同的大小。否则,你如何快速定位第 1000 个元素,比如说?此外,混合类型不会节省那么多空间,因为您必须将每个元素的类型存储在原始数据之上。

回复你的第二个问题。首先。numpy中有类型提升规则。我能找到的最好的文档是np.result_type文档字符串:

result_type(...) result_type(*arrays_and_dtypes)

返回将 NumPy 类型提升规则应用于参数所产生的类型。

NumPy 中的类型提升与 C++ 等语言中的规则类似,但略有不同。当同时使用标量和数组时,数组的类型优先并考虑标量的实际值。

例如,计算 3*a,其中 a 是一个 32 位浮点数组,直观上应该会产生一个 32 位浮点输出。如果 3 是 32 位整数,则 NumPy 规则表明它无法无损地转换为 32 位浮点数,因此结果类型应为 64 位浮点数。通过检查常量 '3' 的值,我们看到它适合一个 8 位整数,可以无损地转换为 32 位浮点数。

[...]

我没有在这里引用整个内容,有关更多详细信息,请参阅文档字符串。

这些规则的确切应用方式很复杂,似乎代表了直观和效率之间的折衷。

例如,选择基于输入,而不是结果

>>> A = np.full((2, 2), 30000, 'i2')
>>> 
>>> A
array([[30000, 30000],
       [30000, 30000]], dtype=int16)
# 1
>>> A + 30000
array([[-5536, -5536],
       [-5536, -5536]], dtype=int16)
# 2
>>> A + 60000
array([[90000, 90000],
       [90000, 90000]], dtype=int32)
Run Code Online (Sandbox Code Playgroud)

在这里,效率取胜。可以说让 #1 表现得像 #2 会更直观。但这会很昂贵。

此外,与您的问题更直接相关的是,类型提升仅适用于异地,而不是就地:

# out-of-place
>>> A_new = A + 60000
>>> A_new
array([[90000, 90000],
       [90000, 90000]], dtype=int32)
# in-place
>>> A += 60000
>>> A
array([[24464, 24464],
       [24464, 24464]], dtype=int16)
Run Code Online (Sandbox Code Playgroud)

或者

# out-of-place
>>> A_new = np.where([[0, 0], [0, 1]], 60000, A)
>>> A_new
array([[30000, 30000],
       [30000, 60000]], dtype=int32)
# in-place
>>> A[1, 1] = 60000
>>> A
array([[30000, 30000],
       [30000, -5536]], dtype=int16)
Run Code Online (Sandbox Code Playgroud)

同样,这可能看起来相当不直观。然而,做出这种选择是有令人信服的理由的。

这些应该回答你的第二个问题:

更改为更大的 dtype 需要分配更大的缓冲区并复制所有数据。对于大型阵列来说,这不仅会很昂贵。

numpy 中的许多习惯用法都依赖于视图以及写入视图直接修改基本数组(和其他重叠视图)的事实。因此,数组不能随时更改其数据缓冲区。为了不中断视图之间的链接,数组需要知道所有视图都进入其数据缓冲区,这会增加大量管理开销,并且所有这些视图也必须更改它们的数据指针和元数据。如果第一个数组本身就是另一个数组的视图(比如一个切片),事情会变得更糟。

我想我们可以一致认为不值得,这就是类型没有就地提升的原因。