从一个巨大的文件 (70GB) 中有效地删除列中的重复值

Efficiently remove duplicated values within a column from a huge file (70GB)

大家好,我有一个巨大的表格文件 (76GB),我想删除该文件的一些行。

这是头:

CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei A0A653DM45_Callosobruchus_maculatus_Insect  100.000 45  0   0   4   48  65  109 1.563E-24   98  110
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei Q1HR02_Aedes_aegypti_Insect 100.000 45  0   0   4   48  102 146 1.563E-24   98  147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei Q7PRY1_Anopheles_gambiae_Insect 100.000 45  0   0   4   48  102 146 1.563E-24   98  147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei P25867_Drosophila_melanogaster_Insect   100.000 45  0   0   4   48  102 146 1.563E-24   98  147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei J3JXP2_Dendroctonus_ponderosae_Insect   100.000 45  0   0   4   48  102 146 1.563E-24   98  147

我想使用一种有效的方法(我不能使用 pandas 因为加载文件的时间非常长)。 为了在第一列中只保留第一个重复的 2 行。

例如,如果我有一个文件,例如:

注意:table 是 tabular separated

示例;

CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     A0A653DM45_Callosobruchus_maculatus_Insect      100.000 45      0       0       4       48      65      109     1.563E-24       98      110
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     Q1HR02_Aedes_aegypti_Insect     100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     Q7PRY1_Anopheles_gambiae_Insect 100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     P25867_Drosophila_melanogaster_Insect   100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     J3JXP2_Dendroctonus_ponderosae_Insect   100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     A0A0L7KYD3_Operophtera_brumata_Insect   100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     A0A653DM45_Callosobruchus_maculatus_Insect      100.000 45      0       0       4       48      65      109     1.563E-24       98      110
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     Q1HR02_Aedes_aegypti_Insect     100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     Q7PRY1_Anopheles_gambiae_Insect 100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     P25867_Drosophila_melanogaster_Insect   100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     J3JXP2_Dendroctonus_ponderosae_Insect   100.000 45      0       0       4       48      102     146     1.563E-24       98      147

预期输出:

CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     A0A653DM45_Callosobruchus_maculatus_Insect      100.000 45      0       0       4       48      65      109     1.563E-24       98      110
CM009916.1_1:1212271-1212415(+):Neodiprion_lecontei     Q1HR02_Aedes_aegypti_Insect     100.000 45      0       0       4       48      102     146     1.563E-24       98      147
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     A0A653DM45_Callosobruchus_maculatus_Insect      100.000 45      0       0       4       48      65      109     1.563E-24       98      110
CM009917.1_1:1212271-1212415(+):Neodiprion_lecontei     Q1HR02_Aedes_aegypti_Insect     100.000 45      0       0       4       48      102     146     1.563E-24       98      147

使用这个 Perl 单行代码:

perl -F'\t' -lane 'print if $seen{ $F[0] }++ < 2' in_file > out_file

示例:

echo 1 2 3 2 3 3 3 3 | xargs -n1 | perl -F'\t' -lane 'print if $seen{ $F[0] }++ < 2'

打印:

1
2
3
2
3

Perl 单行代码使用这些命令行标志:
-e : 告诉 Perl 查找内联代码,而不是在文件中。
-n :一次循环输入一行,默认分配给 $_
-l : 在执行内联代码之前去除输入行分隔符(默认情况下在 *NIX 上为 "\n"),并在打印时附加它。
-a : 在空格或 -F 选项中指定的正则表达式上将 $_ 拆分为数组 @F
-F'/\t/' :在 TAB 上拆分为 @F,而不是在空格上。

$seen{ $F[0] }++:将散列%seen的元素加1,其中键为$F[0],第一列的值

另见:
perldoc perlrun: how to execute the Perl interpreter: command line switches

awk 救援!

如果您的文件按示例排序,则此脚本不会占用任何内存。

如果文件以制表符分隔且第一个字段没有任何空格,则无需指定字段分隔符

$ awk 'p!={c=2; p=} c&&c--' file

每次更改密钥后捕获新密钥并打印 2 行。

我知道 perl 和 awk 版本可以工作,而且很好而且紧凑。如果你想要 python 中的版本,我可能会天真地尝试:

import collections
keys = collections.defaultdict(int)
with open("file_in.txt") as file_in:
    with open("file_out.txt", "w") as file_out:
        for line in file_in:
            key = line.split("\t", 1)[0]
            keys[key] += 1
            if keys[key] < 3:
                file_out.write(line)