【问题标题】:Efficient dictionary inversion on dictionary like file字典类文件的高效字典反转
【发布时间】:2013-06-24 22:18:43
【问题描述】:

无法有效地反转(交换键的值和值的键)存储在文件中的大型 (2.8GB) 字典。效率是问题,我目前的解决方案是:

  • 逐行读取字典文件(格式:,,,...)

    对于每一行:

    • 运行上一遍的in-progress输出文件,将其逐行复制到临时文件中
    • 在适当的地方(按字母顺序)将字典值 (val1,val2,...) 插入到临时文件中,每个新值都是一个新键
    • 用临时文件覆盖之前的pass输出文件
    • 重复直到处理完所有字典行 (以格式结束::,,,.. :,,.. :,,,..)

这个算法非常笨拙,至少输出文件必须被写入 n^2 次(我认为...),其中 n 约为 30,000,000。缺乏可用内存会阻止整个读取它并在内存中处理它。

可能没有比让它继续下去更好的解决方案了,但如果有人有任何想法,我们将不胜感激。

编辑:应该明确最后输出的每一行都可以包含多个键作为值。

【问题讨论】:

  • 您是否查看过dbm 和相关模块(gdbmanydbm)?
  • 我很困惑。每个<key>,<val1>,<val2> 行是否应该代表原始字典中的一个键值对,其中键是row[0],值类似于tuple(row[1:])?您是想将row[1]: row[0], row[2]: row[0], … 放入“倒置”字典中,还是将tuple(row[1:]): row[0] 放入或其他不同的地方?
  • @abarnert 是的,你的正确,第一行项目是键,后续项目是该键的值。
  • @senderle 不,我从未听说过它们,尽管它们看起来很有前途。

标签: python file dictionary


【解决方案1】:

我会建议以下 3 道解决方案:

  1. 遍历原始字典文件一次,为每个 val 的输出文件添加一个 val 键行。

  2. 使用 unix sort 命令或其他快速排序程序对步骤 1 中的输出文件进行排序。

  3. 如果第 1 步可能产生需要删除的重复项,请在编写最终输出文件时从第 2 步迭代输出文件并删除重复项。由于 2 的输出文件已排序,因此您只需要一次传递和最少的内存即可执行此操作。

【讨论】:

    【解决方案2】:

    我不确定我是否理解您的问题,所以让我描述一下我认为您想要什么,并举例说明,然后说明如何去做。

    您的输入是这样的文本 CSV 文件:

    a,1,2,3
    b,4,5,6
    c,7,8,9
    

    每一行都是一个键,后面跟着一组值。它代表一个字典,其中每个值都是一个元组——例如,d['a'] = (1,2,3)

    输出应该是这样的 CSV 文件:

    1,a
    2,a
    3,a
    4,b
    5,b
    6,b
    7,c
    8,c
    9,c
    

    ...但是以任意的行顺序。原始文件中的每个值都映射到其所在行中第 0 列的键。 (如果值重复,则任意选择一个键。)


    所以,如果你在内存中做这一切,它看起来像这样:

    in_dict = {'a': (1, 2, 3), 'b': (4, 5, 6), 'c': (7, 8, 9)}
    out_dict = {value: key for key, value_set in in_dict.items() for value in value_set}
    

    唯一的问题是这可能需要大约 5.2GB 的 RAM 来处理 2.6GB 的字典,所以你已经将 in_dict 以你的特殊形式存储在磁盘上,并且想要将 out_dict 写入磁盘以类似的形式,无需将所有内容都读入内存。

    最简单的方法是使用 DBM 作为中间存储。将 CSV 读入一个 DBM——它的结构与上面的out_dict 完全相同;只是写起来有点复杂。

    显然,您需要使用 csv 模块来读取(和写入)CSV,并使用 dbm(或者,对于 Python 2.x,anydbm)模块来读取 DBM。

    with contextlib.closing(dbm.open('kv.dbm', 'n')) as db:
        with open('kv.csv') as f:
            for row in csv.reader(f):
                for col in row[1:]:
                    db[col] = row[0]
    

    然后将该 DBM 写入您喜欢的格式。如果dbm 对象有一个items 方法,那就是:

        with open('kvt.csv', 'w') as f:
            csv.writer(f).writerows(dbm.items())
    

    既然没有,你可以添加一个,或者编写一个 genexp:

            csv.writer(f).writerows((key, db[key]) for key in db.keys())
    

    或显式迭代:

            w = csv.writer(f)
            for key in db.keys():
                w.writerow((key, db[key])
    

    您可能还想使用tempfile 使 DBM 成为一个临时文件,在您完成后自动清理它。由于 Windows 和 *nix 以及 Python 版本之间的细节略有不同,我将把这部分作为练习留给读者。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-09
      • 1970-01-01
      • 2013-10-06
      • 2010-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多