Python省内存的稀疏矩阵存储方案

前端之家收集整理的这篇文章主要介绍了Python省内存的稀疏矩阵存储方案前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
对python这个高级语言感兴趣的小伙伴,下面一起跟随编程之家 jb51.cc的小编两巴掌来看看吧!

推荐系统中经常需要处理类似user_id,item_id,rating这样的数据,其实就是数学里面的稀疏矩阵,scipy中提供了sparse模块来解决这个问题,但scipy.sparse有很多问题不太合用:1、不能很好的同时支持data[i,...]、data[...,j]、data[i,j]快速切片;2、由于数据保存在内存中,不能很好的支持海量数据处理。

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

  1. # @param 一种比较省内存的稀疏矩阵Python存储方案
  2. # @author 编程之家 jb51.cc|www.www.jb51.cc
  3. '''
  4. Sparse Matrix
  5. '''
  6. import struct
  7. import numpy as np
  8. import bsddb
  9. from cStringIO import StringIO
  10. class DictMatrix():
  11. def __init__(self,container = {},dft = 0.0):
  12. self._data = container
  13. self._dft = dft
  14. self._nums = 0
  15. def __setitem__(self,index,value):
  16. try:
  17. i,j = index
  18. except:
  19. raise IndexError('invalid index')
  20. ik = ('i%d' % i)
  21. # 为了节省内存,我们把j,value打包成字二进制字符串
  22. ib = struct.pack('if',j,value)
  23. jk = ('j%d' % j)
  24. jb = struct.pack('if',i,value)
  25. try:
  26. self._data[ik] += ib
  27. except:
  28. self._data[ik] = ib
  29. try:
  30. self._data[jk] += jb
  31. except:
  32. self._data[jk] = jb
  33. self._nums += 1
  34. def __getitem__(self,index):
  35. try:
  36. i,j = index
  37. except:
  38. raise IndexError('invalid index')
  39. if (isinstance(i,int)):
  40. ik = ('i%d' % i)
  41. if not self._data.has_key(ik): return self._dft
  42. ret = dict(np.fromstring(self._data[ik],dtype = 'i4,f4'))
  43. if (isinstance(j,int)): return ret.get(j,self._dft)
  44. if (isinstance(j,int)):
  45. jk = ('j%d' % j)
  46. if not self._data.has_key(jk): return self._dft
  47. ret = dict(np.fromstring(self._data[jk],f4'))
  48. return ret
  49. def __len__(self):
  50. return self._nums
  51. def __iter__(self):
  52. pass
  53. '''
  54. 文件生成matrix
  55. 考虑到dbm读写的性能不如内存,我们做了一些缓存,每1000W次批量写入一次
  56. 考虑到字符串拼接性能不太好,我们直接用StringIO来做拼接
  57. '''
  58. def from_file(self,fp,sep = 't'):
  59. cnt = 0
  60. cache = {}
  61. for l in fp:
  62. if 10000000 == cnt:
  63. self._flush(cache)
  64. cnt = 0
  65. cache = {}
  66. i,v = [float(i) for i in l.split(sep)]
  67. ik = ('i%d' % i)
  68. ib = struct.pack('if',v)
  69. jk = ('j%d' % j)
  70. jb = struct.pack('if',v)
  71. try:
  72. cache[ik].write(ib)
  73. except:
  74. cache[ik] = StringIO()
  75. cache[ik].write(ib)
  76. try:
  77. cache[jk].write(jb)
  78. except:
  79. cache[jk] = StringIO()
  80. cache[jk].write(jb)
  81. cnt += 1
  82. self._nums += 1
  83. self._flush(cache)
  84. return self._nums
  85. def _flush(self,cache):
  86. for k,v in cache.items():
  87. v.seek(0)
  88. s = v.read()
  89. try:
  90. self._data[k] += s
  91. except:
  92. self._data[k] = s
  93. if __name__ == '__main__':
  94. db = bsddb.btopen(None,cachesize = 268435456)
  95. data = DictMatrix(db)
  96. data.from_file(open('/path/to/log.txt','r'),',')
  97. # End www.jb51.cc

测试4500W条rating数据(整形,整型,浮点格式),922MB文本文件导入,采用内存dict储存的话,12分钟构建完毕,消耗内存1.2G,采用示例代码中的bdb存储,20分钟构建完毕,占用内存300~400MB左右,比cachesize大不了多少,数据读取测试:

  1. # @param 一种比较省内存的稀疏矩阵Python存储方案
  2. # @author 编程之家 jb51.cc|www.www.jb51.cc
  3. import timeit
  4. timeit.Timer('foo = __main__.data[9527,...]','import __main__').timeit(number = 1000)
  5. # End www.jb51.cc

消耗1.4788秒,大概读取一条数据1.5ms。

采用类Dict来存储数据的另一个好处是你可以随便用内存Dict或者其他任何形式的DBM,甚至传说中的Tokyo Cabinet….

好的,码完收工。

猜你在找的Python相关文章