手动设置scipy稀疏矩阵形状的含义

时间:2018-05-21 23:24:44

标签: python numpy scipy sparse-matrix reshape

我需要在TF-IDF模型上进行在线培训。我发现了scipy的  TfidfVectorizer不支持在线时尚培训,因此我正在实施自己的CountVectorizer以支持在线培训,然后使用scipy的TfidfTransformer在预定义后更新tf-idf值已在语料库中输入的文件数量。

我发现here你不应该为numpy数组添加行或列,因为所有数据都需要复制,因此它存储在连续的内存块中。

但后来我发现,实际上,使用scipy稀疏矩阵可以manually change the matrix's shape

Numpy reshape docs说:

  

在不复制数据的情况下,无法始终更改阵列的形状。如果要在复制数据时引发错误,则应将新形状分配给数组的shape属性

由于通过分配新形状来完成稀疏矩阵的“重塑”,是否可以安全地说数据没有被复制?这样做有什么意义?它有效吗?

代码示例:

matrix = sparse.random(5, 5, .2, 'csr') # Create (5,5) sparse matrix
matrix._shape = (6, 6) # Change shape to (6, 6)
# Modify data on new empty row

我还想扩展我的问题,询问vstack允许一个append arrays to one another的方法(与添加一行相同)。是vstack复制整个数据,以便将其存储为我的第一个链接中所述的连续内存块?那么hstack呢?

修改 因此,在this question之后,我实现了一种方法来改变稀疏矩阵中行的值。

现在,将添加新空行的想法与修改现有值的想法混合起来我提出了以下内容:

matrix = sparse.random(5, 3, .2, 'csr')
matrix._shape = (6, 3)
# Update indptr to let it know we added a row with nothing in it.
matrix.indptr = np.hstack((matrix.indptr, matrix.indptr[-1]))

# New elements on data, indices format
new_elements = [1, 1]
elements_indices = [0, 2] 

# Set elements for new empty row
set_row_csr_unbounded(matrix, 5, new_elements, elements_indices)

我在同一次执行期间运行了上面的代码几次并且没有错误。但是一旦我尝试添加一个新列(然后就不需要更改indptr),当我尝试更改值时会出现错误。任何导致这种情况发生的原因?

好吧,由于set_row_csr_unbounded下面使用numpy.r_,我认为我最好使用lil_matrix。即使所有元素一旦添加也无法修改。我是对的吗?

我认为lil_matrix会因为我假设numpy.r_正在复制数据而引起注意。

1 个答案:

答案 0 :(得分:3)

numpy reshape中,意味着以保持相同数字元素的方式更改shape。所以形状术语的乘积不能改变。

最简单的例子就像

np.arange(12).reshape(3,4)

分配方法是:

x = np.arange(12)
x.shape = (3,4)

method(或np.reshape(...))返回一个新数组。 shape作业就地发挥作用。

文档指出,在执行类似

之类的操作时,您的引用会起作用
x = np.arange(12).reshape(3,4).T
x.reshape(3,4)   # ok, but copy
x.shape = (3,4)  # raises error

为了更好地了解这里发生的事情,请在不同阶段打印数组,并查看原始0,1,2,...连续性如何变化。 (这是留给读者的练习,因为它不是更大问题的核心。)

有一个resize函数和方法,但它使用不多,而且它对视图和副本的行为很棘手。

np.concatenate(以及np.stacknp.vstack等变体)创建新数组,并复制输入中的所有数据。

列表(和对象dtype数组)包含指向元素的指针(可能是数组),因此不需要复制数据。

稀疏矩阵将其数据(和行/列索引)存储在格式不同的各种属性中。 coocsrcsc有3个1d数组。 lil有2个包含列表的对象数组。 dok是一个字典子类。

lil_matrix实现了reshape方法。其他格式没有。与np.reshape一样,尺寸的乘积不能改变。

理论上,稀疏矩阵可以“嵌入”在较大的矩阵中,只需最少的数据复制,因为所有新值都将是默认值0,而不占用任何空间。但是,该操作的细节尚未针对任何格式制定。

sparse.hstacksparse.vstack(不要在稀疏矩阵上使用numpy版本)通过组合输入的coo属性来工作(通过sparse.bmat )。是的,他们制作了新的数组(datarowcol)。

制作更大的稀疏矩阵的最小示例:

In [110]: M = sparse.random(5,5,.2,'coo')
In [111]: M
Out[111]: 
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 5 stored elements in COOrdinate format>
In [112]: M.A
Out[112]: 
array([[0.        , 0.80957797, 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.23618044, 0.        , 0.91625967, 0.8791744 ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.7928235 , 0.        ]])
In [113]: M1 = sparse.coo_matrix((M.data, (M.row, M.col)),shape=(7,5))
In [114]: M1
Out[114]: 
<7x5 sparse matrix of type '<class 'numpy.float64'>'
    with 5 stored elements in COOrdinate format>
