矢量操作与numpy

时间:2015-08-29 05:42:25

标签: python numpy matrix vectorization

我有三个numpy数组:

X:3073 x 49000矩阵
W:10 x 3073矩阵 y:49000 x 1向量

y包含0到9之间的值,每个值代表W中的一行。

我想将X的第一列添加到W中第一个元素给出的y行中。即如果y中的第一个元素为3,则将X的第一列添加到W的第四行。然后将X的第二列添加到W中由y中的第二个元素指定的行中,依此类推,直到X的所有列都已添加到Wy指定的W[y] += X.T行,表示总共添加了49000行。

W对我不起作用,因为这不会在In [1]: import numpy as np In [2]: a, b, c = 3, 4, 5 In [3]: np.random.seed(0) In [4]: X = np.random.randint(10, size=(b,c)) In [5]: W = np.random.randint(10, size=(a,b)) In [6]: y = np.random.randint(a, size=(c,1)) In [7]: X Out[7]: array([[5, 0, 3, 3, 7], [9, 3, 5, 2, 4], [7, 6, 8, 8, 1], [6, 7, 7, 8, 1]]) In [8]: W Out[8]: array([[5, 9, 8, 9], [4, 3, 0, 3], [5, 0, 2, 3]]) In [9]: y Out[9]: array([[0], [1], [1], [2], [0]]) In [10]: W[y.ravel()] + X.T Out[10]: array([[10, 18, 15, 15], [ 4, 6, 6, 10], [ 7, 8, 8, 10], [ 8, 2, 10, 11], [12, 13, 9, 10]]) In [11]: W[y.ravel()] = W[y.ravel()] + X.T In [12]: W Out[12]: array([[12, 13, 9, 10], [ 7, 8, 8, 10], [ 8, 2, 10, 11]]) 的行中添加多个向量。

请注意:我只是在寻找 矢量化 解决方案。即没有for-loops。

编辑:为了澄清我将添加一个小矩阵大小的示例,该示例改编自Salvador Dali的示例。

W = [[17, 22, 16, 16],
     [ 7, 11, 14, 17],
     [ 8,  2, 10, 11]]

问题是将 BOTH 列0和X中的第4列添加到W中的第0行,以及将X中的第1列和第2列添加到W中的第1行。

因此,期望的结果是:

my code 

HtmlElementCollection bColl = webBrowser1.Document.GetElementsByTagName("td");
        foreach (HtmlElement bEl in bColl)
        {
            if (bEl.GetAttribute("b").Contains("your"))
            txtLinkDetails.Text = bEl.OuterHtml.Split('"')[3].Replace("&amp", "&");
        }

4 个答案:

答案 0 :(得分:3)

矢量化方法

方法#1

基于this answer,这是使用np.bincount的矢量化解决方案 -

N = y.max()+1
id = y.ravel() + np.arange(X.shape[0])[:,None]*N
W[:N] += np.bincount(id.ravel(), weights=X.ravel()).reshape(-1,N).T

方法#2

您可以充分利用boolean indexingnp.einsum以简洁的矢量化方式完成工作 -

N = y.max()+1
W[:N] += np.einsum('ijk,lk->il',(np.arange(N)[:,None,None] == y.ravel()),X)

Loopy接近

方法#3

由于您从X每个唯一y选择并添加了大量列,因此在性能方面运行与complexity等于此类唯一y's的数量,似乎在max等于W中的行数,而在您的情况下仅为10。因此,循环只有10次迭代,不错!这是实现这些愿望的实施 -

for k in range(W.shape[0]):
    W[k] += X[:,(y==k).ravel()].sum(1)

方法#4

你可以引入np.einsum来进行列式求和,并得到最终输出 -

for k in range(W.shape[0]):
    W[k] += np.einsum('ij->i',X[:,(y==k).ravel()])

答案 1 :(得分:3)

首先将直接循环解决方案作为参考:

In [65]: for i,j in enumerate(y):
    W[j]+=X[:,i]
   ....:     

In [66]: W
Out[66]: 
array([[17, 22, 16, 16],
       [ 7, 11, 14, 17],
       [ 8,  2, 10, 11]])

add.at解决方案:

In [67]: W=W1.copy()
In [68]: np.add.at(W,(y.ravel()),X.T)
In [69]: W
Out[69]: 
array([[17, 22, 16, 16],
       [ 7, 11, 14, 17],
       [ 8,  2, 10, 11]])

add.at执行无缓冲计算,绕过阻止W[y.ravel()] += X.T工作的缓冲。它仍然是迭代的,但循环已经移动到编译代码。它不是真正的矢量化,因为应用程序的顺序很重要。一行X.T的添加取决于前一行的结果。

https://stackoverflow.com/a/20811014/901925是我几年前给出类似问题的答案(对于1d数组)。

但在处理大型数组时:

X: a 3073 x 49000 matrix
W: a 10 x 3073 matrix
y: a 49000 x 1 vector 

这可能会遇到速度问题。请注意,W[y.ravel()]X.T的大小相同(为什么选择这些需要转置的大小?)。它是一个副本,而不是一个视图。所以已经有时间惩罚了。

之前的问题已经提出了{p> bincount,我认为它更快。 Making for loop with index arrays faster(bincount和add.at解决方案)

迭代3073的小尺寸也可以获得速度优势。或者更好的是尺寸10维,Divakar演示。

对于小型测试用例a,b,c=3,4,5add.at解决方案最快,其次为Divakar's bincounteinseum。对于较大的a,b,c=10,1000,20000add.at变得非常慢,bincount是最快的。

相关SO答案

https://stackoverflow.com/a/28205888/901925(请注意bincount需要y完全覆盖。

https://stackoverflow.com/a/30041823/901925(其中Divakar再次显示bincount规则!)

答案 2 :(得分:1)

这将实现您的目标:X + W[y.ravel()].T

要看到这确实有效,这是一个可重复的例子:

import numpy as np
np.random.seed(0)
a, b, c = 3, 5, 4  # you can use your 3073, 49000, 10 later

X = np.random.rand(a, b)
W = np.random.rand(c, a)
y = np.random.randint(c, size=(b, 1))

现在你的矩阵是:

[[ 0.0871293   0.0202184   0.83261985]
 [ 0.77815675  0.87001215  0.97861834]
 [ 0.79915856  0.46147936  0.78052918]
 [ 0.11827443  0.63992102  0.14335329]]

[[3]
 [0]
 [3]
 [2]
 [0]]

[[ 0.5488135   0.71518937  0.60276338  0.54488318  0.4236548 ]
 [ 0.64589411  0.43758721  0.891773    0.96366276  0.38344152]
 [ 0.79172504  0.52889492  0.56804456  0.92559664  0.07103606]]

W[y.ravel()]给你"由y"中的第一个元素给出的W通过转置它,您将获得一个准备好添加到X的矩阵:

[[ 0.11827443  0.0871293   0.11827443  0.79915856  0.0871293 ]
 [ 0.63992102  0.0202184   0.63992102  0.46147936  0.0202184 ]
 [ 0.14335329  0.83261985  0.14335329  0.78052918  0.83261985]]

答案 3 :(得分:0)

虽然我不能说这是非常pythonic,但这是一个解决方案(我认为):

for column in range(x.shape[1]):
    w[y[column]] = x[:,column].T