代码之家  ›  专栏  ›  技术社区  ›  deckardk

用相同的键连接大型词典

  •  2
  • deckardk  · 技术社区  · 12 年前

    我有大约10个巨大的文件,其中包含python字典,如下所示:

        dict1:
        {   
            'PRO-HIS-MET': {
                'A': ([1,2,3],[4,5,6],[7,8,9]),
                'B': ([5,2],[6],[8,9]),
                'C': ([3],[4],[7,8])},
            'TRP-MET-GLN': {
                'F': ([-5,-4,1123],[-7,-11,2],[-636,-405])}
        }
    
        dict2:
        {   
            'PRO-HIS-MET': {
                'J': ([-657], [7,-20,3], [-8,-85,15])}
    
            'TRP-MET-GLN':{
                'K': ([1,2,3],[4,50,6],[7,80,9]), 
                'L': ([5,20],[60,80],[8,9])}
        }
    

    基本上它们都是字典中的字典。每个文件的大小约为1GB(以上只是数据的一个示例)。不管怎样,我想做的是把这10本词典放在一起:

        final:
        {
            'PRO-HIS-MET': {
                'A': ([1,2,3],[4,5,6],[7,8,9]),
                'B': ([5,2],[6],[8,9]),
                'C': ([3],[4],[7,8])
                'J': ([-657], [7,-20,3], [-8,-85,15])},
            'TRP-MET-GLN': {
                'F': ([-5,-4,1123],[-7,-11,2],[-636,-405])
                'K': ([1,2,3],[4,50,6],[7,80,9]), 
                'L': ([5,20],[60,80],[8,9])}
        }
    

    我在小文件上尝试了以下代码,效果很好:

        import csv
        import collections
        d1 = {}
        d2 = {}
        final = collections.defaultdict(dict)
    
        for key, val in csv.reader(open('filehere.txt')):
            d1[key] = eval(val)
        for key, val in csv.reader(open('filehere2.txt')):
            d2[key] = eval(val)
    
        for key in d1:
            final[key].update(d1[key])
        for key in d2:
            final[key].update(d2[key])
    
        out = csv.writer(open('out.txt', 'w'))
        for k, v in final.items():
            out.writerow([k, v])
    

    然而,如果我在1GB的文件上尝试这样做,我会很快耗尽内存,因为我会在内存中保留d1和d2以及最终的字典。

    我有几个想法:

    1. 有没有一种方法可以让我从分段的字典中加载键,比较它们,如果在多个字典中找到相同的键,只需组合值?
    2. 与其将字典合并到一个巨大的文件中(这可能会在未来给我带来内存问题),我如何在合并数据后创建多个单独的文件,其中包含一个键的所有值?例如,对于上述数据,我只需要:

      pro-his-met.txt:
      'PRO-HIS-MET': {
          'A': ([1,2,3],[4,5,6],[7,8,9]),
          'B': ([5,2],[6],[8,9]),
          'C': ([3],[4],[7,8])
          'J': ([-657], [7,-20,3], [-8,-85,15])}
      trp-met-gln.txt:
      'TRP-MET-GLN': {
          'F': ([-5,-4,1123],[-7,-11,2],[-636,-405])
          'K': ([1,2,3],[4,50,6],[7,80,9]), 
          'L': ([5,20],[60,80],[8,9])}
      

    作为一名生物学家,我没有太多的编程经验(你可能已经猜到上面的数据代表了一个生物信息学问题),所以任何帮助都将不胜感激!

    3 回复  |  直到 12 年前
        1
  •  1
  •   nneonneo    12 年前

    这个 shelve 模块是一个非常易于使用的Python数据库。它远不如真正的数据库强大(请参阅@Voo的回答),但它可以操作大型词典。

    首先,从词典中创建工具架:

    import shelve
    s = shelve.open('filehere.db', flag='n', protocol=-1, writeback=False)
    for key, val in csv.reader(open('filehere.txt')):
        s[key] = eval(val)
    s.close()
    

    既然你已经把所有东西都整齐地放好了,你就可以高效地操作词典了:

    import shelve
    import itertools
    s = shelve.open('final.db', flag='c', protocol=-1, writeback=False)
    s1 = shelve.open('file1.db', flag='r')
    s2 = shelve.open('file2.db', flag='r')
    for key, val in itertools.chain(s1.iteritems(), s2.iteritems()):
        d = s.get(key, {})
        d.update(val)
        s[key] = d # force write
    s.close()
    
        2
  •  1
  •   Voo    12 年前

    就个人而言,这听起来像是数据库被发明用来解决的问题的原型。是的,你可以自己解决这个问题,保留文件,为了优化性能,将它们映射到内存中,让操作系统处理交换等,但这真的很复杂,很难做到很好。

    如果你能让一个投入了数百万工时的数据库来处理它,为什么要付出这么多努力?这将更加高效,而且作为一个额外的好处,查询信息更加容易。

    我看到Oracle数据库存储的数据远远超过10 GB,没有任何问题,我相信postgre也会处理得很好。。好的一点是,如果你使用ORM,你可以将这些细节抽象出来,并在必要时担心它们。

    此外,虽然生物信息学不是我的专长,但我很确定有专门针对生物信息学的解决方案——也许其中一个非常适合?

        3
  •  0
  •   DarrenMB    12 年前

    这个概念应该可行。

    我会考虑对文件进行多次传递,每次传递一部分密钥。并保存该结果。

    例如,如果您在一次传递中创建了所有键的唯一第一个字符的列表,然后处理每个传递到新输出文件的过程。如果是简单的字母数据,那么逻辑选择将是字母表中每个字母的循环。

    例如,在“p”通行证中,您将处理“PRO-HIS-MET”

    然后,您将在最后组合所有文件的所有结果。

    如果你是一名开发人员,如果你能处理这种交互,那么前面回答中的数据库思想可能是最好的方法。这个想法需要创建一个2级结构,在其中插入和更新记录,然后用SQL语句查询结果。