【问题标题】:Unexpected result with += on NumPy arrays在 NumPy 数组上使用 += 的意外结果
【发布时间】:2023-03-29 14:59:01
【问题描述】:

我正在使用标准方法在 Python 中使用 NumPy 创建对称矩阵/数组:

x = rand(500,500)
x = (x+x.T)
all(x==x.T)
> True

现在让我们聪明点:

x = rand(500,500)
x += x.T
all(x==x.T)
> False

等等,什么?

x==x.T
> array([[ True,  True,  True, ..., False, False, False],
       [ True,  True,  True, ..., False, False, False],
       [ True,  True,  True, ..., False, False, False],
       ..., 
       [False, False, False, ...,  True,  True,  True],
       [False, False, False, ...,  True,  True,  True],
       [False, False, False, ...,  True,  True,  True]], dtype=bool)

左上段和右下段是对称的。如果我选择一个更小的数组呢?

x = rand(50,50)
x += x.T
all(x==x.T)
> True

好的....

x = rand(90,90)
x += x.T
all(x==x.T)
> True

x = rand(91,91)
x += x.T
all(x==x.T)
> False

为了确定……

x = rand(91,91)
x = (x+x.T)
all(x==x.T)
> True

这是一个错误,还是我要学习一些关于 += 和 NumPy 数组的疯狂知识?

【问题讨论】:

  • @AndrewJaffe 这是 Python 3.4.1 上的 Numpy 1.9,分布在 Anaconda 中。
  • @jeffalstott 是的,我误读了这个问题——我也看到了这种行为。

标签: python arrays numpy


【解决方案1】:

transpose operation 返回数组的视图,这意味着没有分配新数组。反过来,这意味着您正在同时读取和修改数组。很难说为什么结果的某些大小或某些区域有效,但很可能与 numpy 如何处理数组加法(可能它会复制子矩阵)和/或数组视图(可能对于它确实创建的小尺寸一个新的数组)。

x = x + x.T 操作有效,因为您正在创建一个新数组,然后分配给x,当然。

【讨论】:

    【解决方案2】:

    别人提到的实现细节叫做buffering。您可以在the docs on array iteration 阅读更多相关信息。

    如果您更详细地查看失败的示例:

    >>> a = np.random.rand(91, 91)
    >>> a += a.T
    >>> a[:5, -1]
    array([ 0.83818399,  1.06489316,  1.23675312,  0.00379798,  1.08967428])
    >>> a[-1, :5]
    array([ 0.83818399,  1.06489316,  1.75091827,  0.00416305,  1.76315071])
    

    所以第一个错误值是90*91 + 2 = 8192,不出所料,这就是我们从中得到的:

    >>> np.getbufsize()
    8192
    

    而且我们也可以设置得更高,然后:

    >>> np.setbufsize(16384)  # Must be a multiple of 16
    8192  # returns the previous buffer size
    >>> a = np.random.rand(91, 91)
    >>> a += a.T
    >>> np.all(a == a.T)
    True
    

    虽然现在:

    >>> a = np.random.rand(129, 129)
    >>> a += a.T
    >>> np.all(a == a.T)
    False
    

    为了保证正确性,这当然是一件危险的事情,因为它确实是一个可能会更改的实现细节。

    【讨论】:

      【解决方案3】:

      问题是添加不是“一次”完成的; x.Tx视图,因此当您开始添加x 的每个元素时,x.T 会发生突变。这会打乱以后的添加。

      它适用于(91, 91) 以下的尺寸几乎绝对是一个实现细节。使用

      x = numpy.random.rand(1000, 1000)
      x += x.T.copy()
      numpy.all(x==x.T)
      #>>> True
      

      解决了这个问题,但是你真的没有任何空间优势。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-27
        • 1970-01-01
        • 1970-01-01
        • 2017-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多