有人知道Python中一个很好的稀疏一维数组库吗?

时间:2010-06-09 03:58:25

标签: python performance algorithm arrays sparse-array

我正在研究一种使用int64s数组的算法。这些数组通常是稀疏的,并且不断地读取和写入。我目前正在使用相对较大的本机阵列,性能良好,但内存使用率很高(正如预期的那样)。

我希望能够让数组实现不浪费空间用于未使用的值,并允许索引偏移量不为零。举个例子,如果我的数字从1,000,000开始,我希望能够从1,000,000开始索引我的数组,并且不需要浪费具有一百万个未使用值的内存。

数组读取和写入需要快速。扩展到新领域可能会有一点延迟,但如果可能,读取和写入应为O(1)。

有人知道可以做到的图书馆吗?

谢谢!

更新为提及int64作为数据类型。

5 个答案:

答案 0 :(得分:4)

听起来像blist类型(documentationdownload)可能就是您正在寻找的内容(免责声明:我是作者)。它与Python的list具有完全相同的接口,因此没有学习曲线,但它具有不同的性能特征。特别是,在许多情况下,它可以有效地处理稀疏列表。下面是一个创建包含2 ** 29个元素的列表的示例。这几乎是瞬间完成的。以这种方式创建的稀疏列表使用O(log n)空间。

>>> from blist import *
>>> x = blist([0])             # x is a blist with one element
>>> x *= 2**29                 # x is a blist with > 500 million elements
>>> x.append(5)                # append to x
>>> y = x[4:-234234]           # Take a 500 million element slice from x
>>> del x[3:1024]              # Delete a few thousand elements from x

迭代整个列表的操作仍需要O(n)时间(removereversecount等。文档说明了每个操作的时间复杂性,因此您应该能够评估它是否能满足您的需求。

答案 1 :(得分:1)

您可以将numpy sparse matrix重新映射到稀疏数组中 - 或者考虑使用哈希表(python dict)。就偏移量而言,只需包装您正在使用的任何存储类,并使您拥有自己的插入/查找/删除方法。

答案 2 :(得分:1)

我不知道任何Python,所以这可能是一个没有答案的:

在某些语言中,您可以通过将索引空间中的函数定义到数据空间来模拟稀疏数组。例如(伪代码):

f[1000000] = 32;
f[2000000] = 51;

在某些语言(最好的语言)中,数组引用的形式(例如f[3])看起来就像函数调用的形式(例如f[3])。当然,这是因为数组定义了从索引空间到数据空间的函数。以这种方式实现更高维度的稀疏数组非常容易。

答案 3 :(得分:1)

为什么不直接使用dict?

sparse = dict()
sparse[100000] = 1234
sparse[123456] = 2345

答案 4 :(得分:1)

另一种选择 - 至少如果你愿意自己实施 - 是Page table。这通常用于虚拟内存系统中,以将虚拟地址映射到物理地址,如果地址空间稀疏填充,并且使用的地址是群集的,则效果最佳。如果使用过的地址是随机分布的,那么效果就会降低。

页表的基本方法与Trie - 递归细分相同。页表具有一些固定数量的级别,每个节点是固定大小的数组。如果给定子节点的条目为null,则该节点覆盖的所有叶子都为空。页表的主要优点是查找速度快,只需要几个位移和解引用。

让我们看一个2级页面表的简单Python实现:

class Pagetable(object):
  def __init__(self, num_bits=8):
    """Creates a new Pagetable with num_bits bits per level.

    Args:
      num_bits: The number of bits per pagetable level.
        A 2 level pagetable will be able to store indexes between 0 and 2^(num_bits*2).
    """
    self.num_bits = num_bits
    self.mask = (1 << num_bits) - 1
    self.root = [None] * (2 ** num_bits)

  def __getitem__(self, idx):
    page = self.root[idx >> self.num_bits]
    return page and page[idx & self.mask]

  def __setitem__(self, idx, val):
    page = self.root[idx >> self.num_bits]
    if not page:
      page = self.root[idx >> self.num_bits] = [None] * (2 ** self.num_bits)
    page[idx & self.mask] = val