zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Python使用稀疏矩阵节省内存实例

Python内存实例 使用 矩阵 节省 稀疏
2023-06-13 09:15:32 时间

推荐系统中经常需要处理类似user_id,item_id,rating这样的数据,其实就是数学里面的稀疏矩阵,scipy中提供了sparse模块来解决这个问题,但scipy.sparse有很多问题不太合用:

1、不能很好的同时支持data[i,...]、data[...,j]、data[i,j]快速切片;
2、由于数据保存在内存中,不能很好的支持海量数据处理。

要支持data[i,...]、data[...,j]的快速切片,需要i或者j的数据集中存储;同时,为了保存海量的数据,也需要把数据的一部分放在硬盘上,用内存做buffer。这里的解决方案比较简单,用一个类Dict的东西来存储数据,对于某个i(比如9527),它的数据保存在dict["i9527"]里面,同样的,对于某个j(比如3306),它的全部数据保存在dict["j3306"]里面,需要取出data[9527,...]的时候,只要取出dict["i9527"]即可,dict["i9527"]原本是一个dict对象,储存某个j对应的值,为了节省内存空间,我们把这个dict以二进制字符串形式存储,直接上代码:

复制代码代码如下:

"""
SparseMatrix
"""
importstruct
importnumpyasnp
importbsddb
fromcStringIOimportStringIO
 
classDictMatrix():
   def__init__(self,container={},dft=0.0):
       self._data =container
       self._dft  =dft
       self._nums =0
 
   def__setitem__(self,index,value):
       try:
           i,j=index
       except:
           raiseIndexError("invalidindex")
 
       ik=("i%d"%i)
       #为了节省内存,我们把j,value打包成字二进制字符串
       ib=struct.pack("if",j,value)
       jk=("j%d"%j)
       jb=struct.pack("if",i,value)
 
       try:
           self._data[ik]+=ib
       except:
           self._data[ik]=ib
       try:
           self._data[jk]+=jb
       except:
           self._data[jk]=jb
       self._nums+=1
 
   def__getitem__(self,index):
       try:
           i,j=index
       except:
           raiseIndexError("invalidindex")
 
       if(isinstance(i,int)):
           ik=("i%d"%i)
           ifnotself._data.has_key(ik):returnself._dft
           ret=dict(np.fromstring(self._data[ik],dtype="i4,f4"))
           if(isinstance(j,int)):returnret.get(j,self._dft)
 
       if(isinstance(j,int)):
           jk=("j%d"%j)
           ifnotself._data.has_key(jk):returnself._dft
           ret=dict(np.fromstring(self._data[jk],dtype="i4,f4"))
 
       returnret
 
   def__len__(self):
       returnself._nums
 
   def__iter__(

测试代码:

复制代码代码如下:

importtimeit
timeit.Timer("foo=__main__.data[9527,...]","import__main__").timeit(number=1000)

消耗1.4788秒,大概读取一条数据1.5ms。
采用类Dict来存储数据的另一个好处是你可以随便用内存Dict或者其他任何形式的DBM,甚至传说中的TokyoCabinet….

好了,码完收工。