In [115]: M1.A
Out[115]: 
array([[0.        , 0.80957797, 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.23618044, 0.        , 0.91625967, 0.8791744 ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.7928235 , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ]])
In [116]: id(M1.data)
Out[116]: 139883362735488
In [117]: id(M.data)
Out[117]: 139883362735488

MM1具有相同的data属性(相同的数组ID)。但是,对这些矩阵的大多数操作都需要转换为另一种格式(例如csr用于数学,或lil用于更改值),并且将涉及复制和修改属性。因此,两个矩阵之间的这种联系将被打破。

使用coo_matrix之类的函数创建稀疏矩阵,并且不提供shape参数时,它会从提供的坐标中推断出形状。如果您提供shape它会使用它。该形状必须至少与隐含形状一样大。使用lil(和dok),您可以有利地创建具有大形状的“空”矩阵,然后迭代地设置值。您不希望使用csr执行此操作。而且您无法直接设置coo值。

创建稀疏矩阵的规范方法是从各个部分迭代地构建datarowcol数组或列表 - 使用list append / extend或array concatenates,以及从中制作coo(或csr)格式数组。因此,即使创建矩阵,您也可以进行所有“增长”。

更改_sh​​ape

制作一个矩阵:

In [140]: M = (sparse.random(5,3,.4,'csr')*10).astype(int)
In [141]: M
Out[141]: 
<5x3 sparse matrix of type '<class 'numpy.int64'>'
    with 6 stored elements in Compressed Sparse Row format>
In [142]: M.A
Out[142]: 
array([[0, 6, 7],
       [0, 0, 6],
       [1, 0, 5],
       [0, 0, 0],
       [0, 6, 0]])

In [144]: M[1,0] = 10
... SparseEfficiencyWarning)
In [145]: M.A
Out[145]: 
array([[ 0,  6,  7],
       [10,  0,  6],
       [ 1,  0,  5],
       [ 0,  0,  0],
       [ 0,  6,  0]])

您的新形状方法(确保dtype的{​​{1}}不会改变):

indptr

添加列似乎也有效:

In [146]: M._shape = (6,3)
In [147]: newptr = np.hstack((M.indptr,M.indptr[-1]))
In [148]: newptr
Out[148]: array([0, 2, 4, 6, 6, 7, 7], dtype=int32)
In [149]: M.indptr = newptr
In [150]: M
Out[150]: 
<6x3 sparse matrix of type '<class 'numpy.int64'>'
    with 7 stored elements in Compressed Sparse Row format>
In [151]: M.A
Out[151]: 
array([[ 0,  6,  7],
       [10,  0,  6],
       [ 1,  0,  5],
       [ 0,  0,  0],
       [ 0,  6,  0],
       [ 0,  0,  0]])
In [152]: M[5,2]=10
... SparseEfficiencyWarning)
In [153]: M.A
Out[153]: 
array([[ 0,  6,  7],
       [10,  0,  6],
       [ 1,  0,  5],
       [ 0,  0,  0],
       [ 0,  6,  0],
       [ 0,  0, 10]])

属性共享

我可以从现有的矩阵中创建一个新矩阵:

In [154]: M._shape = (6,4)
In [155]: M
Out[155]: 
<6x4 sparse matrix of type '<class 'numpy.int64'>'
    with 8 stored elements in Compressed Sparse Row format>
In [156]: M.A
Out[156]: 
array([[ 0,  6,  7,  0],
       [10,  0,  6,  0],
       [ 1,  0,  5,  0],
       [ 0,  0,  0,  0],
       [ 0,  6,  0,  0],
       [ 0,  0, 10,  0]])
In [157]: M[5,0]=10
.... SparseEfficiencyWarning)
In [158]: M[5,3]=10
.... SparseEfficiencyWarning)
In [159]: M
Out[159]: 
<6x4 sparse matrix of type '<class 'numpy.int64'>'
    with 10 stored elements in Compressed Sparse Row format>
In [160]: M.A
Out[160]: 
array([[ 0,  6,  7,  0],
       [10,  0,  6,  0],
       [ 1,  0,  5,  0],
       [ 0,  0,  0,  0],
       [ 0,  6,  0,  0],
       [10,  0, 10, 10]])

In [108]: M = (sparse.random(5,3,.4,'csr')*10).astype(int) In [109]: newptr = np.hstack((M.indptr,6)) In [110]: M1 = sparse.csr_matrix((M.data, M.indices, newptr), shape=(6,3)) 属性是共享的,至少在视觉上是这样的:

data

但如果我通过添加非零值来修改In [113]: M[0,1]=14 In [114]: M1[0,1] Out[114]: 14

M1

矩阵之间的联系打破了:

In [117]: M1[5,0]=10
...
  SparseEfficiencyWarning)