numpy 使用数组“m”中的值修改数组“a”的值以创建数组“c”

qf9go6mv  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(86)

考虑数组a,在这个例子中,它有5行6列。
每一行都包含0到5的值。a的30个元素使用以下规则组成15对:如果行的列p保存值q,则该行的列q保存值p。为了避免配对的重复,我们只考虑p > q的配对。

a = np.array([[1, 0, 3, 2, 5, 4],
              [2, 4, 0, 5, 1, 3],
              [3, 5, 4, 0, 2, 1],
              [4, 3, 5, 1, 0, 2],
              [5, 2, 1, 4, 3, 0]])

以下是15个配对中的一些,由列索引命名:
在行0中,配对(0,1)保存值(1,0),配对(2,3)保存值(3,2),配对(4,5)保存值(5,4)
等等
在行4中,配对(0,5)保存值(5,0),配对(1,2)保存值(2,1),配对(3,4)保存值(4,3)
现在,考虑数组m

m = np.array([[0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4],
              [1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5], 

              [6, 4,-2, 5, 0,-1, 1, 4,-7,-8,-7, 3,-4, 1, 0],
              [3,-8, 1, 2, 1, 3, 1, 6, 5, 5, 0, 2, 3, 1, 0]])

m的前两行显示了15对(0,1), (0,2), ... (0,5), (1,2), ... (3,4), (3,5), (4,5)
m的最后两行显示的值,对于每个配对,必须添加到a的相应元素中。
因此,对于a配对(0, 1),我们将(6, 3)添加到现有值(1, 0)以获得值(7, 3)
对于a(0, 2),我们将(4,-8)添加到现有值(2, 0)以获得值(6,-8)
最后的结果看起来像这样:

c = np.array([[7, 3,-5, 7, 5, 4],
              [6, 8,-8, 6, 7, 4],
              [1,-2,-3, 1, 2, 6],
              [9, 4, 8, 2, 2, 4],
              [5, 1, 4, 0, 6, 1]])

问题:如何使用m中的值修改a中的值以生成c

fruv7luv

fruv7luv1#

我很确定有一个更简单的方法,因为我的解决方案是O(nmp),(n×m是矩阵a的大小,p是矩阵M中的列数。
当它应该有可能有一个O(np)的解决方案时(对于矩阵m中的每一列,我们访问矩阵a的匹配列,并搜索行)。
但至少它是“矢量化的”;它不使用python for循环

col=np.arange(a.shape[1])
mask=(m[0][:,None,None]==col[None,None,:]) & (m[1][:,None,None]==a)

创建三维布尔掩码。轴0表示m的每一列。轴1和2是a的行和列。
如果m[1,k]等于a[i,j]j(又名col[k,i,j])等于m[0,k],则该掩码中的位置k,i,j为True。
因此,掩码中位置k,i,j处的True意味着在a的位置ij处,存在等于m[0,k]的值
我们需要将m[2,k]添加到a中的[i,j]位置。并在同一rowi,但列m[1,k]添加m[3,k]
所以剩下的

k,i,j=np.where(mask) 
a[i,j] += m[2]
a[i, m[1,k]] += m[3]

当然,如果您希望保持原始a的完整性,则需要在此之前创建a的副本
所以匹配函数

def withoutFor(a,m):
    col=np.arange(a.shape[1])
    k,i,j=np.where((m[0][:,None,None]==col[None,None,:]) & (m[1][:,None,None]==a))
    c=a.copy()
    c[i,j] += m[2]
    c[i, m[1,k]] += m[3]
    return c

非矢量化版本

为了强调我最初的观点(从复杂性的Angular 来看,它不是最佳的),下面是如何使用for循环来完成的

def withFor(a,m):
    c=a.copy()
    for v in m.T:
        row=np.where(a[:,v[0]]==v[1])[0][0]
        c[row,v[0]] += v[2]
        c[row,v[1]] += v[3]
    return c

复杂性低一个数量级。不过,它是3倍慢与您的例子。它可能会随着更大的例子而改变

编辑:双向

实际上,我只需要编写for loop版本就可以实现这个版本也可以“矢量化”。所以我们可以鱼与熊掌兼得。也就是说,有一个O(np)版本,不使用for循环

def bothWay(a,m):
    row=np.where((a[:, m[0,:]]==m[1,:]).T)[1] 
    c=a.copy()
    c[row, m[0]] += m[2]
    c[row, m[1]] += m[3]
    return c

即使在你的小例子中,它也比其他两个方法快。在更长的时间里应该会赢得更多。
a[:, m[0,:]]是一个5×15矩阵,其列是m的第一行中给定的列(即一个5×15矩阵,其元素i,j是矩阵a的元素i,m[0,j]
所以(a[:, m[0,:]]==m[1,:])是一个5×15的布尔矩阵,其在i,j位置的元素为真当且仅当矩阵a的元素i,m[0,j]等于m的第1行第j列的值。换句话说,我不知道。换句话说,在每个j=0到14列中,你在行位置有True,在列m[0,j]中你可以找到m[1,j]中的值。
例如,这个布尔矩阵的第7列在第1行(第二行)包含True,因为在矩阵a的第1列(1是我们在m的第7列第1行找到的)中,你在第1行找到了4(m的第7列第2行)。
因此,该矩阵具有m那么多列。每列包含一个,而且只有一个True。匹配行,其中,在矩阵a中,我可以在m的第1行中指示的列中找到m的第2行中的值。
通过转置这个布尔矩阵,我得到一个15×5的布尔矩阵。由于np.where的工作顺序,这确保了我将获得15的位置。
因此np.where((a[:, m[0,:]]==m[1,:]).T)[1]是我需要知道的15行。(...[0]只是数字0到14)对于m的每一列,这给予我a的行号,我可以在m的第一行指示的列中找到m的第二行中的值。
所以,a[row]是15,我需要改变的15行,对于m的每15列。a[row, m[0]]是15个值,在那些行、列m[0]的a中找到。我需要把在m[2]中找到的15个值加到这15个值上。同样,a[row, m[1]]是匹配列中相同行的15个值。我需要将在m的第4行中找到的15个值添加到其中。

相关问题