【问题标题】:Composite Index updates for Numpy MatricesNumpy 矩阵的综合指数更新
【发布时间】:2023-03-13 19:03:01
【问题描述】:

我正在尝试更新 numpy 矩阵的一组特定行和列。这是一个例子:

import numpy as np
A=np.zeros((8,8))
rows=[0, 1, 5]
columns=[2, 3]
#(What I am trying to achieve) The following does not update A
A[rows][:,columns]+=1
#while this just does
for i in rows:
    A[i][columns]+=1

我期望的输出是:

In [1]:print(A)
Out[1]: 
    array([[ 0.,  0.,  1.,  1.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  1.,  1.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  1.,  1.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

有没有办法在不循环的情况下同时执行多列和-行更新?

【问题讨论】:

    标签: python numpy matrix


    【解决方案1】:

    rows 需要是“列”向量,例如

    rows=[[0],[1],[5]]
    cols=[2,3]
    A[rows,cols]+=1
    

    有时 2 阶段索引有效,A[rows][:,cols],但并非总是如此。特别是在这种情况下,rows 不是切片。 A[rows] 现在是一个副本,因此更改它不会更改 A

    有多种方法可以索引这个块。 plonser's 使用 product 有效,但我很少看到它与 numpy 一起使用。

    np.ix_ 是一个方便的工具:

    In [70]: np.ix_([0,1,5],[2,3])
    Out[70]: 
    (array([[0],
            [1],
            [5]]), array([[2, 3]]))
    

    np.newaxis 也将行向量转换为列:

    rows=np.array([0,1,5])
    cols=np.array([2,3])
    A[rows[:,None],cols]
    

    这种列向量和行向量配对有效,因为numpy 广播它们以生成(3,2) 索引数组。

    itertools.product 生成相同的索引集,但作为元组列表(或生成器)

    In [80]: list(itertools.product([0,1,5],[2,3]))
    Out[80]: [(0, 2), (0, 3), (1, 2), (1, 3), (5, 2), (5, 3)]
    In [84]: tuple(np.array(list(itertools.product([0,1,5],[2,3]))).T)
    Out[84]: (array([0, 0, 1, 1, 5, 5]), array([2, 3, 2, 3, 2, 3]))
    

    【讨论】:

      【解决方案2】:

      使用数组或索引列表进行索引属于fancy indexing 类别,并且始终会生成数组相关部分的副本,而不是让您查看原始数据。

      单个花哨的索引操作的结果的分配或就地修改工作正常,(例如A[rows] += 1)。但是,修改 chained 索引表达式的结果,例如A[rows][:, columns],如果第一个表达式使用花哨的索引,不会工作。原因是A[rows]生成一个副本,所以A[rows][:, columns]加1不会影响A

      如果您一次性完成所有索引,则可以避免此问题。在某些时候,您可能尝试过这样的事情:

      In [38]: A[rows, columns] += 1
      ---------------------------------------------------------------------------
      ValueError                                Traceback (most recent call last)
      <ipython-input-38-7d2300f59d51> in <module>()
      ----> 1 A[rows, columns] += 1
      
      ValueError: shape mismatch: objects cannot be broadcast to a single shape
      

      这里 numpy 将您的索引列表转换为数组,然后尝试将 broadcast 它们转换为相同的形状,但由于它们的尺寸不兼容而失败。要使其成功,您可以将rows 设置为(n, 1) 数组并将columns 设置为(1, m) 数组,其中mn 是单个行/列索引的数量。这样它们就可以沿着大小 1 的维度展开到大小(n, m),并用于索引到A

      r = np.array(rows)
      c = np.array(columns)
      A[r[:, None], c[None, :]] += 1
      print(A)
      # [[ 0.  0.  1.  1.  0.  0.  0.  0.]
      #  [ 0.  0.  1.  1.  0.  0.  0.  0.]
      #  [ 0.  0.  0.  0.  0.  0.  0.  0.]
      #  [ 0.  0.  0.  0.  0.  0.  0.  0.]
      #  [ 0.  0.  0.  0.  0.  0.  0.  0.]
      #  [ 0.  0.  1.  1.  0.  0.  0.  0.]
      #  [ 0.  0.  0.  0.  0.  0.  0.  0.]
      #  [ 0.  0.  0.  0.  0.  0.  0.  0.]]
      

      使用None 进行索引与使用np.newaxis 相同 - 它具有插入大小为 1 的新维度的效果。

      实际上有一个方便的函数np.ix_,用于从一维索引序列生成多维索引:

      A = np.zeros((8, 8))
      A[np.ix_(rows, columns)] += 1
      

      【讨论】:

      • 我同意你的结果肯定比我的更优雅......但是,我想指出一个 可以 使用高级索引更改 A 的元素请参阅here
      • @plonser 确实如此,但仅在分配给单个索引表达式的结果时。分配给链式索引的结果(例如A[x][y] = ...)仅在第一组索引是切片或整数(即非花式索引)时才有效。我将编辑我的答案以使区别更加清晰。
      • 此方法占用大量内存
      • @bluesky 你把它比作什么?
      【解决方案3】:

      您可以先使用itertool.product获取所有坐标

      import itertools
      coord = np.array(list(itertools.product(rows,columns)))
      

      然后使用高级索引

      A[tuple(coord.T)] += 1
      

      正如其他答案中提到的,高级索引通常会提供一份副本。但是,如果处理得当,它仍然可以像 here 争论的那样工作。

      【讨论】:

      • 我同意必须小心高级索引和副本......但是我的代码工作正常,这就是为什么我不明白我被否决了
      猜你喜欢
      • 2016-07-25
      • 2016-02-02
      • 2017-06-22
      • 1970-01-01
      • 1970-01-01
      • 2012-01-20
      • 2014-09-10
      • 2021-11-20
      • 2010-12-28
      相关资源
      最近更新 更多