在Python中存储设置数据的最佳方法是什么?

时间:2008-09-24 16:39:08

标签: python dictionary data-structures set

我有以下表格的数据清单:

[(id\__1_, description, id\_type), (id\__2_, description, id\_type), ... , (id\__n_, description, id\_type))

从属于同一组的文件加载数据。在每个组中,可以有多个相同的id,每个来自不同的文件。我不关心重复项,所以我认为存储所有这些的一个好方法是将它扔进Set类型。但是有一个问题。

有时对于相同的ID,描述可能略有不同,如下所示:

IPI00110753

  • Tubulin alpha-1A链
  • Tubulin alpha-1链
  • α-微管蛋白1
  • α-微管蛋白同种型M-α-1

(请注意,此示例取自uniprot protein database。)

我不在乎描述是否有所不同。我不能把它们扔掉,因为我使用的蛋白质数据库有可能不包含某个标识符的列表。如果发生这种情况,我希望能够向生物学家展示人类可读的描述,以便他们大致了解他们正在研究的蛋白质。

我目前正在使用字典类型解决此问题。但是我真的不喜欢这个解决方案,因为它使用了大量的内存(我有很多这样的ID)。这只是他们的中间列表。在将ID放入数据库之前,ID会进行一些额外的处理,因此我希望将数据结构保持较小。

我真的有两个问题。首先,我是否会使用Set类型(通过字典类型)获得更小的内存占用,或者我是否应该使用排序列表,每次我插入列表时检查是否存在ID,或者是否存在我没有想到的第三个解决方案?其次,如果Set类型是更好的答案,我如何键入它来查看元组的第一个元素而不是整个元素?

感谢您阅读我的问题,

更新

根据我收到的一些评论,让我澄清一点。我对数据结构所做的大部分工作都是插入其中。我只阅读了两次,一次用附加信息注释,*和一次插入数据库。然而,在我插入数据库之前,可能会有一些额外的注释。不幸的是,我不知道这是否会在这个时候发生。

现在我正在研究将这些数据存储在不基于散列表(即字典)的结构中。我希望新结构在插入时相当快,但读取它可能是线性的,因为我只做了两次。我试图远离哈希表以节省空间。是否有更好的结构,或者是一个关于它的好的哈希表?

*该信息是我通过查询uniprot获得的Swiss-Prot蛋白质标识符列表。

6 个答案:

答案 0 :(得分:2)

设置没有键。元素键。

如果您认为需要密钥,则需要映射。或多或少的定义。

即使使用二进制搜索,顺序列表查找也可能很慢。映射使用哈希并且速度很快。

你在谈论这样的字典吗?

{ 'id1': [ ('description1a', 'type1'), ('description1b','type1') ], 
  'id2': [ ('description2', 'type2') ],
...
}

这肯定是最小的。 ID仅代表一次。

也许你有类似的东西?

{ 'id1': ( ('description1a', 'description1b' ), 'type1' ),
  'id2': ( ('description2',), 'type2' ),
...
}

除非你使用struct模块,否则我不确定你能找到更紧凑的东西。

答案 1 :(得分:1)

我假设您尝试通过减少使用的内存来解决的问题是您的进程的地址空间限制。此外,您还可以搜索允许快速插入和合理顺序读取的数据结构。

使用较少的结构,除了字符串(str)

您要问的问题是如何在一个进程中构建数据以使用更少的内存。对此的一个规范答案是(只要你仍然需要关联查找),尽可能使用其他结构,然后使用python字符串(str,而不是unicode)。 python哈希(字典)可以非常有效地存储对字符串的引用(它不是b树实现)。

但是我认为你不会对这种方法走得很远,因为你所面对的是巨大的数据集,它们最终可能会超出你正在使用的机器的进程地址空间和物理内存。

替代解决方案

我会提出一个不同的解决方案,不涉及将数据结构更改为更难插入或解释的内容。

  • 将您的信息分成多个流程,每个流程都保存着方便的数据结构。
  • 使用套接字实现进程间通信,以便进程可以完全驻留在其他计算机上。
  • 尝试划分数据,以最大限度地减少进程间通信(与cpu周期相比,i / o速度非常慢)。

我概述的方法的优点是

  • 您可以在机器上完全使用两个或更多核心来提高性能
  • 您不受一个进程的地址空间限制,甚至不受一台计算机的物理内存的限制

分布式处理有很多软件包和方法,其中一些是

答案 2 :(得分:1)

如果您正在进行n路合并以删除重复项,那么您可能正在寻找以下内容。

此生成器将合并任意数量的源。每个来源必须是一个序列。 密钥必须位于位置0.它一次生成一个项目的合并序列。

def merge( *sources ):
    keyPos= 0
    for s in sources:
        s.sort()
    while any( [len(s)>0 for s in sources] ):
        topEnum= enumerate([ s[0][keyPos] if len(s) > 0 else None for s in sources ])
        top= [ t for t in topEnum if t[1] is not None ]
        top.sort( key=lambda a:a[1] )
        src, key = top[0]
        #print src, key
        yield sources[ src ].pop(0)

此生成器从序列中删除重复项。

def unique( sequence ):
    keyPos= 0
    seqIter= iter(sequence)
    curr= seqIter.next()
    for next in seqIter:
        if next[keyPos] == curr[keyPos]:
            # might want to create a sub-list of matches
            continue
        yield curr
        curr= next
    yield curr

这是一个脚本,它使用这些函数生成一个结果序列,它是删除了重复项的所有源的并集。

for u in unique( merge( source1, source2, source3, ... ) ):
    print u

每个序列中的完整数据集必须存在于内存中一次,因为我们在内存中进行排序。但是,结果序列实际上并不存在于内存中。实际上,它通过消耗其他序列来工作。

答案 3 :(得分:0)

如何使用{id: (description, id_type)}字典?或者{(id, id_type): description}字典if(id,id_type)是关键。

答案 4 :(得分:0)

Python中的集合是使用哈希表实现的。在早期版本中,它们实际上是使用集合实现的,但这改变了AFAIK。使用set保存的唯一内容就是每个条目的指针大小(指向值的指针)。

要仅使用元组的一部分作为哈希码,您必须子类化tuple并覆盖哈希码方法:

class ProteinTuple(tuple):
     def __new__(cls, m1, m2, m3):
         return tuple.__new__(cls, (m1, m2, m3))

     def __hash__(self):
         return hash(self[0])

请注意,在这种情况下,您需要支付__hash__的额外函数调用,否则它将是C方法。

我会选择Constantin的建议并从元组中取出id,看看它有多大帮助。

答案 5 :(得分:0)

它仍然是模糊的,但听起来你有几个[(id,description,type)的列表...]

id在列表中是唯一的,在列表之间是一致的。

你想创建一个UNION:一个列表,每个id出现一次,可能有多个描述。

出于某种原因,您认为映射可能太大了。你有这方面的证据吗?没有实际测量,不要过度优化。

这可能是(如果我猜错了)来自多个来源的标准“合并”操作。

source1.sort()
source2.sort()
result= []
while len(source1) > 0 or len(source2) > 0:
    if len(source1) == 0:
        result.append( source2.pop(0) )
    elif len(source2) == 0:
        result.append( source1.pop(0) )
    elif source1[0][0] < source2[0][0]:
        result.append( source1.pop(0) )
    elif source2[0][0] < source1[0][0]:
        result.append( source2.pop(0) )
    else:
        # keys are equal
        result.append( source1.pop(0) )
        # check for source2, to see if the description is different.

这通过排序和合并来组合两个列表的并集。没有映射,没有哈希。