按稀疏矩阵分组并返回矩阵

And*_*own 3 python nlp matrix scipy

关于SO处理groupby稀疏矩阵的使用存在一些问题。但是输出似乎是列表,字典数据框和其他对象。

我正在研究NLP问题,并希望在处理过程中将所有数据保留在稀疏的Scipy矩阵中,以防止出现内存错误。

这里是上下文:

我已对一些文档进行矢量化处理(此处为示例数据):

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer

df = pd.read_csv('groupbysparsematrix.csv')
docs = df['Text'].tolist()

vectorizer = CountVectorizer()
train_X = vectorizer.fit_transform(docs)

print("Dimensions of training set: {0}".format(train_X.shape))
print type(train_X)

Dimensions of training set: (8, 180)
<class 'scipy.sparse.csr.csr_matrix'>
Run Code Online (Sandbox Code Playgroud)

从原始数据框中,我使用日期(采用一年中的某天的格式)创建要汇总的组:

from scipy import sparse, hstack    

df['Date'] = pd.to_datetime(df['Date'])
groups = df['Date'].apply(lambda x: x.strftime('%j'))
groups_X = sparse.csr_matrix(groups.astype(float)).T
train_X_all = sparse.hstack((train_X, groups_X))

print("Dimensions of concatenated set: {0}".format(train_X_all.shape))

Dimensions of concatenated set: (8, 181)
Run Code Online (Sandbox Code Playgroud)

现在,我想应用groupby(或类似的功能)来查找每天每个令牌的总和。我希望输出是另一个稀疏的scipy矩阵。

输出矩阵将为3 x 181,如下所示:

 1, 1, 1, ..., 2, 1, 3
 2, 1, 3, ..., 1, 1, 4
 0, 0, 0, ..., 1, 2, 5
Run Code Online (Sandbox Code Playgroud)

其中列1到180代表令牌,列181代表一年中的一天。

hpa*_*ulj 5

计算csr稀疏矩阵的选定列(或行)之和的最佳方法是将一个矩阵乘积与另一个要在其求和的稀疏矩阵上加1。实际上,csr总和(针对整个行或一列)是通过矩阵乘积来工作的,而索引行(或列)也可以通过乘积来完成(/sf/answers/2765069051/

因此,我将对dates数组进行分组,并使用该信息来构造求和的“掩码”。

为了便于讨论,请考虑以下密集数组:

In [117]: A
Out[117]: 
array([[0, 2, 7, 5, 0, 7, 0, 8, 0, 7],
       [0, 0, 3, 0, 0, 1, 2, 6, 0, 0],
       [0, 0, 0, 0, 2, 0, 5, 0, 0, 0],
       [4, 0, 6, 0, 0, 5, 0, 0, 1, 4],
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
       [0, 7, 0, 8, 1, 0, 9, 0, 2, 4],
       [9, 0, 8, 4, 0, 0, 0, 0, 9, 7],
       [0, 0, 0, 1, 2, 0, 2, 0, 4, 7],
       [3, 0, 1, 0, 0, 0, 0, 0, 0, 2],
       [0, 0, 1, 8, 5, 0, 0, 0, 8, 0]])
Run Code Online (Sandbox Code Playgroud)

制作稀疏副本:

In [118]: M=sparse.csr_matrix(A)
Run Code Online (Sandbox Code Playgroud)

根据最后一列生成一些组;collections.defaultdict是执行此操作的便捷工具:

In [119]: grps=defaultdict(list)
In [120]: for i,v in enumerate(A[:,-1]):
     ...:     grps[v].append(i)

In [121]: grps
Out[121]: defaultdict(list, {0: [1, 2, 4, 9], 2: [8], 4: [3, 5], 7: [0, 6, 7]})
Run Code Online (Sandbox Code Playgroud)

我可以遍历这些组,收集的行M,对这些行求和并产生:

In [122]: {k:M[v,:].sum(axis=0) for k, v in grps.items()}
Out[122]: 
{0: matrix([[0, 0, 4, 8, 7, 2, 7, 6, 8, 0]], dtype=int32),
 2: matrix([[3, 0, 1, 0, 0, 0, 0, 0, 0, 2]], dtype=int32),
 4: matrix([[4, 7, 6, 8, 1, 5, 9, 0, 3, 8]], dtype=int32),
 7: matrix([[ 9,  2, 15, 10,  2,  7,  2,  8, 13, 21]], dtype=int32)}
Run Code Online (Sandbox Code Playgroud)

在最后一列中,值包括2 * 4和3 * 7

因此,有2个任务-收集组,无论是使用此defaultdict还是itertools.groupby(在这种情况下,都需要排序)或pandasgroupby。其次,这是行和求和的集合。这个字典迭代在概念上很简单。

掩蔽矩阵可能像这样工作:

In [141]: mask=np.zeros((10,10),int)
In [142]: for i,v in enumerate(A[:,-1]): # same sort of iteration
     ...:     mask[v,i]=1
     ...:     
In [143]: Mask=sparse.csr_matrix(mask)
...
In [145]: Mask.A
Out[145]: 
array([[0, 1, 1, 0, 1, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
       ....
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)
In [146]: (Mask*M).A
Out[146]: 
array([[ 0,  0,  4,  8,  7,  2,  7,  6,  8,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 3,  0,  1,  0,  0,  0,  0,  0,  0,  2],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 4,  7,  6,  8,  1,  5,  9,  0,  3,  8],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 9,  2, 15, 10,  2,  7,  2,  8, 13, 21],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0]], dtype=int32)
Run Code Online (Sandbox Code Playgroud)

Mask*M具有与字典行相同的值,但具有额外的0。我可以使用以下lil格式隔离非零值:

In [147]: (Mask*M).tolil().data
Out[147]: 
array([[4, 8, 7, 2, 7, 6, 8], [], [3, 1, 2], [],
       [4, 7, 6, 8, 1, 5, 9, 3, 8], [], [],
       [9, 2, 15, 10, 2, 7, 2, 8, 13, 21], [], []], dtype=object)
Run Code Online (Sandbox Code Playgroud)

我可以Mask使用coo稀疏的输入样式直接构造矩阵:

Mask = sparse.csr_matrix((np.ones(A.shape[0],int),
    (A[:,-1], np.arange(A.shape[0]))), shape=(A.shape))
Run Code Online (Sandbox Code Playgroud)

那应该更快并且避免内存错误(无循环或大型密集数组)。