3

我有两个包含大量文本的大文件,我要做的是保留文件 A 中的所有行,这些行的字段与文件 B 中的字段匹配。

文件 A 类似于:

Name (tab)  #  (tab)  #  (tab)  KEYFIELD  (tab)  Other fields

file BI 设法使用 cut 和 sed 以及其他东西基本上将其归结为一个列表字段。

所以目标是将文件 A 中的所有行保留在第 4 个字段中(它说 KEYFIELD),如果该行的字段与文件 B 中的行之一匹配。(不必完全匹配,所以如果文件 B 有Blah和文件A说Blah_blah,没关系)

我试着做:

grep -f fileBcutdown fileA > outputfile

编辑:好的,我放弃了。我只是强行杀死了它。

有一个更好的方法吗?对于任何关心的人来说,文件 A 为 13.7MB,文件 B 缩减后为 32.6MB。

编辑:这是文件 A 中的示例行:

chr21 33025905 33031813 ENST00000449339.1 0 - 33031813 33031813 0 3 1835,294,104, 0,4341,5804,

文件 B 中的示例行被删减:

ENST00000111111
4

4 回答 4

3

您正在达到使用基本 shell 工具的极限。假设每行大约 40 个字符,文件 A 有 400,000 行,文件 B 有大约 1,200,000 行。您基本上是为文件 A 中的每一行运行 grep,并且每次执行都让 grep 遍历 1,200,000 行。这是您正在解析的 4800亿行。Unix 工具速度惊人,但即使是快速完成 4800 亿次的事情也会加起来。

最好使用 Perl 或 Python 等完整的编程脚本语言。您将文件 B中的所有行放在哈希中。您获取文件 A 中的每一行,检查第四个字段是否与散列中的某些内容匹配。

读几十万行?创建一个 10,000,000 条目哈希?Perl 可以在几分钟内解析这两者。

某事——在我的脑海中。你没有给我们太多的spects,所以我没有做任何测试:

#! /usr/bin/env perl

use strict;
use warnings;
use autodie;
use feature qw(say);

# Create your index
open my $file_b, "<", "file_b.txt";
my %index;

while (my $line = <$file_b>) {
    chomp $line;
    $index{$line} = $line;    #Or however you do it...
}
close $file_b;


#
# Now check against file_a.txt
#

open my $file_a, "<", "file_a.txt";
while (my $line = <$file_a>) {
    chomp $line;
    my @fields = split /\s+/, $line;
    if (exists $index{$field[3]}) {
         say "Line: $line";
    }
}
close $file_a;

哈希意味着您只需读取一次 file_b 而不是 400,000 次。启动程序,去办公室厨房拿杯咖啡。(嗯!非乳制奶精!)当你回到办公桌时,它就会完成。

于 2012-09-21T20:09:21.220 回答
2

这是使用GNU awk. 像这样运行:

awk -f script.awk fileB.txt fileA.txt

内容script.awk

FNR==NR {
    array[$0]++
    next
}

{
    line = $4
    sub(/\.[0-9]+$/, "", line)
    if (line in array) {
        print
    }
}

或者,这是单线:

awk 'FNR==NR { array[$0]++; next } { line = $4; sub(/\.[0-9]+$/, "", line); if (line in array) print }' fileB.txt fileA.txt

GNU awk还可以使用和执行fileB.txt您描述的预处理。如果您希望我将其构建到上述脚本中,您将需要提供此行的示例。cutsed


更新使用文件HumanGenCodeV12GenBasicV12

像这样运行:

awk -f script.awk HumanGenCodeV12 GenBasicV12 > output.txt

内容script.awk

FNR==NR {
    gsub(/[^[:alnum:]]/,"",$12)
    array[$12]++
    next
}

{
    line = $4
    sub(/\.[0-9]+$/, "", line)
    if (line in array) {
        print
    }
}

这成功打印GenBasicV12了可以在HumanGenCodeV12. 输出文件 ( output.txt) 包含 65340 行。该脚本只需不到 10 秒即可完成。

于 2012-09-22T06:42:09.610 回答
0

grep -f即使对于中等大小的模式文件(< 1MB)似乎也很慢。我猜它会尝试输入流中每一行的每个模式。

一个对我来说更快的解决方案是使用 while 循环。这假设它fileA相当小(在您的示例中它是较小的),因此多次迭代较小的文件比多次迭代较大的文件更可取。

while read line; do
  grep -F "$line" fileA
done < fileBcutdown > outputfile

请注意,如果匹配多个模式,此循环将多次输出一行。要解决此限制,请使用sort -u,但这可能会慢很多。你得试试。

while read line; do
  grep -F "$line" fileA
done < fileBcutdown | sort -u | outputfile

如果您依赖于行的顺序,那么我认为您除了使用grep -f. 但基本上它归结为尝试 m*n 模式匹配。

于 2012-09-21T19:39:57.070 回答
0

使用以下命令:

awk 'FNR==NR{a[$0];next}($4 in a)' <your filtered fileB with single field> fileA
于 2012-09-26T02:31:02.990 回答