【问题标题】:Find duplicate records in large text file在大文本文件中查找重复记录
【发布时间】:2013-04-27 04:47:40
【问题描述】:

我在一台 linux 机器 (Redhat) 上,我有一个 11GB 的文本文件。文本文件中的每一行都包含一条记录的数据,并且该行的前 n 个字符包含该记录的唯一标识符。该文件包含超过 2700 万条记录。

我需要验证文件中没有具有相同唯一标识符的多条记录。我还需要对一个 80GB 的文本文件执行此过程,因此任何需要将整个文件加载到内存中的解决方案都不实用。

【问题讨论】:

  • 听起来是时候建立数据库了。这是一个庞大的文件。

标签: python linux bash shell


【解决方案1】:

逐行读取文件,因此您不必将其全部加载到内存中。

为每一行(记录)创建一个 sha256 哈希(32 个字节),除非您的标识符更短。

将哈希/标识符存储在numpy.array 中。这可能是最紧凑的存储方式。 2700 万条记录乘以 32 字节/哈希为 864 MB。这应该适合这些天体面机器的记忆。

为了加快访问速度,您可以使用第一个,例如2 个字节的散列作为 collections.defaultdict 的键,并将其余散列放在值的列表中。这实际上会创建一个包含 65536 个桶的哈希表。对于 27e6 条记录,每个存储桶平均包含大约 400 个条目的列表。 这意味着比 numpy 数组更快的搜索,但它会使用更多的内存。

d = collections.defaultdict(list)
with open('bigdata.txt', 'r') as datafile:
    for line in datafile:
        id = hashlib.sha256(line).digest()
        # Or id = line[:n]
        k = id[0:2]
        v = id[2:]
        if v in d[k]:
            print "double found:", id
        else:
            d[k].append(v)

【讨论】:

    【解决方案2】:

    这项工作的正确工具:将您的记录放入数据库。除非你手头有 Postgres 或 MySQL 安装,否则我会选择 sqlite。

    $ sqlite3 uniqueness.sqlite
    create table chk (
      ident char(n), -- n as in first n characters
      lineno integer -- for convenience
    );
    ^D
    

    然后我将唯一标识符和行号插入到该表中,可能使用如下 Python 脚本:

    import sqlite3 # install pysqlite3 before this
    n = ... # how many chars are in the key part
    lineno = 0
    
    conn = sqlite3.connect("uniqueness.sqlite")
    cur = conn.cursor()
    with open("giant-file") as input:
      for line in input:
        lineno +=1
        ident = line[:n]
        cur.execute("insert into chk(ident, lineno) values(?, ?)", [ident, lineno])
    cur.close()
    conn.close()
    

    在此之后,您可以索引表并使用SQL:

    $ sqlite3 uniqueness.sqlite
    create index x_ident on chk(ident); -- may take a bit of time
    
    -- quickly find duplicates, if any
    select ident, count(ident) as how_many
    from chk
    group by ident
    having count(ident) > 1;
    
    -- find lines of specific violations, if needed
    select lineno 
    from chk
    where ident = ...; -- insert a duplicate ident
    

    是的,我尝试了大部分代码,它应该可以工作:)

    【讨论】:

      【解决方案3】:

      假设我不能使用数据库,我会尝试类似的东西

      # read the file one line at a time http://stackoverflow.com/a/6475407/322909,
      #be sure to read the comments
      
      keys = set()
      
      with open("bigfile.txt") as f:
          for line in f:
              key = get_key(line)
              if key in keys:
                  print "dup"
              else:
                  keys.add(key)
      

      【讨论】:

      • 如果你打算这样做,你真的希望keys是一个集合,而不是一个列表。
      【解决方案4】:

      试试这个:

      n=unique identifier size
      cat 11gb_file | cut -c-$n | sort | uniq -cd
      

      这将输出任何重复的标识符以及它们出现的次数。

      【讨论】:

      • 对 2700 万个单词进行排序将是一项昂贵的操作。
      • @glennjackman:如果有多个重复项,那可能是值得的。
      • 而不是sortuniq 选项,使用awk '{++c[$1]; if (c[$1] > 1) print;}。不需要排序 - 虽然awk 关联数组会有些慢,但它不应该像尝试排序那样糟糕......
      【解决方案5】:

      我还没有在那么大的文件上尝试过这个,但是......假设 n 个字符的固定位置是 7,并且行不超过 999+7 个字符,这可能会完成这项工作:

       awk  'BEGIN{FIELDWIDTHS="7 999"} ! a[$1]++' file > newfile
      

      【讨论】:

        【解决方案6】:

        我绝不会建议您尝试在 Python 中过滤如此庞大的文本文件。不管你如何处理它,你都需要通过一些复杂的步骤来确保你不会耗尽内存。

        首先想到的是创建行的散列,然后使用散列查找重复项。由于您还保存了行号,因此您可以直接比较文本以确保没有哈希冲突。

        但是,最简单的解决方案是将文本文件转换为数据库,以便您快速排序、搜索和过滤掉重复项。然后,如果确实需要,您可以使用它重新创建文本文件。

        【讨论】:

          【解决方案7】:

          Read large text files in Python, line by line without loading it in to memory

          这个问题的答案是这样的,

          with open("log.txt") as infile:
              for line in infile:
                  do_something_with(line)
          

          也许这会对你有所帮助,祝你好运。

          【讨论】:

            猜你喜欢
            • 2013-08-20
            • 2016-12-24
            • 2016-10-17
            • 2012-03-02
            • 1970-01-01
            • 1970-01-01
            • 2020-06-28
            • 2013-10-10
            • 2016-08-04
            相关资源
            最近更新 更多