删除数百万个压缩CSV文件中的重复行,同时保留重复行中的一条信息

时间:2019-07-10 21:48:11

标签: python algorithm hash duplicates

收集了约1000万个GZipped CSV文件,每个文件包含100-1000行和> 2000列。每个文件还包含一个标题。

每个CSV文件中都有两个重要的列,“ ID”和“ target”。

我正在尝试删除具有重复“目标”的行,但保留要删除的行中的ID和不会删除的行。

例如

输入:

CSV1
|   ID  |  Target                      |
|-------|------------------------------|
| IX213 | C1=CC(=CC=C1CC(=O)C(=O)O)O   |
| IX412 | CN1C=NC2=C1C(=O)N(C(=O)N2C)C |

CSV2
|   ID  |  Target                      |
|-------|------------------------------|
| BC144 | CN1C=NC2=C1C(=O)N(C(=O)N2C)C |
| BC155 | C(CC(=O)O)C(C(=O)O)N         |

输出:

CSV1*
|   ID         |  Target                      |
|--------------|------------------------------|
| IX213        | C1=CC(=CC=C1CC(=O)C(=O)O)O   |
| IX412; BC144 | CN1C=NC2=C1C(=O)N(C(=O)N2C)C |

CSV2*
|   ID  |  Target                      |
|-------|------------------------------|
| BC155 | C(CC(=O)O)C(C(=O)O)N         |

对于使用Pandas(Python)之类的少量文件来说,这将是直截了当的,但是希望有人可以对拥有数十亿个条目的数百万个文件进行更好的处理。

2 个答案:

答案 0 :(得分:2)

我将编写一个程序,该程序通过gzip压缩的csv文件并写入以下4列:target id file row

对每个文件运行此操作,您将获得约1000万个小文件。

假设您不是以分布式方式执行此操作,接下来我将把这些文件合并为一个大文件,并使用unix sort实用程序对其进行排序。 (警告您要执行LC_ALL=C sort foo.txt是因为C语言环境更快,并且会产生更明智的结果。有关更多信息,请参见sort not sorting as expected (space and locale)。)

现在可以轻松处理该文件,并决定保留哪个文件。您可以将列file row target id is_keep removed_ids写入文件。确保用前导零写出该行,因此您可以写42来代替000042removed_ids是您从其他文件中删除的文件(如果保留此文件)。 (前导零的数量应该足以容纳最大的文件。这就是说,升序顺序应与数字顺序匹配。)

再次对该文件排序,然后将其分解为每个文件的决定。

鉴于原始的gzip压缩文件以及要保留的该行的文件以及要存储的ID,可以轻松处理原始文件以删除/保留行并记录删除的内容。我强烈建议进行完整性检查,以验证目标/ id /行是否全部匹配。除非通过了健全性检查,否则不要删除原件。


如果您是分布式处理的忠实拥护者,那么从排序到映射缩减的转换非常简单。如果您有用于该设置的基础结构,则最好使用它。如果没有,我建议使用这种排序的文件方法,只使用并行化处理第一个/最后一个文件。

答案 1 :(得分:1)

即使数据量看起来不堪重负,但我认为如果只保留所需数量的数据,则可以按顺序遍历所有文件。例如,您可以跟踪具有唯一目标的关系,该目标具有第一个具有此目标的ID和 ID别名的关系(例如,ID IX412对应于BC144)。这样,您的解决方案可能看起来像这样:

import csv

filenames = [...]
target_ids = {}
aliases = {}

for filename in filenames:
    with open(filename, 'r') as file_in:
        reader = csv.DictReader(file_in)
        for row in reader:
            if row['Target'] in target_ids:
                aliases[row['ID']] = target_ids[row['Target']]
                remove_row(row)  # Do whatever you may require
            else:
                target_ids[row['Target']] = row['ID']

请注意,将dict的键值对配对为10M是很容易做到的。

如果这仍然不适合内存,则可以使用shelve而不是dicts,以便将相应的数据存储在HDD中。您可以执行以下操作:

import csv
import shelve

filenames = [...]

with shelve.open('target_ids') as target_ids, shelve.open('aliases') as aliases:
    for filename in filenames:
        with open(filename, 'r') as file_in:
            reader = csv.DictReader(file_in)
            for row in reader:
                if row['Target'] in target_ids:
                    aliases[row['ID']] = target_ids[row['Target']]
                    remove_row(row)  # Do whatever you may require
                else:
                    target_ids[row['Target']] = row['ID']

shelve在常规命令速度方面的缺点。