【问题标题】:Storing large amount of boolean data in python在python中存储大量布尔数据
【发布时间】:2012-02-11 18:48:48
【问题描述】:

我需要存储稀疏矩阵数据。 数据大小为10^6 10^4 列。 在每一列中,我存储了一个 0 的向量,除了 true 的少数值。

然后我需要对每个矩阵中的列求和,并将每一行乘以一个标量。 我尝试了字典,但是当我需要求和和乘法时它们会失败。

你会用什么?

附言。 numpy.zeros 太小了

【问题讨论】:

  • 您是仅存储此数据还是将其作为矩阵处理?你只是在寻找更小的内存占用还是其他什么?你想让它看起来像一个列表吗?

标签: python arrays numpy


【解决方案1】:

两个字典怎么样?假设这是矩阵(x 代表True):

   0  1  2  3  4  5  6  7
0  x     x        x 
1     x
2                       x
3              x
4
5
6        x        x
7

你只需要存储

rows = {0: [0, 2, 5], 1: [1], 2: [7], 3: [4], 6: [2, 5]}

你可以很容易地把它变成

columns = {0: [0], 1: [1], 2: [0, 6], 4: [3], 5: [0, 6], 7: [2]}

使用类似的东西

columns = {}
for row in rows:
    for column in rows[row]:
        columns.setdefault(column, []).append(row)

然后对列 (sum(1 for x in column[2])) 或行求和,然后将结果与您想要的任何值相乘。

【讨论】:

  • 当您使用索引作为键时,为什么还要使用 dicts?那时,只要有列表。
  • @Lattyware:因为它是一个稀疏矩阵。根据 OP 的规范想象一百万行和 10.000 列(矩阵中只有一百个点被占用)。
  • 我认为将数据实际存储在两个字典中是可以的,但我想说“这样做的方法”是围绕它们构建一个类,这样你就可以获得很好的执行方法插入、重置和所需的操作。
  • 这是由内而外的。您应该将 (row, col) 的元组存储在一个字典中...
  • @Colt45:这没有多大意义。我可以想象将(row, col) 元组存储在一个列表中,并且取决于插入、删除、对列求和等的频率,它可能优于我的解决方案,但为什么在字典中?什么是键,什么是值?我编写了我的解决方案,以便允许跨行和/或列快速求和。我会说这取决于用例。
【解决方案2】:

正如其他人提到的,你应该看看scipy.sparse:

http://docs.scipy.org/doc/scipy/reference/sparse.html

有许多不同的格式针对各种稀疏运算进行了优化,包括标量乘法和求和。

例如:

import scipy.sparse
import numpy as np

rows = np.array([1,100,1000])
cols = np.array([100,99,1474])
vals = np.ones_like(rows)

A = scipy.sparse.coo_matrix((vals,(rows,cols)),shape=(int(1E6),int(1E6)),dtype=np.bool)

然后乘以一个标量并取和:

B = 3*A
B.sum() # 9

【讨论】:

  • 我没有检查它,但是我曾经在 Matlab 中使用过稀疏矩阵。我先查了字典,结果很好。
【解决方案3】:

根据您的需要,实际上有数百种方法可以做到这一点。 Wikipedia 上的 Sparse Matrix 条目是一个很好的开始,可以找到一种专门适用于您需求的方法。

作为一个非常简单的示例,您可以像这样使用Dictionary of Keys 类:

class SparseDOK(dict):

    def __init__(self):
        pass

    def __setitem__(self,key,value):
        if value in[0,0.0,False,None]:
            dict.__setitem__(self,key,False)
            dict.__delitem__(self,key)
        else:
            dict.__setitem__(self,key,True)

    def __getitem__(self, key):    
        try: 
            return dict.__getitem__(self, key)

        except KeyError: 
            return False


>>> dok=SparseDOK()
>>> dok[10,20]=55
>>> print dok
{(10, 20): True}
>>> print dok[10,20]
True
>>> print dok[55,300]      
False
>>> dok[10,20]=False
>>> print dok[10,20]
False

任意“矩阵”中的每个条目都假定为 False,除非特别设置为 True。您需要添加错误检查,但这将非常紧凑和快速。

构造Key Dictionary的优点是数据结构的构造非常高效。您只需将原始数据浏览一次,即可轻松添加或删除数据。缺点是一旦构建了矩阵,它的交互处理就会减少。

由于字典键是元组,因此按行或列添加索引很简单。由于整个矩阵需要在构造之后进行处理才能执行此操作,因此我们可以构造一个包含任何所需总和或乘积的 dict,然后引用已处理数据的该 dict。

>>> dok[10,20]=True
>>> dok[10,2000]=True
>>> dok[11,2000]=True
>>> dok[35000,2000]=True
>>> dok[10,35000]=True
>>> print dok
{(11, 2000): True, (10, 2000): True, (35000, 2000): True, (10, 20): True, (10, 35000): True}
cols={}
for tup in dok.keys():
    if tup[1] not in cols:
        cols[tup[1]]=1
    else:
        cols[tup[1]]+=1    

>>> print cols
{2000: 3, 35000: 1, 20: 1}

现在您可以参考 cols 中的 col 键来获取 col 行的总和。添加产品等很简单。请记住,如果原始 DOK 被编辑或更改,您需要重新计算总和/产品。如果您预计 DOK 在首次创建后会经常更改,则可以保留一个运行总计。

如果您的需求更复杂,请考虑使用SciPy 或Pysparse。如您所见,SciPy 中有 7 种不同的稀疏矩阵格式。不要重新发明别人已经做得更好的东西......

【讨论】:

  • 每次你想对单个列或行的值求和时,不需要遍历整个字典吗?
  • @Tim Pietzcker:是的,正如目前所写的那样,您需要遍历整个字典。将按列保持运行总计添加到类中是相当简单的,这可能会减慢数据结构的构建。然而,这种用于一般 DOK 的元组字典的方法与 SciPY 对 DOK 的处理方式相同。 OP 没有具体说明快速构建还是快速处理更重要。 A DOK 具有构建速度快的优点。由于 OP 表示他在可能数百万个错误中只有几个正确,这似乎是一个公平的权衡。
  • @Tim Pietzcker:不是每次都没有。 cols dict 被计算为 col 总和的 dict,因此您只需在构建后一次遍历布尔值的稀疏矩阵。然后只需参考 cols dict 来获取 col 的所有总和。如果我认为原始数据发生更改,您将需要再次 calc cols,但这不是 OP 指定的。
  • 您认为哪种方式更快:创建字典还是 scipy.sparse?
  • @Intelligent-Infrastructure:您需要定义 faster 吗?写得更快?更快地构建一次矩阵?交互式?更快地处理一次矩阵?交互式?调试速度更快?更快地安装在其他机器上?如果您只构建一次 DOK,然后构建一次求和和乘积的派生 dict,则 DOK 很简单,易于调试,并且可能非常快。 SciPy 速度极快,非常无错误,而且许多部分都是用 C 编写的。SciPy 有一个学习曲线,它必须达到目标,并且有一些开销。我需要更多信息才能确定地告诉你。测试一下。
猜你喜欢
  • 2018-03-07
  • 2014-04-26
  • 2010-10-10
  • 1970-01-01
  • 2012-08-04
  • 2017-09-28
  • 2015-08-25
  • 2011-07-25
相关资源
最近更新 更多