2016-09-23 67 views
1

有关SO处理使用groupby与稀疏矩阵的几个问题。然而,输出似乎是列表,dictionaries,dataframes和其他对象。通过scipy中的稀疏矩阵进行分组并返回矩阵

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

这里的背景:

我已经矢量一些文件(sample data here):

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'> 

从原来的数据框我使用日期,在这一年格式的一天,创建组我想总结过:

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) 

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

输出矩阵是3×181,并期待这样的:

1, 1, 1, ..., 2, 1, 3 
2, 1, 3, ..., 1, 1, 4 
0, 0, 0, ..., 1, 2, 5 

凡列1至180代表令牌和列181代表一年中的一天。

+0

你在说'熊猫''groupby'吗?你能给一个密集阵列的工作例子吗?有一种熊猫稀疏格式,但它与稀疏矩阵的交互仍在开发中。 – hpaulj

+0

第181列 - 稀疏与否? – hpaulj

+0

第181列(groups_X)是'scipy.sparse.csc.csc_matrix',但实际上它是密集的,因为每个观察都有一个日期。 –

回答

1

计算一个csr稀疏矩阵的选定列(或行)总和的最佳方法是使用另一个稀疏矩阵的矩阵乘积,该矩阵在要求和的位置有1。事实上csr总和(一整行或列)的工作原理是矩阵的产品,和索引行(或列)也与产品完成(https://stackoverflow.com/a/39500986/901925

所以我组的日期排列,并利用这些信息构造总和'面具'。

为了讨论起见,考虑这个密集阵:

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]]) 

做一个稀疏副本:

In [118]: M=sparse.csr_matrix(A) 

产生一些群体的基础上,最后一列; 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]}) 

我可以遍历这些群体,横跨这些行收集的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)} 

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

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

掩蔽矩阵可能的工作是这样的:

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) 

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) 

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

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

这应该是更快和避免记忆误差(无环或大密集阵列)。

+0

伟大的解决方案。这些方法在小数据集上运行良好,但是是否有更改可以将它们应用于较大的数据集?我特别的问题是(69424,685296)稀疏矩阵。 jupyter在我试图用'{k:M [v,:]。sum(axis = 0)for k,v in grps.items()}'的时候挂起,当我尝试创建一个(69424,685296)零掩码。 –

+0

我添加了一种直接用'coo'风格的输入构造组掩码矩阵的方法。 – hpaulj

+0

我试着用样本数据和我自己的数据进行编辑,但是我收到一个'ValueError:用序列设置数组元素.'我已经搜索过并尝试通过更改数据类型和重新排列语法来解决问题但不能让它工作。有什么想法吗? –