使用另一个巨大的文件过滤较小的文件

时间:2016-09-07 07:14:59

标签: python perl awk

我有一个巨大的csv文件,大约有10 ^ 9行,每行有一对id,如:

IDa,IDb
IDb,IDa
IDc,IDd

调用此文件1。我有另一个小得多的csv文件,大约有10 ^ 6行,格式相同。调用此文件2。

我想简单地找到file2中的行,这些行至少包含一个存在于file1中的ID。

有快速的方法吗?如果它是在awk,python或perl中,我不介意。

5 个答案:

答案 0 :(得分:5)

$ cat > file2 # make test file2
IDb,IDa
$ awk -F, 'NR==FNR{a[$1];a[$2];next} ($1 in a&&++a[$1]==1){print $1} ($2 in a&&++a[$2]==1){print $2}' file2 file1 > file3
$ cat file3 # file2 ids in file1 put to file3
IDa
IDb
$ awk -F, 'NR==FNR{a[$1];next} ($1 in a)||($2 in a){print $0}' file3 file2
IDb,IDa

答案 1 :(得分:4)

我实际上会使用sqlite来做类似的事情。您可以使用sqlite3 test.sqlite从两个文件创建一个新数据库,然后执行类似的操作:

create table file1(id1, id2);
create table file2(id1, id2);
.separator ","
.import file1.csv file1
.import file2.csv file2
WITH all_ids AS (
    SELECT id1 FROM file1 UNION SELECT id2 FROM file1
)
SELECT * FROM file2 WHERE id1 IN all_ids OR id2 IN all_ids;

使用sqlite的优点是,您可以比使用某种脚本语言编写的简单脚本更智能地管理内存。

答案 2 :(得分:2)

使用这些输入文件进行测试:

$ cat file1
IDa,IDb
IDb,IDa
IDc,IDd

$ cat file2
IDd,IDw
IDx,IDc
IDy,IDz

如果file1可以适合内存:

$ awk -F, 'NR==FNR{a[$1];a[$2];next} ($1 in a) || ($2 in a)' file1 file2
IDd,IDw
IDx,IDc

如果没有,但file2可以适合内存:

$ awk -F, '
    ARGIND==2 {
        if ($1 in inBothFiles) {
            inBothFiles[$1] = 1
        }
        if ($2 in inBothFiles) {
            inBothFiles[$2] = 1
        }
        next
    }
    ARGIND==1 {
        inBothFiles[$1] = 0
        inBothFiles[$2] = 0
        next
    }
    ARGIND==3 {
        if (inBothFiles[$1] || inBothFiles[$2]) {
            print
        }
    }
' file2 file1 file2
IDd,IDw
IDx,IDc

以上使用GNU awk进行ARGIND - 其他awks只是在开始时添加FNR==1{ARGIND++}块。

我有ARGIND==2块(即处理第二个参数的部分,在本例中是10 ^ 9 file1)首先列出效率,因此我们不会不必要地测试{ {1}}用于更大文件中的每一行。

答案 3 :(得分:1)

在perl中,

use strict;
use warnings;
use autodie;

# read file2
open my $file2, '<', 'file2';
chomp( my @file2 = <$file2> );
close $file2;

# record file2 line numbers each id is found on
my %id;
for my $line_number (0..$#file2) {
    for my $id ( split /,/, $file2[$line_number] ) {
        push @{ $id{$id} }, $line_number;
    }
}

# look for those ids in file1
my @use_line;
open my $file1, '<', 'file1';
while ( my $line = <$file1> ) {
    chomp $line;
    for my $id ( split /,/, $line ) {
        if ( exists $id{$id} ) {
            @use_line[ @{ $id{$id} } ] = @{ $id{$id} };
        }
    }
}
close $file1;

# print lines whose ids were found
print "$_\n" for @file2[ grep defined, @use_line ];

答案 4 :(得分:0)

示例文件:

cat f1
IDa,IDb
IDb,IDa
IDc,IDd


cat f2
IDt,IDy
IDb,IDj

Awk解决方案:

awk -F, 'NR==FNR {a[$1]=$1;b[$2]=$2;next} ($1 in a)||($2 in b)' f1 f2
IDb,IDj

这将在数组a和b中存储file1的第一列和第二列。如果第二个文件中显示第一列或第二列,则打印这些行。