【问题标题】:Remove All Duplicates In A Large Text File删除大文本文件中的所有重复项
【发布时间】:2014-05-02 15:21:55
【问题描述】:

我真的被这个问题难住了,因此我已经停止了一段时间的工作。我处理非常大的数据。我每周收到大约 200gb 的 .txt 数据。数据范围可达 5 亿行。其中很多是重复的。我猜只有 20gb 是独一无二的。我已经制作了几个自定义程序,包括哈希删除重复项、外部删除重复项,但似乎都没有。最新的一个使用临时数据库,但需要几天时间才能删除数据。

所有程序的问题是它们在某个时间点后崩溃,并且在这些程序上花费了大量资金之后,我想我会上网看看是否有人可以提供帮助。我知道这个问题之前已经在这里得到了回答,我在过去的 3 个小时里在这里阅读了大约 50 个线程,但似乎没有一个问题与我相同,即庞大的数据集。

任何人都可以为我推荐任何东西吗?它需要超级准确和快速。最好不要基于内存,因为我只有 32gb 的内存可以使用。

【问题讨论】:

  • 这个问题如果不正确标记就不会引起太多关注。您应该使用您打算使用的语言对其进行标记。
  • 谢谢菲利克斯,我已经做到了
  • 您能否更具体地说明重复删除的范围?您是否需要从最后一天的数据中删除重复行?最后一周?自古以来?此外,除了重复删除之外,您是否要求数据保持“有序”?如果是这样,保留哪个副本是否重要?
  • 您实际使用的是什么语言?我很确定这在每种语言中都是不同的。
  • 200gb 和 500M 行 = 每行大约 400 个字符...需要比较整行吗?你能提供一个示例数据吗?你用什么操作系统?

标签: c# java c++ perl duplicates


【解决方案1】:

删除重复项的标准方法是对文件进行排序,然后执行顺序传递以删除重复项。对 5 亿行进行排序并非易事,但它肯定是可行的。几年前,我有一个日常流程是在 16 GB 的机器上对 50 到 100 GB 的数据进行排序。

顺便说一句,您也许可以使用现成的程序来做到这一点。当然,GNU 排序实用程序可以对大于内存的文件进行排序。我从来没有在 500 GB 的文件上尝试过,但你可以试一试。您可以将其与GNU Core Utilities 的其余部分一起下载。该实用程序有一个--unique 选项,因此您应该可以只使用sort --unique input-file > output-file。它使用一种类似于我在下面描述的技术。我建议先在 100 兆字节的文件上尝试,然后慢慢处理更大的文件。

使用 GNU 排序和我在下面描述的技术,如果输入目录和临时目录位于不同的物理磁盘上,它的性能会好很多。将输出放在第三个物理磁盘上,或与输入放在同一个物理磁盘上。您希望尽可能减少 I/O 争用。

可能还有一个商业(即付费)程序可以进行分类。开发一个可以有效地对一个巨大的文本文件进行排序的程序是一项非常重要的任务。如果你能花几百美元买东西,如果你的时间值得,你可能会领先一步。

如果你不能使用现成的程序,那么 . . .

如果您的文本位于多个较小的文件中,则问题更容易解决。您首先对每个文件进行排序,从这些文件中删除重复项,然后编写已删除重复项的已排序临时文件。然后运行一个简单的 n 向合并,将文件合并到一个已删除重复项的单个输出文件中。

如果您只有一个文件,则首先将尽可能多的行读入内存,对这些行进行排序,删除重复项,然后写入一个临时文件。您继续对整个大文件执行此操作。完成后,您将拥有一些已排序的临时文件,然后您可以合并它们。

在伪代码中,它看起来像这样:

fileNumber = 0
while not end-of-input
    load as many lines as you can into a list
    sort the list
    filename = "file"+fileNumber
    write sorted list to filename, optionally removing duplicates
    fileNumber = fileNumber + 1

您实际上不必从临时文件中删除重复项,但如果您的唯一数据确实只占总数的 10%,那么您将通过不将重复项输出到临时文件来节省大量时间。

写入所有临时文件后,您需要合并它们。根据您的描述,我认为您从文件中读取的每个块将包含大约 2000 万行。因此,您可能需要使用 25 个临时文件。

您现在需要进行 k 路合并。这是通过创建优先级队列来完成的。您打开每个文件,从每个文件中读取第一行并将其连同对它来自的文件的引用一起放入队列中。然后,您从队列中取出最小的项目并将其写入输出文件。要删除重复项,请跟踪输出的上一行,如果新行与上一行相同,则不输出新行。

一旦您输出了该行,您就可以从文件中读取您刚刚输出的行的下一行,并将该行添加到优先级队列中。继续这种方式,直到清空所有文件。

不久前我发表了一系列关于sorting a very large text file 的文章。它使用我上面描述的技术。它唯一不做的是删除重复项,但这是对输出临时文件的方法和最终输出方法的简单修改。即使没有优化,该程序的性能也相当不错。它不会设置任何速度记录,但它应该能够在不到 12 小时内对 5 亿行中的重复项进行排序和删除。考虑到第二遍只处理总数据的一小部分(因为您从临时文件中删除了重复项),可能要少得多。

您可以做的一件事来加快程序的速度,即在较小的块上进行操作,并在将下一个块加载到内存中时在后台线程中对一个块进行排序。你最终不得不处理更多的临时文件,但这真的不是问题。堆操作稍微慢一些,但是通过将输入和输出与排序重叠来重新捕获额外的时间。您最终基本上免费获得了 I/O。在典型的硬盘驱动器速度下,加载 500 GB 大约需要两个半到三个小时。

看看文章系列。有许多不同的(大多是小型的)文章带您了解我描述的整个过程,并提供工作代码。我很乐意回答您可能对此提出的任何问题。

【讨论】:

  • 我想我真的很喜欢解决这样的问题。感谢您分享您的详细信息。本周末我们还有另一个线程询问 k-way 合并:Perl Merge File
  • 嗨,吉姆,感谢您的详细回复。我现在要调查一下。我在网上搜索了一段时间,但找不到任何可以处理我正在使用的数据量并保持速度和准确性的商业程序
【解决方案2】:

我不是此类算法的专家,但如果是文本数据(或数字,没关系),您可以尝试读取您的大文件并通过前两个或三个符号将其写入多个文件:all以“aaa”开头的行转到 aaa.txt,所有以“aab”开头的行 - 到 aab.txt,等等。您会得到很多文件,其中的数据处于等价关系:一个单词的重复是在与单词本身相同的文件中。现在,只需解析内存中的每个文件即可。 同样,不确定它是否会起作用,但我会先尝试这种方法......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 2020-01-01
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多