避免在numpy操作中隐式转换为矩阵

时间:2016-02-22 06:02:48

标签: python numpy multidimensional-array scipy

有没有办法全局避免在{numpy计算的任何结果中出现matrix?例如,如果您将x作为numpy.ndarrayy作为scipy.sparse.csc_matrix,并且您说x += y,则x将成为{ {1}}之后。有没有办法防止这种情况发生,即保持matrix x,更一般地说,在生成ndarray的所有地方继续使用ndarray?< / p>

2 个答案:

答案 0 :(得分:2)

我添加了scipy代码,这是scipy.sparse个问题,而不是np.matrix个问题。

In [250]: y=sparse.csr_matrix([[0,1],[1,0]])
In [251]: x=np.arange(2)
In [252]: y+x
Out[252]: 
matrix([[0, 2],
        [1, 1]])

稀疏+数组=&gt;基质

(作为旁注,np.matrixnp.ndarray的子类。sparse.csr_matrix不是子类。它有许多类似numpy的操作,但它在自己的代码中实现它们)

In [255]: x += y
In [256]: x
Out[256]: 
matrix([[0, 2],
        [1, 1]])
从技术上讲,这不应该发生;实际上它正在x = x+yx分配新值,而不仅仅是修改x

如果我先将y变为常规密集matrix,我会收到错误消息。允许该操作会将1d数组更改为2d数组。

In [258]: x += y.todense()
...
ValueError: non-broadcastable output operand with shape (2,) doesn't match the broadcast shape (2,2)

x更改为2d允许继续添加 - 而不将数组更改为矩阵:

In [259]: x=np.eye(2)
In [260]: x
Out[260]: 
array([[ 1.,  0.],
       [ 0.,  1.]])
In [261]: x += y.todense()
In [262]: x
Out[262]: 
array([[ 1.,  1.],
       [ 1.,  1.]])

通常,使用稀疏矩阵执行加法/减法是棘手的。它们是为矩阵乘法而设计的。乘法不会像添加一样改变稀疏性。例如y+1使其密集。

如果不深入研究稀疏添加的编码细节,我会说 - 在没有先将x+=...转换为密集版本的情况下,不要尝试此y操作。

In [265]: x += y.A
In [266]: x
Out[266]: 
array([[ 1.,  2.],
       [ 2.,  1.]])

我无法想到不这样做的充分理由。

(我应该检查scipy github是否存在错误问题。)

scipy / sparse / compressed.py有csr个附加代码。 x+y使用x.__add__(y),但有时会将其翻转为y.__add__(x)x+=y使用x.__iadd__(y)。因此,我可能还需要检查__iadd__ ndarray

但稀疏矩阵的基本添加是:

def __add__(self,other):
    # First check if argument is a scalar
    if isscalarlike(other):
        if other == 0:
            return self.copy()
        else:  # Now we would add this scalar to every element.
            raise NotImplementedError('adding a nonzero scalar to a '
                                      'sparse matrix is not supported')
    elif isspmatrix(other):
        if (other.shape != self.shape):
            raise ValueError("inconsistent shapes")

        return self._binopt(other,'_plus_')
    elif isdense(other):
        # Convert this matrix to a dense matrix and add them
        return self.todense() + other
    else:
        return NotImplemented

因此y+x变为y.todense() + xx+y使用同样的东西。

无论+=详细信息如何,很明显将稀疏添加到密集(数组或np.matrix)涉及将稀疏转换为密集。没有代码可以遍历稀疏值并将这些值有选择地添加到密集数组中。

仅当数组稀疏时才执行特殊的稀疏加法。 y+y有效,返回稀疏。来自y+=y的{​​{1}} NotImplmenentedError失败。

这是我提出的最佳诊断序列,尝试了将sparse.base.__iadd__添加到y数组的各种方法。

(2,2)

添加会产生一个矩阵,但值可以写入In [348]: x=np.eye(2) In [349]: x+y Out[349]: matrix([[ 1., 1.], [ 1., 1.]]) In [350]: x+y.todense() Out[350]: matrix([[ 1., 1.], [ 1., 1.]]) 而不会更改x类(或形状)

x
具有密集矩阵的

In [351]: x[:] = x+y In [352]: x Out[352]: array([[ 1., 1.], [ 1., 1.]]) 也是如此:

+=

In [353]: x += y.todense() In [354]: x Out[354]: array([[ 1., 2.], [ 2., 1.]]) 中的内容更改了+=sparse

的类
x

进一步测试并查看In [355]: x += y In [356]: x Out[356]: matrix([[ 1., 3.], [ 3., 1.]]) id(x)很明显x.__array_interface__取代x += y。即使xx开头,也是如此。所以稀疏np.matrix不是一个就地操作。 +=是一个现场操作。

答案 1 :(得分:0)

是的,这是一个错误;但https://github.com/scipy/scipy/issues/7826

  

我真的没有办法改变这一点。

<小时/> 接下来是X += c * Y没有todense 一些inc( various array / matrix, various sparse ) 已经过测试,但肯定不是全部。

def inc( X, Y, c=1. ):
    """ X += c * Y, X Y sparse or dense """
    if (not hasattr( X, "indices" )  # dense += sparse
    and hasattr( Y, "indices" )):
        # inc an ndarray view, because ndarry += sparse -> matrix --
        X = getattr( X, "A", X ).squeeze()
        X[Y.indices] += c * Y.data
    else:
        X += c * Y  # sparse + different sparse: SparseEfficiencyWarning
    return X